DDDにおけるモデルについてまとめていきたいと思います。
・ドメイン駆動設計とは
・モデル ←ここ
・値オブジェクト
・エンティティ
・リポジトリ
・DIP
「モデル」という言葉を聞くと思い浮かぶのは、例えばプラモデルとか。
例えば飛行機のプラモデルがあるとします。
これは、「プラスチックという扱いやすい素材で、家の中に飾れる、
でも飛行機の要素をきちんと持っているミニチュア」です。
実際の飛行機は、鉄の胴体や、たくさんの部品や、燃料で動くエンジンや・・・で構成されていますが、
その細部をすべて再現するのではなく、重要な要素だけを抜き出してプラモデルとなってるんですね。
ただし、適当に要素を抜き出してしまうと「これは飛行機のミニチュア!」と認識できないので、飛行機たりえる要素をうまく抜き出されていることがポイントです。
☝モデル
モデルとは、「現実のものを抽象化した概念」とのことです。
突然ですが、私は抽象化という言葉が嫌いです!なぜかというとよくわからないので・・・
でも大丈夫。上記の飛行機のプラモデルの例を考えると、これがモデルのことなんだなとわかります。
つまり、なにかの性質や要素を取捨選択して抜き出したものですね。
個人的には、抽象化なんて難しい漢字を使わずデフォルメって言ってほしいですね!
☝モデリング
DDDでは、現実の業務をプログラムに落とし込むため、モデリングという作業が重要になってきます。
「モデル」が「デフォルメ」なら、「モデリング」は「デフォルメする」ですね。わかりやす〜い✌
例えば飛行機を絵で表すために必要な要素は、
- 細長い胴体
- 2つの羽がある
- しっぽ(正式名称知らない)がある
大事なことは、デフォルメの仕方は人それぞれということです。
いろんなイラストがあるように、飛行機のデフォルメはコレ!という正解はないってことですね。
飛行機マニアや飛行機の歴史書の著者さんにとっては、モデリングによりとても精密に要素が抜き出されるでしょう。
逆に、前衛的アートを収集するような画廊の人なら、要素として抜き出したいものはピャッとしたものだけだったりするでしょう。
難しい話に戻ると、対象の業務が求めているのはなにかをよく観察し、モデルを作っていくことがエンジニアには必要ということです。
エンジニアは紙に書かれた仕様をただプログラムに落とし込むだけではなく、そのお仕事がどんなものなのか、なにが求められているのか、よ〜〜く観察(ヒアリング)して、モデルを作っていきましょうということになります。
- クライアント:ドメイン(業務領域)をよく知っている。モデリング手法は知らない。
- エンジニア:ドメインのことをよく知らない。モデリングの手法は知っている。
両者は助け合ってシステムを作り上げていこうね、ということですが、クライアントと接する機会なんてないよ〜という方もいると思います。そういうときは、そのお仕事についてたくさん調べる、お仕事している人が何を必要としているのかをよく考える、というだけでも、なにも考えないよりはいいのかなと思っています。
〜エンジニアがクライアントの要求を読み取れず失敗した例〜
- 受注したシステム:通販サイトのシステム
- 作成対象の仕様:注文は、出荷前であれば、キャンセル処理ができる
- 作ったもの:出荷前かを判定し、OKであればDBをキャンセルに変更する処理
- 起こったこと:キャンセル処理をしている商品が、実は出荷のため段ボールに入れている途中のもので、そのまま出荷されてしまった。
- 現場で起きていたこと:出荷担当は朝イチで出荷前の商品リストを印刷し、2時間かけて発送作業している。発送作業中の2時間、ステータスは「出荷前」のままになっていた。
→このエンジニアは、「朝イチで配送リストを印刷して商品を確保し始めたら、物理的にもう出荷が始まっている(キャンセルできない)」という仕様を汲み取れませんでした。
もしよ〜〜くヒアリングし、現場の動きがどうなっているか観察できていれば、
「未出荷から出荷済になる間、だいぶ時間が開くみたい。キャンセル処理判定を【未出荷】を元にやったら現場とすれ違いが起こるのでは?そうだ、【出荷準備中】のステータスも追加して、配送リストが印刷された時点でステータスを出荷準備中に変更しよう。そうすれば、キャンセル処理は出荷準備中に移行していない場合に、という判定にすればいいな。」
なんて考えることができ、現場が求めるシステムを作れていたかもしれないですね。
☝DDDにおけるモデル
DDDにおいて、モデルは「どの責任を背負っているか」できっちり線引きされます。
この業務は私やってもいいけど、まああなたがやってもいいし…みたいな曖昧な役割分担でなく、きっちり線を引いて役割分担していくことが重要です。
では、システムにおいて各モデルがのような責任を持つべきかを考えていきます。
各モデルの責任?
人間でないものに「責任」という言葉を使うと分かりづらいですが、「ルール」と言い換えても大丈夫です。
システムに組み込まれるモデルには、そのモデル専用のルールを持っているのです。
それではシステムはどんなモデルを持てばいいのでしょう?これもドメインそれぞれで考えられ、正解はありません。
一説には、「現実の業務で会話に出てくる名詞はモデルになりえる」らしいです。
カフェの注文システムなら、カフェの店員さんが以下のようなことを言うかと思います。
「ご注文は何にしますか。カフェラテ、それにサンドイッチを追加、以上ですね。クーポンはご利用ですか。ポイントカードはお持ちですか。お会計は800円になります。」
このオレンジ文字はそれぞれがモデルになりえるということです。
それぞれが仕事をするとして、責任はなにが考えられるでしょうか。
- 注文:注文を受け付ける。注文を確定させる。合計金額を計算する。
- カフェラテ・サンドイッチ:自身の金額と商品名を持つ。アレルギー情報を保持する。
- クーポン:適用対象かどうか判断する。割引金額を計算する。
注文の責任、カフェラテの責任、というとなんだか分かりづらいですが、「聞いたことやお願いした処理について、正しい答えを返してくれる」って感じです。
DDDは役割分担をする設計だというお話を以前にしています。
つまりメインプログラムくんが、処理を各モデルに丸投げするために、各モデルは「ぼくの役割はこれ!ばっちこい!」という状態になっていないといけないのです。
「カフェラテくん、君の値段は?」
「クーポン君、割引金額を計算して!」
「会計くん、最終金額を計算して!」
こんなメインプログラムくんのお願いに対して答えられるのは、モデルが責任を持っているからです。
逆に、責任を考えられないモデルというのはあるでしょうか。
例えば、アイスカフェラテにつけるストローがあるとして、責任を考えることができるでしょうか。
カフェの注文時にストローが持つべき責任・・・例えば有料のストローなら、金額をもつ責任があるでしょうが、無料でつけてくれるなら特にないように思います。(別にストローアンチじゃないよ)
責任がない場合、ドメインで出てくる名詞だとしても無理にモデルにする必要はありません。モデルは責任を持ってこそモデルになるのです。
役割分担の練習をしてみよう
ここまで偉そうに書いていますが、私はこのモデルとモデルが持つべき責任についていまいち理解できなかったため、geminiに聞いて何度も練習しました。
練習していて使った例題をひとつここで紹介します。わかりづらかったら、geminiに「モデルと責任の例題出して〜」ってお願いして練習するのをおすすめします。
レストランの予約システム<問題>
- 座席数を超えた予約は不可。
- 営業時間外の予約は不可。
- 定休日の予約は不可。
- 予約すると残席数が減り、キャンセルすると増える。
- モデルになりえるものは何でしょうか。
- 各モデルはどんな情報を持っているでしょうか。
- その情報から判断できることはなんでしょうか。
- 各モデルの責任はどんなものが考えられるでしょうか。
- 店舗/残席数/残り何席あるか?/残席数を計算する責任
- 座席/?/?/責任がない
- 予約/名前、人数、日時/対応する予約があるか?/予約情報をもつ責任
- 営業時間/開始時間、終了時間/営業時間内か?/営業時間内か確認する責任
- 定休日/日付/営業日か?/営業日かを確認する責任
■座席は、モデルとして上げてみたが役割がないのでモデルにすべきでないね。
■予約可能かどうかを判断する責任をどこに持たせればいいのかわからない。
「予約」は「営業時間」「定休日」の情報がないと、予約可能か判断できない。
それなら、「営業日」というモデルを作ったほうがよかった?
営業日が、営業日時、予約情報、残席数を持つと、営業日はそこから予約可能かどうかの判断ができるなあ・・・営業日モデルの中に他のモデルを内包することもできるのかあ・・・
以上がモデルの解説でした!
次は値オブジェクトについて見ていきましょう〜👏
.png)
.png)
.png)
.png)
0 件のコメント:
コメントを投稿