Closed45

エリック・エヴァンスのドメイン駆動設計

bayamasabayamasa

モデル
モデルとは現実の何らかの側面や興味の対象となる概念を表している。
またモデルは簡素化したものである。

なので問題を解決する上での関連する側面を抽象化して、それ以外の詳細を無視する、現実に対する一つの解釈。

Custmer modelが存在したときに、現実ではクレームを言ったりするし、寄付をしたりすることもあるかもしれないが、Customerとしての要素のうちで、あるシステムにおいて必要な部分だけ切り取ることで、モデルとしての抽象化を成立させる。

bayamasabayamasa

ドメインモデルとは特定の図ではなく、図が伝えようとしている考え方である。
そのため、伝えるということにおいて図を必須として必要とするものではない。

ある目的に従って現実の概要を表現しているものであり、写実的なものではない。

bayamasabayamasa

ソフトウェアの核心はドメインに関係した問題をユーザーのために解決する能力である。
それ以外の特徴はこの目的を達成するための副次的なものに過ぎない。

bayamasabayamasa

ウォーターフォール手法とドメイン駆動手法(XP)の違いは、ソフトウェア開発者のフィードバックがあるかどうかである。ビジネスサイドはビジネスサイドのみで話した上でソフトウェア開発者に依頼を投げる。

これにより、ソフトウェア開発者のドメインについての学習の機会を損ねるため、新たな依頼などが積み上がってきたときに、既存のドメインとのよい組み合わせなどが発見できず、コードが散らばってしまう。

bayamasabayamasa

メンバー全員でドメインを噛み砕くことで、機械的に機能を作り出すのではなく開発者が原理を習得しようとし、ドメインエキスパートはより本質的なところを抽出しようとする

bayamasabayamasa

ポリシー化する
ある一部のドメインルールをアプリケーションメソッドに格納するのではなく、特別なメソッド、ポリシーメソッドを使用してそれを注入することにより、要件がわかっていない技術者に対しての設計図的な役割を示すようにする。

bayamasabayamasa

ユビキタス言語
ドメインエキスパートと技術者における言語に違いが生じて、翻訳コストが高くなるケースが存在する。
また一人の技術者としても話しことばと聞きことばが異なるケースは多様に存在する。

これらのケースを回避するために、予め専門用語を定義しておく。
これをユビキタス言語と呼ぶ。

bayamasabayamasa

つまりモデルは言語を明らかに表したものにし、ドメインエキスパート/他の技術者との会話は全てこのユビキタス言語を使用したものにすること。
技術者はアウトプットする全ての成果物に対して、ユビキタス言語を使って表現をしなければならない。

bayamasabayamasa

ドメインエキスパートが理解できないモデルはどこかしらに齟齬があると考えた方が良い。
もしそうなった場合、ユビキタス言語を再構成してモデリングをし直す必要がある

bayamasabayamasa

図について
多くの場合モデリングとはUML図を指す事が多いが、UML図を書くのは労力を使いすぎる。
図の本質的な役割は、直感的に見ることでわかりやすい方法でコミュニケーションをとるという伝達手段に過ぎない。

そのため図は考え方の骨格を示している。
またその図が表す設計の詳細はコードによって落とし込まれているべきである。

モデルは図ではないということを理解するのは重要。

bayamasabayamasa

XPにおいてはプログラムに設計を語らせることを是としていて、設計書の作成をよしとしない。

しかしDDDにおいてはドキュメントは重要だと考える。
なぜならコードは詳細すぎて、全体像を追えないことがいくつかあるからである。

bayamasabayamasa

ドキュメントは常に更新され続けていなければならない。
これは常に丁寧な図と文章で書かれたドキュメントをメンテナンスしなければならないというわけではなく、表現できれば手書きでもなんでもよいということである。

最悪なのは丁寧なものを作ってメンテナンスをしないということ。
コードとの乖離がでてしまうことは良くない

bayamasabayamasa

ドキュメントは最小限にし、焦点を会話とコードの補足に絞る事が大事。
これがドキュメントを常に最新の状態に保つ事ができる方法である。

bayamasabayamasa

ドメインモデルとコードモデルは常に概ね一致するべきである。
結局設計と実装がかけはなれていたのでは意味がない。

そのために強固なユビキタス言語を使用して、ドメインと実装のどちらにも適用できるような、単一なモデルを作成すること。

bayamasabayamasa

モデリングをして、コードを書かないような仕事をしては行けない
モデリングの際のフィードバックを受けられなくなるから。
またコーディングをする人は全員、ドメインエキスパートととの話し合いに参加しなければならない
開発者がモデルの概念をおろそかにしていると、だんだん実装がモデリングと離れていき、最終的にモデルを用いないようなコーディングになってしまう場合が存在する。

bayamasabayamasa

モデル駆動設計においてモデルは疎結合にされるべきである。
そのため関連の矢印を双方向ではなく、単方向にすることで関連をへらす事ができる。

bayamasabayamasa

エンティティ
同一性を持つドメインオブジェクト
そのデータを一意に特定できるような性質を持つオブジェクト
例えば銀行の口座番号や、人でいうと名前/顔/生年月日などの組み合わせがIDとなる。

bayamasabayamasa

モデル図はエンティティがどのような関連を持つか、ということが重要である。

bayamasabayamasa

値オブジェクト
エンティティが同一性を担保できるオブジェクトに対して、値オブジェクトは同一性を担保できないオブジェクト。
銀行口座の預金、人でいう性別/年齢など。

bayamasabayamasa

値オブジェクトの重要な要素

  1. 不変
  2. 等価に使用できる
  3. 交換可能

エンティティに紐づく値オブジェクトを変更したい場合は、値オブジェクトの中身を変更するのではなく、まるごと置き換えるべき

bayamasabayamasa

値オブジェクトは値であるので、値オブジェクトの持つ属性を変更したいときには、既存のオブジェクトを使用するのではなく、別の値オブジェクトを使用する。

しかし頻繁に値オブジェクトを変更したいニーズがあった場合可変を許容する

bayamasabayamasa

module
疎結合/高凝集のコードの塊
moduleの名前にはユビキタス言語の一部を使用する

bayamasabayamasa

集約(Aggregate)
モデルはエンティティと値オブジェクトを含んだ、集合体となることがおおい。
この集合体の集約をどうするかが大事である。

集約したモデルは外部からアクセスできる位置を一つに絞る。
そうすることで内部の値が知らないうちに変更されているということを防ぐ事ができる。

bayamasabayamasa

集約をすることで、トランザクションの単位を統一し、アクセスすることで競合をなくす

bayamasabayamasa

ファクトリ
集約されたモデル群はどこかで作成されなければならない。しかし複雑な作成メソッドをドメインモデル自身に加えるのはオブジェクトの責務と外れてしまう。

しかし、サービスはアプリケーション層などにドメインの作成メソッドを組み込むと内部の実装が流出してしまうということにもなる。

したがってモデル作成ロジックはドメイン層のモデルの集合とは別の部分に、作成する必要がある。
これをファクトリと呼ぶ

bayamasabayamasa

銀行口座開設のように、ファクトリ自体がドメインとなるケースもあるが、ならないケースがほとんどである。
つまりこれは非機能要件となる。

bayamasabayamasa

ファクトリは通常、生成したいモデルと密接に関係しているモデルの中で実装する。
そうすることで、隠蔽力を上げる事ができる。

bayamasabayamasa

ファクトリは引数との結合関係にあるため、引数は慎重な検討が必要となる。
複雑すぎる引数設計は密結合の原因となる。

bayamasabayamasa

エンティティファクトリと値オブジェクトファクトリと最大の違いは、エンティティは後から値を追加できるという点である。
値オブジェクトは不変であるので、ファクトリで生成した後は変更不可のままでよい。
しかしエンティティファクトリは有効な集約に必要な値のみ必要なのであり、他のデータは随時setしておけばよい

bayamasabayamasa

ファクトリは再構成のための機能を保つ必要もある。
再構成のためのファクトリはIDを更新させてはいけない。

bayamasabayamasa

ファクトリは再構成のための機能を保つ必要もある。
再構成のためのファクトリはIDを更新させてはいけない。

bayamasabayamasa

repository
既に作成済みのオブジェクトにアクセスしなければいけない場合がある。
クエリはグローバルである。クエリを使うとどこからでも保存したデータにアクセスすることができるが、それはドメイン駆動設計のカプセル化から離れてしまっている。

bayamasabayamasa

また、集約オブジェクトが持つ値オブジェクトにアクセスするときには、必ず集約ルートをたどって値オブジェクトにアクセスしなければならない。
ただその関連付けが複雑すぎると、毎回たくさんのメソッドを通らないとデータの取り出しが実現できな
い。

bayamasabayamasa

リポジトリパターンとは主眼をドメインモデルに戻す役割をもつアーキテクチャである。
システムの根幹はデータアクセスにある。
しかし我々が扱わないといけないものはデータアクセスではなく、ドメインモデルである。
そのためデータアクセスはカプセル化して単純化する。

bayamasabayamasa

リポジトリの必要要素
型の抽象化
DBは多態性が存在しないので、抽象化することはコードの理解に貢献する

クライアントから切り離された点を活かすこと
クライアントはリポジトリにアクセスするだけでよい。そのためデータアクセスはRDBでなくてもよい。つまりインメモリデータベースに保存するようにするということもできる

トランザクションはクライアントに委ねる
リポジトリはトランザクション制御を持たない

bayamasabayamasa

ファクトリはオブジェクトのライフサイクルの初期、リポジトリは中期を表す
つまり最初の作成はファクトリ、データの格納/取り出しはリポジトリが管理する
しかし、リポジトリがなんらかのオブジェクトではないデータをオブジェクト化しなければならないケースが発生する(ファイルなど)
そのようなときはファクトリを使ってオブジェクトを再編成する。

bayamasabayamasa

ドメインモデルは改良の継続の末に本当にわかりやすいものになる。
その領域までいくのに開発者は負担を強いられないといけない

bayamasabayamasa

ドメインモデルには、仕様モデルと呼ばれるモデルに対する仕様を記載したモデルを作成することもある。
これはサービスと使用感が似ている

bayamasabayamasa

メソッド名、クラス名は重要である。
膨大なソフトウェアの機能から自分が修正したい部分を反映させる際に、他のコードをみる必要がある。
このとき、その膨大なソフトウェアを詳細にみる必要があるコードは開発者にとって優しくない。

よいソフトウェアの設計とは 名前を見ただけで、そのコードが何をしているのか判断できる である。
つまり、開発者は全てのコードを詳細に見なくても、修正コードを描くことができる。

この意味でメソッド名、クラス名は最重要である。
またテストコードをわかりやすく書くのも重要である。
テストで何をやっているかわかることでコードの処理の全容を簡単に把握する事ができる。

bayamasabayamasa

これこそが、カプセル化/高凝集による最大のメリットである。

bayamasabayamasa

副作用とは

ある関数を呼び出したときに、その関数が他の関数を呼び出すといった流れがいくつもネストされ、自分が把握していない範囲の変更もその関数を呼び出すことにより及ぼしてしまうこと。

このような関数は適切に抽象化されていないインターフェースといえる。
安全に中身を詳細に見ることなく関数を使用できるように適切な抽象化が必須となる。

この件におけるベストプラクティスは、その関数を呼び出した際の出力が何回やっても同じ値になることが保証されているかどうかというところである。
ある一定の引数に対して、返り値が違うとその関数は副作用を持っていると言える。

bayamasabayamasa

とは言っても状態を変更する操作もある。
これを本書では** コマンド **とよび、ステータスに変更のみの必要最低限の操作のみに留めるべきである。

値オブジェクトは不変であることが保証されている。
これを利用することで開発者はその関数が不変であることを保証するきっかけにすることができる。
そのため、値オブジェクトが使える部分があれば積極的に使用していくべきである。

bayamasabayamasa

オブジェクト同士の依存関係を極限まで減らすことは良いことであるが限界もある。
其の場合、閉じた操作を使用する。

これは引数と戻り値が等しい場合、複雑な処理を関数に押し込めるといったものである。
抽象的なインターフェースが明白であることがカプセル化の中で重要であるので、複雑性を関数の内部に押し込めることで、明白なインターフェースを作成する事ができる。

このスクラップは2022/05/04にクローズされました