ドメイン駆動設計 (DDD)
業務ドメインの知識をモデルとして表現し、反復的に設計を洗練する手法。DはDomainではなく Design の D。_moc-web-infra
核となる考え
Eric Evans が「エンジニアが業務ドメインに関心を持たない」批判から考案。実装コードを使って独自設計を反復的に得る技法であり、「実装パターンだけ真似た軽量DDD」はアンチパターン視される。
構成要素
- Entity: 同一性(ID)を持つオブジェクト。
@Entityが対応。 - Value Object: 値で等価判定するオブジェクト。
@Embeddableが対応。 - ドメインモデル: 単なるデータモデルだと不変条件を破れてしまう。getter/setter だけのモデルはドメインモデル貧血症で非OOP的。JPA Entity をそのままドメインモデルにマップするのは筋が悪い。
- 境界づけられたコンテキスト: 共通の
Customerを作るのではなく、各サービスごとに局所化したエンティティを作り連結点で差分を調整する。
パッケージング
ディレクトリをドメイン(関心事)で分けるか、性質(service/model/repository)で分けるか。後者は「技術駆動パッケージング」で関心事が低凝集になるアンチパターン。domain/ 以下は関心事で分けるのがよい。
レイヤと責務(テラソルーナ準拠)
- Entity: 基本テーブル単位。FKは Entity or Entity のList/Set で表す。カテゴリカルな値は基本型+Enum。
- Repository: Entity のライフサイクル(CRUD)を制御・永続化。実装は infra 層に置き domain は infra に依存しない。
findOneBy/findPageBy/countBy/existsBy等。 - Service: ビジネスロジック。再利用性は考慮せず特定Controller向けに提供。他Serviceからの呼び出しは原則禁止、共通化したいなら
SharedServiceに(サービスがサービスを呼ぶ問題)。 - 単一/相関 validation・モデル変換は Controller、トランザクション境界は Service。
批判的視点
「ある程度の規模にならないとペイしない」「規模が大きくなると protobuf 詰替えアーキの方がスケールする」という議論もある。
関連
- software-architecture-characteristics / domain-driven-design
- F#による型での表現は functional-domain-modeling-fsharp
CQRS と Event Sourcing
DDD から派生した、読み書きを分離するアーキテクチャパターン。_moc-web-infra
CQRS (Command Query Responsibility Segregation)
サーバの機能を明示的に2つに分ける戦略。
- コマンド: 副作用あり、write
- クエリ: 副作用なし、read、スケーラブル
「情報の参照に使うモデルと更新に使うモデルに異なるものを使う」。 - 更新系モデル(Entity/ValueObject)は Repository を介し、参照系モデル(DTO)は専用の
QueryServiceで取得する。集約をまたいだDTOを扱いやすくなり、画面寄りになる。 - DBを分けることもある: read が多い場合 read replica でスケール。更新の同期は非同期 or 同期(同期だとtxが分かれるため Two-Phase Commit 等)。
- graphql はこの発想と親和的。
Event Sourcing
ドメインで発生したイベントを他ドメインに供給するパターン。状態そのものではなくイベント列を記録し、再生して状態を得る。CQRS のデータ同期手段として併用されることが多い。DynamoDB Streams 等で実装できる(database-transaction-theory)。
実例
一休はPython・ORMを使わず dataclass + Data Mapper パターン + CQRS で、テーブルを変えずに読み書きモデルを分離した。