💭
ドメインモデリングをするときに気をつけていること
ドメインモデリングをする上で個人的に気をつけている点をまとめました。
ドメインモデルとは
- 目的を達成するためだけに特化したモデルのこと
- ある目的を達成するために必要な情報(ドメイン知識[1])の集合
- 仮にソフトウェアが存在しなくても現実世界でも通用する情報
- 技術的な関心ごとやソフトウェアがなければ成り立たない知識やルールはドメインの管轄外
- ビジネスソフトウェアの場合
- ビジネスを完遂する(目的を達成する)ために必要なルール(ドメイン知識)を表現したモノ
- ビジネスの決まり事を整理したモノ、と捉えてしまった方が理解しやすい
良いモデルとは
- 目的を達成できるモデル
- 目的を達成するために必要最小限の情報がまとまっている(凝集度が高い)モデル
- 属性として持っていたり、他のモデルと関係を持っていたり
ドメインモデリングとは
- ある目的を達成するために必要な情報を抽象化して可視化する行為
- 情報を取捨選択、グルーピングして整理すること
- すべての事物を表現する必要はなく、ある目的を達成するために必要な特定の側面を抽出すること
モデリングのツール
- 始めはホワイトボードと付箋で十分
- 清書は PlantUML のオブジェクト図を使うと Git で管理できるのでおすすめ
オブジェクト図での表現方法
- オブジェクト
- ドメインモデル自体を表現
- クラスとして実装される
- 属性
- ドメインモデルが目的を達成するために必要な知識、情報を表現
- インスタンス変数として実装される
- 関係
- 関連
- ドメインモデル同士の関係性を表現
- 引数や返り値として実装される
- コンポジション
- 集約(整合性を保つ必要があるドメインモデル同士の関係性)を表現
- インスタンス変数として実装される
- 誘導可能性
- ドメインモデル同士の依存方向を表現
- 多重度
- 関係性を持つドメインモデルの数を表現
- コレクションオブジェクトとして実装される
- 関連
- パッケージ
- 複数のドメインモデルの集合体を表現
- DDD のモジュールと同義
- そのままパッケージとして実装される
- ノート
- ドメインモデルだけでは表現できないような目的を達成するために必要な情報を表現
- メモとして使用
- メソッドの表記は不要
- 引数や返り値は関連で表すことができるため
- どのドメインモデルがどのドメインモデルと関係性を持つのか、という情報だけで必要十分なため
ドメインモデリングに必要な要素を洗い出すには
- ドメインエキスパートとの会話で発掘する
- 不明瞭な単語や矛盾を追求する
- 隠れた概念や暗黙知を明らかにする
- マニュアルやヘルプを見る
- 現状のソフトウェアを使い尽くす
ドメインモデリングのコツ
ドメインエキスパートの言葉をそのままモデルにしない
- ドメインを正しく捉えられなくなる(目的を達成するためにだけに特化できなくなる)可能性があるため
- ドメインエキスパートはドメインの事物の専門家であり、目的を達成するための手段(ドメインモデル)に詳しいわけではないため
- 目的を達成するために本当に必要な情報という軸でモデリングすることを関係者間でしっかり認識する
技術的な関心ごとを持ち込まない
- 技術的な関心ごとを持ち込んでしまっている例
- 技術的な用語を使用している
- データベースのテーブル構造を意識している
- 実装のしやすさを優先している
- ただでさえ複雑なドメインに、ソフトウェアの複雑さを持ち込んでしまうため
- ドメインを考える際はドメインのみに注力する
- 技術的な関心ごとはドメイン層以外で実装すればいいため
- ドメインを考える際はドメインのみに注力する
- 非エンジニアとの協働が円滑に行うことができなくなるため
- 非エンジニア(ビジネスサイド)のメンバーにも通じる用語を使用する
参考書のサンプルを鵜呑みにしない
- 参考書のモデルは参考書内で通用するモデリングであり、これから作ろうとしているソフトウェアの目的と一致しないケースがほとんどのため
ひとつの目的を達成するためだけに必要な要素を抽出する
- 複数の目的を達成するモデルは凝集度が低くなってしまうため
- そのまま実装すると神クラスになってしまう
- 高凝集になるように意識する
- 目的を達成するために必要最低限な情報(ドメイン知識)だけをまとめる
モデルを必要以上に細かく分けすぎない
- 必要以上にモデルを細かく分けすぎると図が複雑になってしまうため
- 属性に持たせることとオブジェクト同士をコンポジションで関連されることは同じことを表している(下図参照)
- 同じことを表しているなら図がシンプルになるほうで
- ただしモデルとして存在することが重要な場合はきちんとモデルとして表現する
- モデルとして存在することが何かしらの目的を達成することに必要な場合
- モデルとして存在することで暗黙知や隠れた概念を明確に表すことができる場合
目的を達成するために必要な関係性だけを抽出する
- すべての関係性を表すと図が複雑になってしまうため
- ドメインモデル同士は目的を達成することには不要な関係性も持っていることがほとんどなため
- 必要な関係だけを抽出してシンプルに表現する
- 本質的ではない(目的を達成することに不要な)関係は書かない
複数の目的の関係性がある場合は関係を分ける
- 複数の目的を持つ関係性をひとつの関係で表してしまうと、その関係性が何を表しているのかが分からなくなってしまうため
- 目的を達成するために必要な関係性が複数存在する場合は、関係性ごとにきちんと関係を分ける
- ひとつの目的に対してひとつの関係を保つことで、モデル間の関係の種類の数が浮き彫りになる
集約を意識する
- 必ず守りたい整合性を保つための集まりを集約として表現する
- 必ず同時に変更が起きないと破綻してしまう集まりの境界
- 技術的な整合性ではなく、ドメインとしての整合性を意識する
- 目的を達成することに破綻してしまわないか
- ひとつの集約に対してひとつのモジュール(パッケージ)に関連するモデルをまとめる
小さな集約にする
- 大きな集約は変更に弱くなるため
- 実装だけに限らずドメイン的にも
- 技術的にはパフォーマンスやスケーラビリティに直結する
- 必ず同時に変更が起きないと破綻してしまうケースは意外と多くない
- 結果整合性を用いて解決可能か検討する
- 小さな目的を達成するために本当に必要な情報のみ属性に持たせる
隠れた概念を明確にする
- 隠れた概念や暗黙知が抜け落ちると、ドメイン層以外にその知識が流出してしまう(ドメインモデル貧血症になる)リスクがあるため
- 手順やルール、種類などはそれそのものが重要なドメインモデルになりえる
- 例えば手順そのものが重要なドメインロジックの場合、ドメインモデルとして明確に表現していないとドメイン層以外で実装してしまうリスク(アプリケーション層に同等の処理しているなど)がある
- 仕様パターンや区分オブジェクトなどを用いてモデルとして表現するべき重要な概念を明確にする
一度ですべてを明らかにしようとはしない
- 分からないものを無理矢理モデリングすると間違った認識でモデリングしてしまうため
- 分からないものはモデリングできない
- 無理矢理モデリングすると実装の際に無理が生じるため
- 分からない部分は割り切って先送りにする
- ドメインモデリングは反復してブラッシュアップするため
- 分からないことを分かったことが大きな収穫
小さく改善し続ける
- 一度で完璧なモデリングはできないため
- ソフトウェアを取り巻く環境は常に変化するため
- 常にブラッシュアップできる環境にする
アンチパターンと処方箋
ひとつのモデルが大きくなってしまう
何が問題か
- ドメインモデルが低凝集になってしまうため
- そのモデルの役割が明確にならなくなるため
考えられる原因と解決策
- ひとつのドメインモデルで複数の目的を達成しようとしている可能性
- 大きなモデルを作らず、小さなモデルの組み合わせで作る
- ひとつの目的に対してひとつのドメインモデルで達成できるようにする(高凝集にする)
ひとつのドメインモデルが持つ関係が多くなってしまう
何が問題か
- ドメインモデルが低凝集になってしまうため
- そのモデルの役割が明確にならなくなるため
考えられる原因と解決策
- 目的を達成することには無関係な関係が書かれている可能性
- 目的を達成するために必要な関係以外は書かない
- すべての関係性を表現する必要はない
- ドメインモデルが必要以上に分割されていて、(ひとつの目的を達成するために)分割された複数のドメインモデルと関係を持っている可能性
- 必要以上にドメインモデルを分割せず、ひとつのドメインモデルに属性をまとめる
- ひとつのドメインモデルが属性を多く持ちすぎていて、(複数の目的を達成するために)他の複数のドメインモデルから関係を持っている可能性
- ひとつの目的を達成するために必要な属性だけを持つモデルに分割する
双方向に依存している
何が問題か
- 結合度が高まり変更に弱くなるため
- 実装だけに限らずドメイン的にも
考えられる原因と解決策
- 複数の目的の関係性をひとつの関係で表している可能性
- 関係性ごとにきちんと関係を分ける
- 中間に存在する隠れた概念を見落としている可能性
- 隠れた概念がないか模索する
多対多の関係を持っている
何が問題か
- モデル同士の関係性が複雑になるため
考えられる原因と解決策
- 複数の関係性をひとつの関係で表している可能性
- 関係性ごとにきちんと関係を分ける
- 中間に存在する隠れた概念を見落としている可能性
- 中間に隠れた概念がないか模索する
- 中間テーブルのケースが多いが、中間テーブルという用語は技術的な関心ごとのため適切な用語を用いる
- 中間に隠れた概念がないか模索する
- どうしても解決できない場合は実装のときは DIP にする
ノートの記載が多すぎる
何が問題か
- ドメイン知識がドメイン層から漏れるリスクがあるため
考えられる原因と解決策
- モデルで表現可能な隠れた概念やモデルで表現するべき概念を見落としている可能性
- ノート内の重要な単語をモデルとして切り出せないか検討する
最後に
- モデリングの能力を上げるには試行錯誤の繰り返しが必要
- 小さな変更をたくさんして失敗してみる
- ひとまずパターンに当てはめる
- 良い表現を継続して模索できる環境を整えることが最も重要
- プロジェクトやチームの文化に根付かせる
- モデル図を更新しやすい環境、ツールを使う
- リファクタリングしやすいテスト設計[2]にする
参考
- DDD のモデリングとは何なのか、 そしてどうコードに落とすのか
- ドメイン駆動設計は何を解決しようとしているのか
- モデルとは"現実世界を正しく表現したもの"ではないという話 / 境界付けられたコンテキストの必要性[DDD]
- ドメイン知識とユースケースの違いは何か?[ドメイン駆動設計][DDD]
- モデルでドメイン知識を表現するとは何か[DDD]
- ドメイン駆動設計とは何なのか? ユーザーの業務知識をコードで表現する開発手法について
- ドメイン駆動設計における「良いモデル」と「悪いモデル」とは
- ドメイン駆動設計に 15 年取り組んでわかったこと「ビジネスルール・値オブジェクト・型」が 3 つのキーワード
Discussion