Skip to main content
世界一わかりやすいドメイン駆動設計
ドメインを学び
学んだことをコードで表現する
ギルドワークス 増田 亨
2018/9/14 1
ドメイン駆動設計とは?
22018/9/14
What is Domain-Driven Design
ソフトウェア設計の
考え方とやり方の一つ
32018/9/14
ソフトウェア設計の一般原則
関心の分離
Separation of Concerns
モジュール構造
modular construction
全体を複数の関心事に分解する
それぞれの関心事を独立したモジュールとして実装する
モジュールを組み合わせて全体を構築する
42018/9/14
良い設計
• 変更が楽で安全になる
– どこに何が書いてあるかわかりやすい
– 変更の影響範囲を特定しやすい
– 変更の影響範囲が狭い
• ビジネス価値を生む
– 変更が容易(低コスト・低リスク)であれば
– サービスの改善・拡張が加速 ⇒ 顧客満足、差別化
– 事業の運営方針や戦術の選択肢が広がる
2018/9/14 5
ドメイン駆動設計のアプローチ
2018/9/14 6
ドメインロジックに焦点を合わせる
オブジェクト指向のモジュール構造
インクリメンタルに設計を進める
ドメイン駆動設計のアプローチ
2018/9/14 7
ドメインロジックに焦点を合わせる
オブジェクト指向のモジュール構造
インクリメンタルに設計を進める
ドメイン駆動設計のアプローチ
2018/9/14 8
設計のアプローチの比較
ドメイン駆動設計 従来の設計
ドメインロジックに
焦点を合わせる
入出力に
焦点を合わせる
オブジェクト指向のモジュール構造 手続き型のモジュール構造
インクリメンタルに設計する アップフロントに設計する
2018/9/14 9
設計のアプローチの比較
ドメイン駆動設計 従来の設計
ドメインロジックに
焦点を合わせる
入出力に
焦点を合わせる
オブジェクト指向のモジュール構造 手続き型のモジュール構造
インクリメンタルに設計する アップフロントに設計する
2018/9/14 10
アプリケーション全体としてカバーする範囲は同じ
どこに焦点を合わせるかの違い
関心事の違い
ドメイン駆動設計 従来の設計
ドメインロジックに焦点 入出力に焦点
関心事 関心事
ドメインの状況を表す値の種類 ユーザインタフェース
値を使った計算ロジック/判定ロジック データベース
計算結果/判定結果の表現方法 通信
2018/8/22 11
ドメインロジックに焦点を合わせる
どういうこと?
2018/9/14 12
ドメインロジック
• ビジネスで扱うデータを使った計算ロジックや
判定ロジックのこと (業務アプリの場合)
• 従来の設計では、機能分割から導いた
手続き型のモジュールの一部として記述
• ドメイン駆動設計では、ビジネスロジックだけ
を集め独立したモジュールに記述する
2018/9/14 13
入出力の関心事 vs. ドメインロジック
• 入出力の関心事→データモデル+CRUD操作
• 画面項目定義
• テーブル定義
• 登録機能、参照機能、編集機能、削除機能、…
• ドメインロジックの関心事→計算モデル
• 計算する値の種類
• 計算の種類
• 計算の組み合わせ方
2018/9/14 14
ドメインロジックの構成要素
ビジネスを構成する概念(関心の対象)
顧客、商品、
販売、出荷、請求、…
ビジネスの状況や構造を表現する「値の種類」
金額、数量、日付、日数、…
会員区分、商品区分、取引区分、…
値を使った計算や判定
金額計算、数量計算、日数計算、…
顧客タイプ判定、処理区分判定、有効性判定、…
2018/9/14 15
入出力からドメインロジックを分離
プレゼンテーション層
データソース層
アプリケーション層
入出力処理
ルールの実行
次のアクションの指示
画面入出力
Web API
データベース入出力
メッセージ送信
入出力定義して実装
機能分割して実装
従来のアプローチで取り組める
計算する値の種類
+
計算ロジック
判定ロジック
ドメインロジックを
分析設計して実装
ここに焦点を合わせる
金額、数量、日付、日数、…
顧客タイプ、商品カテゴリ、取引区分、…
一致不一致、大小、境界値、
加算減算、乗算、除算、
値とリテラルの変換
論理演算、集合演算
計算・判定ルールの記述を
独立したモジュールに
分離する
2018/9/14 16
ドメインロジックに焦点を合わせる理由
17
1. ソフトウェアが複雑になる原因はドメインロジックの複雑さにある
 入出力処理とドメインロジックの記述が混在すると
どこに、どんなビジネスロジックを書いているか、わかりにくい
 変更の影響があちこちに波及して、変更が厄介で危険になる
2. ドメインロジックを入出力から分離する
 ドメインロジックの輪郭と構造がはっきりしてくる
 ドメインロジックを分離した入出力側の記述はシンプルになる
1. ソフトウェア全体の見通しがよくなり、変更が楽で安全になる
 つまらない不具合が減る(すっきりとしたコードになる)
 修正や拡張が簡単にできるようになる(影響範囲が狭い、推測しや
すい)
2018/9/14
オブジェクト指向のモジュール構造
どういうこと?
2018/9/14 18
ソフトウェアのモジュール構造
ソースコードを扱いやすいかたまりに分割し
コードのかたまりごとに別ファイルに記述する
モジュール間(ソースファイル間)の依存を
できるだけ、小さく/少なくする
モジュール単独で意味が理解でき
独立して実装し試験できることが望ましい
2018/9/14 19
モジュール構造:2つのアプローチ
どこに着眼してソースを分割し整理するか?
入出力処理
vs
計算する値の種類
2018/9/14 20
入出力処理のモジュール構造
• 機能分割し、機能ごとに入力仕様と出力仕様を定義
する
• 機能ごとに、入力から出力への手続きを
時系列に記述する
手続き型のモジュール構造
トランザクションスクリプト
• 複数の機能に共通する処理(サブルーチン)を
集めたサブモジュールを作ることもある
2018/9/14 21
オブジェクト指向のモジュール化
• 「型」(値の種類)に注目してモジュール化
– 標準ライブライリで定義済の型(値の種類)
• BigDecimal, LocalDate, List<T>, …
– アプリケーション固有の型
• 金額型、数量型、有効期限型、商品区分型、…
• 「型」は3つの要素で定義する
– 値の種類の「名前」
– その値に有効な計算(演算)
– 有効な値の範囲
• クラス(class)は「型」を記述する仕組み
2018/9/14 22
オブジェクト指向のモジュール化
• 「型」(値の種類)に注目してモジュール化
– 標準ライブライリで定義済の型(値の種類)
• BigDecimal, LocalDate, List<T>, …
– アプリケーション固有の型
• 金額型、数量型、有効期限型、商品区分型、…
• 「型」は3つの要素で定義する
– 値の種類の「名前」
– その値に有効な計算(演算)
– 有効な値の範囲
• クラス(class)は「型」を記述する仕組み
2018/9/14 23
「型」を定義する
1. 「値の種類」を見つける
– 金額、数量、日付、日数、…
– 商品種類、顧客タイプ、取引区分、…
– 割引率、ポイント、…
2. それぞれの値に有効な計算(演算)を特定する
– 一致/不一致の判定
– 大小の判定
– 境界値(最大最小)の判定
– 足し算、引き算
– 掛け算
– 割り算
– 値とリテラルの変換
– 論理演算
– 集合演算
2018/9/14 24
「型」を定義する
1. 「値の種類」を見つける
– 金額、数量、日付、日数、…
– 商品種類、顧客タイプ、取引区分、…
– 割引率、ポイント、…
2. それぞれの値に有効な計算(演算)を特定する
– 一致/不一致の判定
– 大小の判定
– 境界値(最大最小)の判定
– 足し算、引き算
– 掛け算
– 割り算
– 値とリテラルの変換
– 論理演算
– 集合演算
2018/9/14 25
値の種類は多い→複雑さの原因
値の種類を整理すれば
ドメインロジックの見通しがよくなる
「型」を定義する
1. 「値の種類」を見つける
– 金額、数量、日付、日数、…
– 商品種類、顧客タイプ、取引区分、…
– 割引率、ポイント、…
2. それぞれの値に有効な計算(演算)を特定する
– 一致/不一致の判定
– 大小の判定
– 境界値(最大最小)の判定
– 足し算、引き算
– 掛け算
– 割り算
– 値とリテラルの変換
– 論理演算
– 集合演算
2018/9/14 26
値の種類は多い→複雑さの原因
値の種類を整理すれば
ドメインロジックの見通しがよくなる
計算の種類は多くない
特定の値に必要な計算はさらに少ない
入出力処理でモジュール化すると
同じ計算があちこちに分散し重複する
型(値の種類)に計算を集めれば
一か所のみの記述になる
型を組み合わせてロジックを表現
• 金額=単価×数量
– Amount型、UnitPrice型、Quantity型
– メソッドの設計
UnitPrice#multiply( Quantity ) : Amount
• 明細=(品名、単価、数量、金額)
– LineItem型、Product型、UnitPrice型、Amount型
– コンストラクタの設計
LineItem(Product, UnitPrice, Quantity)
2018/9/14 27
型のグルーピング
• 金額パッケージ
– Amount型、UnitPrice型、…
• 数量パッケージ
– Quantity型、Unit型、PieceUnit型、BoxUnit型、…
• 注文パッケージ
– PurchaseOrder型、LineItem型、…
– ShipTo型、InvoiceTo型、…
• 注文処理パッケージ
– EventType型、StatusType型、… (イベントと状態遷移)
– DeliveryType型、InvoicingType型、… (出荷区分、請求区分)
2018/9/14 28
型のグルーピング
• 金額パッケージ
– Amount型、UnitPrice型、…
• 数量パッケージ
– Quantity型、Unit型、PieceUnit型、BoxUnit型、…
• 注文パッケージ
– PurchaseOrder型、LineItem型、…
– ShipTo型、InvoiceTo型、…
• 注文処理パッケージ
– EventType型、StatusType型、… (イベントと状態遷移)
– DeliveryType型、InvoicingType型、… (出荷区分、請求区分)
2018/9/14 29
ドメインロジックの
全体を俯瞰し
概念の輪郭を描く
同じ名前で異なる型を扱う
部分型:異なる型を、同じ型名で扱う
interface Unit
class PriceUnit implements Unit
class BoxUnit implements Unit
多重定義:同じメソッド名で異なる型を受け取る
UnitPrice#multiply(Piece)
UnitPrice#multiply(Box)
総称性:一つのクラス名で型のバリエーションを扱う
List<Amount>, List<Quantity>, …
2018/9/14 30
オブジェクト指向のモジュール構造
• クラスを使って型を定義する
• 型を組み合わせて型を作る
• パッケージを使って型をグルーピングする
• 同じ名前で異なる型を扱う(ポリモフィズム)
部分型(サブタインピング)
多重定義(オーバーロード)
総称性(ジェネリクス)
2018/9/14 31
インクリメンタルに設計を進める
どういうこと?
2018/9/14 32
最初から良い設計は見つからない
開発の初期は、ドメインの知識が足りない
最初に書いたコードは、後から見直すと
もっと良い書き方が見つかる
初期の要求はあいまいで抜け漏れが多い
時間とともに具体的で詳細になっていく
要求は、時間とともに変化する
2018/9/14 33
アップフロントの設計
• 予測と予断に基づく設計
• リスク
– 要求やビジネスルールの知識と理解が不足した段階
で、ソフトウェアの構造を決めてしまう
– その結果、
ソフトウェア構造の確定後に発見された、要求やビジ
ネスルールを、どこかに無理やりねじ込む
– どこに何が書いてあるかわかりにくく、変更の影響範
囲が特定しにくい
2018/9/14 34
インクリメンタルに設計を進める
• 毎日、
クラス(モジュール)を発見し、実装し、
少し改善する
• 毎日、
モジュール構造(クラスとクラス関係)を検討し、
少し改善する
• 毎日、
全体のモジュール構造を俯瞰し、
少し改善する
2018/9/14 35
インクリメンタルに設計を進める
• 毎日、
問題領域の知識を少し広げ、少し掘り下げる
• 毎日、
一度書いたコードを見直し、少し改善する
• 時間とともに、具体的になる要求を、
時間とともに、コードに反映する
• 時間とともに、変化する要求を、
時間とともに、コードに反映する
2018/9/14 36
ドメインロジックに焦点を合わせる
オブジェクト指向のモジュール構造
インクリメンタルに設計を進める
ドメイン駆動設計のアプローチ
2018/9/14 37
やってみよう
2018/9/14 38
サンプルコード
レンタル料金の計算
典型的な手続き型プログラミング
入出力とロジックの混在
ドメイン知識の暗黙化(開発者の脳内自動変換)
↓
オブジェクト指向のモジュール構造
入出力とドメインロジックの分離
ドメイン知識をコードで明示的に表現する
2018/9/14 39
step by step
1. 計算ロジックの置き場所を改善する
2. ドメインロジックと入出力処理を分離する
3. 料金ルールを分析し深く理解する
4. クラス名でビジネスルールを語る
2018/9/14 40
before
2018/9/14 41
ビジネスルールが見えない設計
after
2018/9/14 42
型でビジネスルールを表現した設計
三つの基本テクニック
• 値オブジェクト
– 値を持つクラスに計算ロジックを持たせる
• コレクションオブジェクト
– 値のコレクションと、コレクションを操作するロジックを、
独立したクラスに分離する
• 区分オブジェクト
– 区分ごとの定数やロジックを別のクラスに分け、
条件分岐(if/switch)の記述をなくす/減らす
2018/9/14 43
設計の改善テクニック
• メソッドの抽出
• 名前の変更
• 説明用変数の導入
• クラスの抽出
• ロジックの移動
• コレクションのカプセル化
• タイプコードをクラスに置き換え
• プレゼンテーションとモデルの分離
• 階層の抽出
2018/9/14 44