エリック・エヴァンスのドメイン駆動設計
https://images-fe.ssl-images-amazon.com/images/I/6181Uutb1tL.jpg
第1部や 8, 9, 11, 13 章 は割と読み飛ばしてしまった。
主要なパターンについて軽くまとめておく。
序盤では、モデルの構成要素、オブジェクトをうまく管理するためのパターンについて述べられている。
中盤では、さらにしなやかな設計を目指すためのパターンについて書かれている。
後半では、大規模複雑化した場合にどうやって立ち向かって行くかが書かれている。
モデル駆動設計の構成要素
アーキテクチャ
レイヤ化アーキテクチャ
関心事の分離
各層は下層にのみ依存する
凝集度を高める
重要なのは、アーキテクチャそのものよりも、ドメイン層を隔離する事
モデルの要素
オブジェクトをその使われ方や責務に応じて Entity, Value Object, Service として表現し、モデルにあった関連を導出し、適切な Modules に分割する。
関連
現実は、多くの物事が多対多で、双方向。関係の性質が見えてこない
以下を守る
関連をたどる方向を強制する
限定子を付加して、多重度を効果的に減らす
本質的ではない関連を除去する
Entity (Reference Objects)
連続性、同一性によって定義される
属性によってその同一性が変化することはない
各オブジェクトを識別する手段が必要
Value Objects
ドメインにおける記述的な側面を表現し、概念的な同一性を持たない
不変であることが望ましい
Service
ドメインの重要な操作だが、Entity にも Value Object にも落ち着かないもの
状態を持たせない
操作名がユビキタス言語の一部になる
Modules (Packages)
利点
全体に圧倒されることなく、モジュール内部の詳細に集中できる
内部の詳細を無視して、モジュール間の関係性を見れる
低結合、高凝集が基本
関連と相互作用の分布から決め ない
概念の凝集した集合を含んでいるものを選ぶ
ドメインオブジェクトのライフサイクルの管理
オブジェクトは、生成され、様々な状態を経て、死ぬ。つまりは、コンストラクタによって生成され、最終的に GC に回収される。その間に他のオブジェクトと複雑に相互作用するが、これをうまく管理する必要がある。
大事なこと
オブジェクトの 整合性を維持 する
モデルが 侵食されない ようにする
上記を満たすためのパターンについて学ぶ。
集約(AGGREGATES)
問題点
オブジェクトに対する変更の一貫性を保証するのは難しい
維持すべき不変条件が、個々のオブジェクトや、そのグループに存在する。特にグループに存在する場合、オブジェクトの相互作用の中でそれを維持するのは難しい
かといってオブジェクトの操作に対しロックをかけすぎると、身動きがとれなくなるし、最悪デッドロックしてしまう
解決策
エンティティと値オブジェクトを集約の中にまとめる
これにより、集約全体に対し不変条件を強制できる
原則
ルートは、同一性を持ち、不変条件を検査する責務をもつ
境界内部のエンティティは、集約内で一意となる同一性を持つ
集約外部のオブジェクトは、 境界内部への参照を保持してはならない
境界内部から値オブジェクトのコピーを渡すことはできる
DB アクセスができるのは、ルートのみ
他のオブジェクトは、ルートから辿って参照する
ファクトリ(FACTORIES)
問題点
オブジェクトの生成は、それ自体が複雑
かといって、生成されるオブジェクト の責務には似合わない
クライアント に直接構築させると、ドメイン層から仕様が漏れ出してしまう
クライアントは、ドメイン層の内部の詳細を意識せざるを得なくなる
クライアントと生成されるオブジェクトの実装が過度に結合してしまう
解決策
複雑な組み立てをカプセル化するインタフェースを提供する
つまり所謂 Factory パターン
リポジトリ(REPOSITORIES)
問題点
利用する側は、オブジェクトへの参照をどうやって手に入れるか?
生成する or オブジェクトを辿っていく?
DB アクセスを利用させる?
クライアントには、技術ではなく、モデルを扱わせたい
クエリを組み立てさせると、クライアント側のコードにドメインが漏れ出してしまったり、失われたりする
ドメインロジックがクエリとクライアントコードに漏れ出してしまう
集約のカプセル化を無視される
集約のルート以外からアクセスされると不変条件を保てない
オブジェクトが単なるデータコンテナになってしまう
ビジネスについて伝えるコードを書きたい
解決策
リポジトリパターン
データベースアクセスの技術をカプセル化する
クライアントをモデルに集中させる
覚えておくべきこと
型を抽象化する
クライアントから切り離す利点を生かす
実装を自由に変更できる
性能の最適化がしやすい
トランザクション制御をクライアントに委ねる
コミットはクライアントに任せる
作業ユニットのコンテキストを理解しているのはクライアントだから
集約ルートに対してのみリポジトリを提供する
リファクタリング
リファクタリングの章があったが、大体が小説感があったので、パターンのみメモしておく
仕様(SPECIFICATION)
問題点
Entity や Value Object の責務のどれとも合致しない ビジネスルール がある
述語 の概念をオブジェクトで実装するのは意図が伝わりにくいし面倒
述語 : 論理プログラミングで扱われる独立した結合可能なルールオブジェクト
解決策
オブジェクトの状態に関する制約のために、特殊な目的を持った 述語的な値オブジェクト を作成する
オブジェクトが 何らかの基準を満たしているかどうかを判定する 述語
論理演算子を適用できるようにしておくとやりやすかったりする
しなやかな設計
しなやかな設計
過去に作成したレガシーに押しつぶされない
変更の追加が容易
柔軟性の名の下にオーバーエンジニアリングをしない
扱う人に対して本当に力を与えるソフトウェアは、通常はシンプルなものになる
意図の明白なインタフェース で伝達し、副作用のない関数 および 表明 で予測可能性をもたらす
コードの意図を明確 にし、コードを使った結果を透過的 にし、モデル要素を分離する
意図の明白なインタフェース
メソッドをどのように利用するかがわかる
副作用のない関数
メソッドを使用した結果をわかりやすくする
副作用がない場合、暗黙的な影響が起こり得ない
複雑なロジックを安全に実行できる
表明
メソッドを使用した結果をわかりやすくする
何を与えると何が起こるかが明白になっている
システムの状態を変更する場合に、それを特徴付ける
そのほかのパターンも含めて以下にメモする。
意図の明白なインタフェース(INTENTION-REVEALING INTERFACES)
問題点
コンポーネントを使用するのに実装について考えなければならないのでは、カプセル化の価値がない
解決策
効果や目的に基づいた命名をし、実行手段には言及しない
ユビキタス言語を活用する
副作用のない関数 (SIDE-EFFECT-FREE-FUNCTIONS)
問題点
開発者がインタフェースによって隠されたものを見る必要が出てくると、抽象化の効果が限定される
呼び出し先で複数のオブジェクトが複雑に相互作用していると予測が困難
解決策
ロジックのうち、できる限り多くの部分を 関数 にする
関数 : 副作用なしに結果を戻す操作
コマンド を 分離 する
コマンド : 目に見える状態を変更することになるメソッド
単純な操作として分離し、ドメインについての情報を漏らさない
表明(ASSERTIONS)
問題点
副作用が暗黙的な場合、原因と結果がもつれる
解決策
表明 を記述する
表明 とは
事前条件: オブジェクトもしくは集約の invariant
事後条件: 副作用
コーディングできる場合はコーディングする
できない場合は、ユニットテストで担保する
概念の輪郭 (CONCEPTUAL CONTOURS)
問題点
カプセル化の粒度をどうするか
モノリシックだと機能が重複したりする
クラスやメソッド分割により、クライアントが無意味に複雑になる
要素の組み合わせ方を、クライアントが理解する必要がある
概念が失われてしまう
解決策
設計要素 は、 凝集した単位 に分割する
設計要素 : メソッドやインタフェース、クラス、集約等
技術指向ではなく、ドメイン指向で分割する
独立したクラス(STANDALONE CLASSES)
問題点
相互依存関係があると理解が難しくなる
暗黙的な概念が存在し、開発者の負荷を上げる
解決策
低結合を極めると、自己完結型のクラスになり、理解の負担を軽減してくれる
テストもしやすい
閉じた操作(CLOSURE OF OPERATIONS)
ある 操作 が、ある型の元で 閉じている とは
引数と戻り値が同じ型
他の概念への依存関係を導入しないインタフェース
異質な概念を省くためのテクニック
宣言的な設計
問題点
表明 があっても、コード内に余計な副作用があれば違反される
紹介してきたパターンは、結局オブジェクト指向プログラミングに正式な厳密さを与えられない
解決策
宣言的な設計
宣言的 とは
プログラムやその一部を、実行可能な仕様 として作成する方法
性質を厳格に記述することで、ソフトウェアを制御する
設計の宣言的スタイル
複数の要素を組み合わせることができる
各要素が持つ意味が明白である
何か影響を与える場合にはそれが特徴付けられている/明らかである
例) 論理演算子を定義し、仕様を結合する
意図の明白なインタフェース、副作用のない関数、表明 によって 明白な実装 を用意し、それを土台に宣言的なスタイルの設計を行えると良い。
モデルの整合性を維持する
システムが大規模、複雑化するにつれて、個々のオブジェクトのレベルでの理解は難しくなる。となると、巨大なモデルを扱う/把握するためのテクニック が必要になる。
例えば、あるプロジェクト内に複数チームが存在する場合、チームごとに扱うモデルが異なって、その一部が重複してしまっていたり、あるいは重なっていてもお互いに利用の仕方や理解が異なってしまっていたりする。モデルにおいて重要なのは一貫性である。
統一性(unification)
各用語の意味が常に同じ
矛盾したルールが存在しない
かといって、一つのシステムに対し巨大なドメインモデルを管理することは現実的ではない。何を統一し、統一しないか?統一しない場合は混乱や破損が生じないためにどうするか?
境界づけられたコンテキストによって各モデルの適用範囲を定義し、コンテキストマップによってプロジェクトのコンテキスト、およびプロジェクトすなわちコンテキスト間の関係性を概観できる。コンテキスト間の関係づけは、共有カーネルを用いた緊密なものであったり、別々の道を行く疎結合なものであったりする。
境界づけられたコンテキスト (BOUNDED CONTEXT)
問題点
プロジェクトには複数のモデルが存在しがち
複数のモデルに基づくコードが混在するとバグりやすい
各モデルの コンテキスト が不明瞭
解決策
モデルが適用される コンテキスト を明示的に定義する
アプリケーションの特有の部分が持つ用途、コードベースやデータベーススキーマ等の物理的な表現等の観点から設定する
モデルが混在していることを示す警告は以下
重複した概念
実際は同じ概念を表しているモデル要素、それに付随する実装が二つある場合
その情報が変更されるたびにもう一箇所で変更が必要になってしまう
偽同族語 (false cognate)
2人の人が同じ用語で違う概念をさしている場合
コンテキストマップ (CONTEXT MAP)
問題点
各チームが別々のモデルにしたがっていると、コンテキストが相互に滲み出す場合がある
解決策
境界づけられたコンテキストを定義し、それに名前をつけ、ユビキタス言語の一部にする
コンテキストマップとして図におこす
かならずしも図におこす必要はなく、議論のみで十分かもしれない
共有カーネル (SHARED KERNEL)
問題点
複数チームが密接に関連したアプリケーションを開発していると、多くの時間を変換層と修復作業に費やす
二度手間のせいで、ユビキタス言語の利点が失われる
解決策
ドメイン層で、ドメインの共有を行う
モデルとコードのサブセットを共有する
腐敗防止層(ANTICORRUPTION LAYER)
問題点
新しいシステムが既存システムと連携する必要性に迫られることがある
既存システムがレガシーである場合、うまく設計されていないかもしれない
うまく設計されていたとしても、新しいシステムとはベースになったモデルが異なる
解決策
腐敗防止層
他システムへメッセージを送信する仕組みではなく、あるモデル/プロトコルのもつ概念オブジェクト/アクションを別のものに変換する役割
アダプターパターンやファサードパターンを利用できる
Adapter Pattern は、別のオブジェクトをラップすることで、ラップされる側 (Adaptee) の機能を異なるインタフェースで提供し直す
Facade Pttern は、異なる複数のオブジェクトをラップし、統一したインタフェースを通して提供するパターン
別々の道 (SEPARATE WAYS)
境界づけられたコンテキストを、他とは一切繋がりを持たないと宣言し、そのスコープ内でシンプルに特化した解決策を見つける取り組みのこと
第14章はこれ以後もしばらく続くが、モデルのコンテキストを各々明らかにしたあと、それに変更を加えるにはどうするか?が書かれる。そこまで大規模なケースについては現状あまり興味がないので、今の所はとばしておく。
蒸留
蒸留 (distillation)
混ざり合ったコンポーネントを分離するプロセス
本質を抽出する
蒸留の一番の動機は コアドメイン を抽出することだが、分離された副産物 (汎用サブドメイン, 凝集されたメカニズム) も価値がある。
コアドメイン
モデルには様々なコンポーネントが存在するが、全てを等しく改良はできない。ドメインのコアとなる部分に集中する
弱い開発者は、以下に魅かれがち
深いドメイン知識がなくても理解可能なドメイン
技術的なインフラストラクチャ
コアドメインは視点によって決まる
汎用サブドメイン
システムにおけるコアではなく、ドメイン全体から見れば付加価値が低く、汎用的なモデル
ビジネスにおける組織図のモデルや、一般的な時刻、時計のモデル等
専門的な知識を反映しておらず、モデルを複雑化させているだけ
このような高凝集のサブドメインは、別モジュールに分離し、開発の優先度をコアに分ける
既製品による解決も検討する
ドメインビジョン声明文 (DOMAIN VISION STATEMENT)
開発後期では、モデルを詳細に調べなくともわかる形で、システムの価値を説明する必要がある
ドメインモデルの本質と、エンタープライズにどのように役にたつかを示す
コアドメインとそれがもたらす価値に関する簡単な記述を 1 ページ程度で示す
強調されたコア (HIGHLIGHTED CORE)
ドメインビジョン声明文は広い観点から見たコアドメインを識別する。具体的なコアモデル要素の識別は個人の解釈で行う必要があり、これは役に立たない
コアドメインをわかりやすくするテクニック
蒸留ドキュメント
コアにフラグを立てる
凝集されたメカニズム (COHENSIVE MECHANISMS)
オブジェクト指向では how を what でカプセル化するが、処理が複雑になると how を示すメソッドが大量に増え what が侵略されてしまう
概念的に凝集されたメカニズムは、切り分けて別個の軽量なフレームワークにする
汎用サブドメインと同様、コアドメインの負担を軽減するのが目的だが、前者はモデルであるのに対し、こちらはドメインではなく、処理である。が、実際には最初はこの区別は純粋ではないのが普通で、リファクタリングによって明らかにして行く
例) 組織図のモデルにおいて、グラフ操作の処理をフレームワークとして分離する
隔離されたコア(SEGREGATED CORE)
WIP