はじめに
- 「実践ドメイン駆動設計」のまとめと感想。
これからドメイン駆動設計を学びたい人へ
- 本書のレビューで「これだけ読めばドメイン駆動設計について学習できる」という主旨のものを見かけたことがあるが、実際は難しいと感じる。
- 本書は「エリック・エヴァンスのドメイン駆動設計」の内容をアップデートするために書かれてはいるが、エヴァンス本を読んでいることを前提に説明を省略していたり、エヴァンス本を読んでいたほうが内容を理解しやすい部分があるので、先にエヴァン本を読んでからドメイン駆動設計の理論についての補強と実装サンプルとして参考にする程度に本書を使ったほうがいい。
- また、エヴァンス本のトピックスを網羅してはいるので、一度一通り学習したあとに復習をしたいという場合にはこれ一冊でなんとかなるかもしれない。
感想
- ドメイン駆動設計についてサンプル付きで説明してもらうことで理解が深まったのは良かった。
- "4章 アーキテクチャ" は既知のレイヤアーキテクチャについての理解を深めて、ヘキサゴナルアーキテクチャなどについての知識のアップデートができたのでためになった。
- "7章 サービス", "10章 集約", "12章 リポジトリ" は理解が曖昧だったのでサンプル付きで解説してもらったことで理解が深まったので良かった。
- ただ全体的に読みづらい。単純に自分の理解力不足もあるかとは思うが、途中で明らかにどこにも説明していないことを前提にして話を進めている部分があるのに気がついたので、そもそもわかるように書かれていないところもあるなと割り切って、適当に流して読みながら必要そうなところだけちゃんと読むようにした。
- SaaSOvation の話は基本的にただのサンプルだし文字だけで説明しすぎている部分はわかりづらいので、引っかかるようなら読み飛ばしても問題ないし、有用そうなら適当にかいつまんで読めば良い。
1章 〜 3章
感想
DDD についてのまとめ的な内容。
エヴァンス本を読んでいたら読み飛ばして良さそう。
4章 アーキテクチャ
- DDD は特定のアーキテクチャに依存しないが、ドメイン的な複雑さと技術的な複雑さを分離してソフトウェアを取り扱いやすくしたい、という方向性があり、それとマッチするアーキテクチャはいくつかある。
- どのアーキテクチャが一番優れているということは無い。求められる要求によって適切なアーキテクチャが異なるので機能要求や品質要求などを鑑みてどのアーキテクチャを採用するか判断すること。
レイヤーアーキテクチャ
- UI・アプリケーション・ドメイン・インフラストラクチャ、など責務によってレイヤーが分割されたアーキテクチャ。
- UI層はビューを提供する。
- アプリケーション層はドメインサービスの取りまとめやトランザクションの管理、セキュリティの管理などを行い、ロジックを持たない軽量なコーディネーターとして振る舞う。
- ドメイン層はソフトウェアの中心となるドメインロジックを実装する。
- インフラストラクチャ層はデータベースによる永続化などの機能を提供する。
DIP を適用したレイヤーアーキテクチャ
- DIP とは依存性逆転の原則。
- 上のレイヤーが下のレイヤーの実装に依存するのでは無く、上のレイヤーがインターフェイスを持ち、下のレイヤーがそのインターフェイスに合わせて実装することによって、依存関係を弱めて管理しやすくするパターン。
- レイヤーアーキテクチャに DIP を適用し、ドメイン層がインフラストラクチャ層を抽象として取り扱うことで、ドメイン層から技術的な関心事を分離しやすくなる。
ヘキサゴナルアーキテクチャ
- DIP を適用したレイヤーアーキテクチャの発展形で、ドメイン層あるいはアプリケーション層の内部でそれ以外のレイヤーを全て抽象として扱うアーキテクチャ。
- こうすることで外部のレイヤーの差し替えをする自由度が高まり、内部のドメイン層ではよりドメイン的な関心事に注力しやすくなる、などのメリットが有る。
REST
- DDD で外部向けやコンテキスト間で RESTful なインターフェイスを使うと、外部との分離がしやすくなり外部からの取扱がわかりやすくなる。
CQRS
- 参照系の処理と更新系の処理を別モデルとして実装するパターン。
- 更新と参照を別モデルにすることで、ユースケースに合わせた柔軟な参照系の実装ができ、更新処理に特化した堅い更新系の実装が可能になる。
イベント駆動アーキテクチャ
- 入出力をイベントとして保存し取り扱うパターンで、実装難易度は高いが、適切に扱えれば処理の経過を監視したりログを取ったりロールバックや再実行をしたりなどが容易になる。
- DDD ではコンテキスト間の連携にこのパターンを使うなどのアイディアとしてふれられている。
5章 エンティティ
- エンティティとは一意に識別できてライフサイクルを持つオブジェクト。
感想
ID の生成方法とかバリデーションの方法とか具体的なテクニックについての解説はあるが、概念的にエヴァンス本からのアップデートはない。
6章 値オブジェクト
- 値オブジェクトとは状態を持たずに一意に識別されないオブジェクト。
- 値オブジェクトをデータベースへ永続化する方法は、シリアライズしてそのまま集合やエンティティのレコードのカラムに入れる。
- 値オブジェクトのリストであった場合などはリストのままシリアライズしてカラムに入れる。
感想
値オブジェクトは値オブジェクトテーブルを作って ID だけエンティティに持たせていたが、大きくならなければ非正規化して記載の方法のようにやってもいいかもしれない。
7章 サービス
- 複数のエンティティを扱うやレポジトリを扱う処理はドメインサービスとして実装する。
感想
データストアへのアクセスが発生する処理ではレポジトリが必要になるので、大体の処理はドメインサービスでの実装か、ドメインサービスをファサードとする実装となるのではないか。
単純に1件 find して取得したエンティティで処理が完結するようなユースケースならドメインサービスにしない、とかはありえるかもしれないが。
-
なんでもドメインサービスで実装するとドメインモデル貧血症となる。エンティティが責務を負うべき処理は適切に切り分けてドメインサービスを過剰に利用しないこと。
- ドメインモデル貧血症とは、エンティティ外部で振る舞いを持ちすぎたせいで、エンティティが振る舞いを持たずにデータの入れ物になってしまう状態のこと。
-
ドメインサービス内での他サービスやレポジトリの初期化にはサービスファクトリを利用する場合がある。コンストラクタからそれらの依存オブジェクトをもらってくるパターンも有る。
- サービスファクトリとは
DomainRegistry.userRepository()のようにレポジトリの初期化のみを行うオブジェクト。
- サービスファクトリとは
感想
サービスファクトリを利用するとドメインサービスから手軽にレポジトリや他サービスなどの依存しているオブジェクトを呼び出すことができて便利。
便利ではある一方、ドメインサービスがなにに依存しているのかが内部実装に隠蔽されるため、ドメインサービスを利用する立場から見ると優しくない。
明示的にコンストラクタなどでリポジトリなどを受け取るようにし、依存関係が明示的になっていたほうが可読性は良さそうな気がする。正直面倒ではあるが。
- ドメインサービスはアプリケーションサービスではないので、トランザクションの境界などは持たない。
8章 ドメインイベント
- ドメインモデル内でオブザーバーパターンを採用した実装の紹介。
- イベントをオブジェクトとして実装し、永続化し、入出力として扱う。
- 境界づけられたコンテキストの外側にイベントを伝播させる場合の実装として、RESTful API を用意したりメッセージングミドルウェアを利用するケースの紹介。
感想
イベントをドメインモデル内で自然にモデリングできるのであれば使うのはありだと思う。
使うかどうかはともかくアイディアの一つとしておぼえておきたい。
9章 モジュール
- レイヤーや役割によって名前空間をまとめて取扱しやすくする。
感想
あまり内容がない。
10章 集約
-
集約はツリー構造を持つオブジェクトの集合。
-
集約内部のオブジェクトにはルートのオブジェクト経由でのみアクセスできる。
-
一つのトランザクションで一つの集約だけが更新されるようにモデリングする。
-
言い換えると、トランザクションによって整合性が確実に取れて欲しい単位で集約にまとめる。
- name と description という要素があり、参照されたときにそれぞれのバージョンが食い違うと問題が出る場合は、name と description は同一の集約にまとまる。
-
異なる集約同士を同時に更新する場合は結果整合性のみを保証する。
- つまり、ドメインイベントなどを利用して遅延して更新されることを受け入れ、整合性の取れていない時間を許容する。
感想
集約は本当に実装のイメージがついていなかったが、レポジトリもドメインサービスも内包しないということで、エンティティの集合かエンティティを集約したオブジェクトであると理解できた。
たとえばドキュメント指向のデータベースであれば、1 レコードが 1 集約になる感じだろう。
11章 ファクトリ
- エンティティや集約やレポジトリやサービスを生成するだけのオブジェクトをファクトリオブジェクトという。
- サービス自身も配下のエンティティなどのファクトリとして振る舞うことがある。
12章 リポジトリ
-
リポジトリはエンティティや集約の永続化と、その永続化されたエンティティなどの検索をするオブジェクト。
- 実際の使い方として、ファクトリなどで生成した集約を、データベースなど永続化層に保存したり検索したりすることに使われる。
-
リポジトリは Set や Map などの既存の集合を扱うクラスのインターフェイスを模して実装されるべきだ。
- インターフェイスに制限を設けることでドメインロジックがエンティティや集約にまとまりやすくなり、ドメインモデリングがスムーズになる。
感想
リポジトリにロジックを持ちすぎるべきではないというのはわかるが、現実的な性能要件から考えるとユースケースに対応したフィルタリングメソッドを実装することにはなると思う。
- Set などのリストを模倣してリポジトリを実装した場合、リポジトリは登録と検索時のみに利用され、エンティティの状態が変更された場合にはそれぞれのエンティティが状態を更新する。
感想
筆者の考えるエンティティは純粋なオブジェクトではなく、データベースへの参照を持った特殊なものである可能性が出てきた。エンティティの章にも特別な記載はなかったはず。
本書ではところどころで意味の取りづらい文章があるが、込み入った内容を文章だけで説明しようとしすぎているとか、単純に翻訳がまずいとかだけではなく、筆者が読者に共有していない前提条件が随分あるのだと予測できる。
13章 境界づけられたコンテキストの統合
- 境界づけられたコンテキスト間で連携をするために RESTful API を使う方法とメッセージングミドルウェアを使う方法についてのサンプルと解説。
14章 アプリケーション
- アプリケーションサービスはトランザクションやセキュリティの管理を行い、インフラストラクチャなどのアダプターの初期化やドメインサービスの呼び出しなどのタスクを連携させる薄いレイヤーとしてはたらく。