ドメイン駆動設計 モデリング/実装ガイド(松岡幸一郎)
書籍情報
- 著者:松岡幸一郎(著)
- 発行日:2020-03-01
- ISBN:N/A
- URL:https://booth.pm/ja/items/1835632
書籍目次
- はじめに
- 本書の構成
- 免責事項
- 第1章 DDD 概要
- 1.1 言葉の定義
- 1.2 DDDとは
- 1.3 モデルとは
- 1.4 良いモデル、良くないモデル
- 1.5 良いモデルを作るには
- 1.6 DDDの問題解決のアプローチ
- 1.7 取り組む上で重要な考え方
- 1.8 Q&A
- 第2章 モデリングから実装まで
- 2.1 ドメインモデリング
- 2.2 ドメインモデルの実装
- 2.3 ドメイン層オブジェクト設計の基本方針
- 2.4 実際の開発の進め方
- 2.5 Q&A
- 第3章 DDD固有のモデリング手法
- 3.1 集約
- 3.2 境界づけられたコンテキストの概念
- 3.3 境界づけられたコンテキストの実装
- 第4章 設計の基本原則
- 4.1 凝集度・結合度について
- 4.2 凝集度
- 4.3 結合度
- 4.4 今後の章との関連
- 第5章 アーキテクチャ
- 5.1 3層アーキテクチャ
- 5.2 レイヤードアーキテクチャ
- 5.3 オニオンアーキテクチャ
- 5.4 ヘキサゴナルアーキテクチャ
- 5.5 クリーンアーキテクチャ
- 5.6 オニオンアーキテクチャ、ヘキサゴナルアーキテクチャ、クリーンアーキテクチャの比較
- 5.7 境界づけられたコンテキストの実装
- 第6章 ドメイン層の実装
- 6.1 エンティティ
- 6.2 値オブジェクト
- 6.3 ドメインサービス
- 6.4 リポジトリ
- 6.5 ファクトリー
- 6.6 ドメイン層のそれ以外のオブジェクト
- 6.7 複数集約間の整合性確保
- 6.8 Q&A(ドメインオブジェクト)
- 6.9 Q&A(リポジトリ)
- 6.10 Q&A(その他)
- 第7章 ユースケース(アプリケーション)層の実装
- 7.1 ユースケース
- 7.2 ユースケースからの戻り値クラス
- 7.3 Q&A
- 第8章 CQRS
- 8.1 DDDの参照系処理で発生する問題
- 8.2 解決策
- 8.3 メリット、デメリット
- 8.4 実装時の注意事項
- 8.5 よくある誤解
- 8.6 Q&A
- 第9章 プレゼンテーション層の実装
- 9.1 プレゼンテーション層の処理概要
- 9.2 プレゼンテーション層のクラス、ファイル
- 9.3 リクエスト仕様の定義
- 9.4 レスポンス仕様の定義
- 9.5 Q&A
- 第10章 アーキテクチャ全般・ライブラリなど
- 10.1 例外処理
- 10.2 パッケージ
- 10.3 Webフレームワークへの依存
- 10.4 ORマッパー
- 10.5 言語
- 10.6 Q&A
- 第11章 困った時には
- 11.1 ドメイン駆動設計入門
- 11.2 実践ドメイン駆動設計
- 11.3 英語で検索
- 11.4 DDD Community JP
- 11.5 筆者に質問
- 著者紹介
第1章 DDD 概要
DDDとは
- ソフトウェア開発手法のひとつ
- モデリングによってソフトウェアの価値を高めることを目指す開発手法
モデルとは
- 問題解決のために、物事の特定の側面を抽象化したもの
抽象化とは
- 現実の物事の構成要素からソフトウェアに取り込みたい要素を取捨選択する行為
良いモデル/悪いモデル
- 良いモデル = 問題を解決できるモデル
- 悪いモデル = 問題を解決できないモデル
良いモデルを作るには
- (1) ドメインエキスパートと会話する
- (2) 運用して得られた知見をモデルにフィードバックする
DDDに取り組む上で重要な考え方
- 課題ドリブン
- 小さく始めて、小さく失敗する
第2章 モデリングから実装まで
- [WIP]
第3章 DDD固有のモデリング手法
- [WIP]
第4章 設計の基本原則
- [WIP]
第5章 アーキテクチャ
3層アーキテクチャ
- プレゼンテーション層、ビジネスロジック層、データアクセス層で構成
- よくあるアーキテクチャ
- ビジネスロジックが低凝集になりやすい問題がある
- ビジネスロジック層のモジュールが数百〜数千行の長大なコードになるなど
レイヤードアーキテクチャ
- 3層アーキテクチャで問題になったビジネスロジックの責務が過剰になる問題への対策
- プレゼンテーション層、アプリケーション層、ドメイン層、インフラ層で構成
- アプリケーション層でユースケースの実現、ドメイン層でドメイン知識の表現を行う
- ただし、インフラ層が実装の詳細(DBやO/Rマッパー)に強く依存してしまうという課題がある
- [MEMO]
- 重大な問題ではない気がするのだが
- 日常的にDBの種類を変更するシステムはあまり無いと思う
オニオンアーキテクチャ
- レイヤードアーキテクチャに依存関係逆転の原則を加えて、ドメイン層とインフラ層の依存関係を逆転させる
- リポジトリのインターフェイスをドメイン層に配置、リポジトリの実装クラスをインフラ層に配置
- こうするとドメイン層は実装の詳細に依存しない独立した層として機能するようになる
- [MEMO]
- インフラ層がコロコロ変わるシステムを想定しづらいのでメリットがわからない
- 概念的に綺麗であるというエンジニアの気分以上の理由はなさそう
- ユーザー環境ごとにカスタマイズが必要なシステムだとメリットはあるのかもしれない
- ユーザーのオンプレ環境にインストールするシステムなど
- 逆にSaaS的なサービスを提供するシステムでは無用な複雑性を持ち込んでいるだけに思える
ヘキサゴナルアーキテクチャ
- 実践DDDで紹介されているアーキテクチャ
- 別名: ポートアンドアダプタアーキテクチャ
- アプリケーションと外界がコミュニケーションする時は必ず専用のポートとアダプタを経由する設計思想
クリーンアーキテクチャ
- ロバート・C. マーチンが提唱したアーキテクチャ
- ヘキサゴナル/オニオンアーキテクチャの概念を統合しようとしたもの
- [MEMO]
- 全部盛りなので複雑
境界づけられたコンテキストの実装
- (1) 1コンテキスト:1アプリケーションの場合
- コンテキストごとにアプリケーションを実装
- コンテキストごとにアプリケーションを実装する為、実装コストは大きい
- アプリケーション同士のコミュニケーションはAPIなりメッセージングなりで行う(マイクロサービス)
- (2) 1コンテキスト:1アプリケーション以外の場合
- パッケージで分割(モジュラモノリス的な)
- パッケージ間のコミュニケーションはAPI(HTTP)経由やメッセージングで行うなどする
- パッケージを跨いでコードを直接importするとパッケージ分割した意味が無くなってしまう
第6章 ドメイン層の実装
DDDの戦術的設計パターン
- ドメインオブジェクト
- エンティティ
- 値オブジェクト
- ドメインイベント
- ドメインオブジェクトを利用するもの
- リポジトリ
- ファクトリー
- ドメインサービス
エンティティ
- 識別子によって特定できるモデル
値オブジェクト
- 識別子による一意性を持たない/不要なモデル
- 典型的には金額などの単位系
ドメインサービス
- モデルをオブジェクトとして表現するには無理があるもの
- 操作や処理そのもの
- ただし、そのような処理でも極力ドメインモデルとして実装し、ドメインサービスを濫用してはいけない
リポジトリ
- 集約を永続化するモジュール
- リポジトリはListかのように扱う・振る舞うべきである
- 例: ユーザーリポジトリは新規ユーザーをAddされる、ユーザー削除時はユーザーIDを用いてRemoveする、的なイメージ
- リポジトリはドメインの知識やルールを持たず、シンプルなCRUDを提供するものでなければならない
ファクトリー
- 複雑なドメインオブジェクトの生成を担当する
複数集約間の整合性確保
- (1) ユースケース層のメソッドで確保する
- ユースケースクラスの1メソッドの中で複数集約のオブジェクトをそれぞれのリポジトリを使って永続化
- 整合性の確保を実装によって担保するので、実装漏れ=整合性の破綻に直結するリスク
- (2) ドメインイベントを用いて結果整合性で確保する
- ドメイン層のオブジェクトの操作に対してイベントを発火させ、イベントハンドラで別集約の操作を呼び出す
- イベントの仕組みの設計・実装が必要で、全体としては複雑化するが概念としてはスマート
第7章 ユースケース(アプリケーション)層の実装
ユースケース層の役割
- ドメイン層のクラスが公開しているメソッドを組み合わせてユースケースを実現する
- ドメインオブジェクトの生成、利用、リポジトリを用いた永続化
ユースケースからの戻り値の扱いについて
- 設計方針:
- (1) 専用の戻り値クラスに詰め替えて返却する
- (2) ドメインオブジェクトをそのまま返却する
- 保守性が高いのは(1)
戻り値クラスの名称
- DDD的な規約は無い
- 本書では
DTO
を採用
ユースケースクラスの粒度
- 1クラス1パブリックメソッド
- 1クラスに複数のパブリックメソッドは保守性を低下させる
- [MEMO]
- 以前、1クラス1パブリックメソッドの設計を提案したら、「ひとつのパブリックメソッドしか持たないクラスはオブジェクト指向設計的ではない」として却下されたことがある
- メリット/デメリットを論ずる事もなく、特定の思想に忠実かどうかを判断軸にするようになったらエンジニアとしてはもうダメである
ユースケースクラスの命名規則
- DDD的な規約はないので、基本的に自由
- 例: タスクを作成するユースケース →
CreateTaskUseCase
- 動詞 + 名詞 +
UseCase
というスタイル
第8章 CQRS
DDDの参照系処理で起こる問題
- 複数の集約からデータを読み込んでくる必要がある
- 戻り値クラスにデータを詰め替える処理が煩雑になる
- 複数の集約をまたいだページング処理が難しい
解決策
- CQRSを導入
- 参照用のモデルと更新用のモデルを分ける
- 更新用のモデルはドメインモデル
- 参照用モデルは参照系(UI)の要求に従ったデータになる
本書での呼称
- 参照モデル → DTO
- 参照用サービス → QuerySevice
- 公式なルールは無いのであくまでも一例
参照モデルを定義するレイヤー
- クエリサービスのインターフェイスと戻り値はユースケース
- 実装クラスはインフラ層
- [MEMO]
- リポジトリの一種でよいのでは
メリット/デメリット
- メリット:
- 集約をまたがるデータ取得がシンプルになる
- クエリのチューニングがしやすい
- 集約をまたいだページングが可能
- デメリット:
- ドメインオブジェクトのデータが参照されている場所が分かりにくくなる
- 参照用モデル/クエリサービスを追加することでアーキテクチャが複雑になる
- [MEMO]
- ドメイン駆動設計の理念としては敗北宣言に等しい
- 結局、「きれいなドメインオブジェクト」だけではシステムを構築することは難しい
- とはいえ、更新系におけるドメインオブジェクトの有用性が否定されるわけではない
- DDDは万能ではないというだけである
CQRS導入の注意事項
- 部分的に導入可能
- いきなり全てをCQRSにする必要はない
データソースを分離する事は必須ではない
- データソースの分離 → DBとNoSQLに分けるなど
- 一緒に紹介されるので必須なものと誤解されがち
- データソースの分離はパフォーマンス問題に対する対策でCQRSの必須要件ではない
- [MEMO]
- 実践DDDはその辺の説明が不十分でCQRSがデータソースの必須要件だと誤認する事例が多かった
CQRS = イベントソーシングではない
- CQRSとイベントソーシングは相性がいいが、それぞれは別個の概念であり、必須要素ではない
第9章 プレゼンテーション層の実装
プレゼンテーション層の役割
- クライアントとアプリケーションの入出力を担当
- クライアントとユースケース層の間に位置し、リクエストとレスポンスの変換を行う
プレゼンテーション層のクラス/ファイル
- コントローラクラス
- テンプレートファイル
- ルーティングのコンフィグモジュール
リクエスト仕様の定義
- ユースケース層以下は特定のF/Wに依存しないようにする
第10章 アーキテクチャ全般・ライブラリなど
- [WIP]
第11章 困った時には
- [WIP]