【ビッグローブ × DDD 〜ドメイン駆動設計の現場〜】で発表させていただきました。

devlove.doorkeeper.jp

縁あって、DevLoveのDDDイベントで発表させていただきました。

DevLoveの運営のみなさんや会場設営の会社メンバーに感謝です。

内容としては、以下の2部構成でした。

  1. ビッグローブの事例紹介

  2. 登壇者と参加者によるディスカッション

事例紹介は、以下4パートに分割されて、僕は「2.導入」で発表させてもらいました。

  1. 立ち上げ:何故、ドメイン駆動開発に取り組もうとしたのか

  2. 導入:初期チームでの取り組みと、ファーストサービスリリース

  3. 現場でやってみて:ビッグローブでのDDD実践内容

  4. これからの取り組み:組織でこれからドメイン駆動設計で向かうところ、組織でのメンバ育成について

各発表者の資料のリンクです。残念ながら、最初に発表したスライドはアップ不可とのことだったので、未掲載です。

www.slideshare.net

www.slideshare.net

www.slideshare.net

ついでに、【Togetterまとめ】のリンクも張っておきます。

ビッグローブ × DDD 〜ドメイン駆動設計の現場〜(2015/6/18) #devlove - Togetterまとめ

参加してくれた方の感想として、よかった点と次回へ期待することを会社でよく使うホワイトシートと付箋で書き出してもらいました。

感想を見る限りだと、結構満足してくれたみたいなので、ホッとしました。

f:id:mmm-mao:20150619103956j:plain

f:id:mmm-mao:20150619104003j:plain

まとめ

僕個人としては、社外向けの発表が初めてだったので、すごくいい経験ができました。

スライド作成時は、約2年前のことが中心だったので、思い出すのが必須だったり、当時のドキュメントを見つけるたびに懐かしくなったり、当時のチームメンバと昔話をしたりしてました。

これから他社との交流ができるきっかけができたので、会社としてはすごいよかったです。僕らと同じような会社は他にもあると思うので、お互いの会社で意見交換して、より良い環境作りができればなっと思っています。

最後になりますが、今回は導入部分ということで、具体的な実践編は他メンバに任せたんですが、僕もいろいろ話したい内容があるので、次の機会があれば、ぜひ発表したいなっと思います。

エリックエバンスのドメイン駆動設計の「第13章 より深い洞察へ向かうリファクタリング」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第13章 より深い洞察へ向かうリファクタリング」のまとめです。

重点的に取り組む活動

より深い洞察へ向かうリファクタリングにおいて、重点的に取り組む活動は以下3点となります。

  1. ドメインを学習する。

  2. 常に、物事に対して違う見方をする。

  3. ドメインエキスパートとの対話を途切れさせない。

開始

リファクタリングは様々な方法で始めることが可能で、最初のアプローチはコードの見直しではなく、ドメインモデルの見直しです。

探求

ドメインモデルの見直しにおいて、ドメインモデルに対する不満の原因が何であれ、意図を明確かつ自然に伝達するようモデルを改良する方法を探し出すことです。

先達の技

改良の探し出す手がかりとして、欠けている概念やより優れたモデルを求めるブレインストーミングのプロセスは極めて懐く、どんな情報源からの考え方でも吸収できます。 具体的な情報源は

などが挙げられます。

これらの情報源と現場の知識と組み合わせてかみ砕き続けることで、現状の問題に対する答えを探します。

タイミング

リファクタリングを実施するタイミングを判断することは難しいが、以下に該当する場合はリファクタリングを実施すべきです。

  • 設計が、ドメインに関するチームの現在の理解を表現していない。

  • 重要な概念が設計で暗黙的になっている。(明示的にする方法がわかっている。)

  • 設計において重要な部分を、よりしなやかにするチャンスがある。

まとめ

深い洞察へと向かうリファクタリングは、継続したプロセスです。

モデルを安定して改良しているところで、突然ブレイクスルーが起こるかもしれません。

ブレイクスルーが起こることによって、深いモデルとしなやかな設計に繋がる変更が発生します。

そして、また安定した改良が始まり、ブレイクスルーがいつか起こる。という繰り返しになります。

以上が「第13章 より深い洞察へ向かうリファクタリング」のまとめです。

エリックエバンスのドメイン駆動設計の「第10章 しなやかな設計」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第10章 しなやかな設計」のまとめです。

概要

初期リリース以降も、プロジェクトの進行を加速させるためには、変更に強い設計が必要不可欠です。

そのため、しなやかな設計とは『変更に強い設計』と定義されます。

DDD本で紹介されている以下6つのパターンを用いることによって、設計にしなやかさを与えることが可能になります。

No パターン名 概要
1 意図の明白なインターフェース 名前はユビキタス言語で命名する。
2 副作用のない関数 複雑なロジックは、副作用のない関数によって、安全に実行する。
3 表明 システムの状態を変更するメソッドは、表明によって特徴づける。
4 概念の輪郭 ドメインオブジェクトは意味のある単位に分割する。
5 独立したクラス ドメインオブジェクト間の結合は低結合にする。
6 閉じた操作 引数の型と戻り値の型が同じ場合は、閉じた操作と呼ぶ。

説明するパターンは、

  • 抽象化:理解を容易にして、詳細を隠蔽する。(1と2と3)

  • 分割:適切な粒度を保ち、かつ依存関係の排除する。(4と5と6)

に分類されます。

具体的なパターンの詳細は、一つずつ解説します。

意図の明白なインターフェース

クラス名、メソッド名に対して、手段ではなく、目的が把握できるユビキタス言語の名前をつけます

ユビキタス言語にすることによって、チームメンバがすぐ意味を推測できるようになります。また、呼び出し側はインターフェースの内部処理を理解不要となります。

具体的な例として、以前僕が社内でやったリファクタリングのワークショップのスライドがあったので、以下を参考にしてください。

f:id:mmm-mao:20150609083816j:plain

副作用のない関数

副作用とは

副作用とは、システムの状態に対して行われる、あらゆる変更を意味します。

そのため、呼び出し側で、副作用があるのか/ないのかを安全に予測できることが重要となってきます。

補足:コマンドクエリ分離

操作は以下2つのカテゴリに分けられます。

No カテゴリ名 概要
1 コマンド システムに何らかの変更を与える操作
2 クエリ システムに変更を加えずに、何らかの値を返却する操作

やりたいこと

  • 業務ロジックのうち、できる限り多くの部分をクエリで実現する。

  • コマンドは厳密にクエリと分離してドメインオブジェクトを戻さない、非常に単純な操作にする。

表明

システムの状態を変更するメソッド(先ほどのコマンド)は、表明によって特徴づけます。

表明の表現方法としては、以下があります。

No 条件名 概要
1 事後条件 事後条件が操作の副作用なので、メソッドを呼び出すことで保証される結果を記述する
2 事前条件 事前条件とは事後条件が成り立つことを保証するために満たすべき条件
3 クラスの不変条件 あらゆる操作が終わったときのオブジェクトの状態を宣言。集約全体の整合性に関するルールを厳密に定義する

表明の具体的な表現方法としては、自動化されたユニットテストで表現することになります。

テストの内容としては、テストを準備する際に事前条件を整えて、実行後の事後条件が成り立っているかどうかを判定するだけになります。

概念の輪郭

ドメインオブジェクトは、意味のある単位をより直感的に使用したり、組み合わせたりできるような単位で分割します。

最初から理想となる分割はできず、リファクタリングした結果によるものです。

ただし、リファクタリングは技術視点ではなく、ドメイン視点で実施する必要があります。

独立したクラス

関連が多ければ多いほど、モデルは複雑になるので、オブジェクト間の関連は低結合になるように徹底的にやります。

閉じた操作

引数の型と戻り値の型が同じ場合は、閉じた操作と呼びます。

閉じた操作にすることによって、引数と戻り値が同じ型になるので、余計な概念を呼び込む必要がなくなります。

逆の例だと、操作の引数や戻り値に新たなクラスが登場すると、新たな依存関係ができるという現象になります。

以上が「第10章 しなやかな設計」のまとめです。

エリックエバンスのドメイン駆動設計の「第9章 暗黙的な概念を明示的にする」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第9章 暗黙的な概念を明示的にする」のまとめです。

概要

ユーザの活動や問題、その解決策に関する本質的な知識は、ドメインの中心となる概念によってシンプルに表現できます。

概念を明示的にするための最初のステップは、ドメインにおける本質的な概念をモデルで表現することです。

その後、ドメインモデルとコードが大きく変化するのは、ドメインエキスパートとのコミュニケーションであったり、設計の中に暗に存在する概念に開発者が気づき、モデルの中で明示的に表現したときです。

こうした変化によりブレイクスルーになることもあるが、まずは暗黙的な概念を荒削りであってもモデルで表現することが大事です。

概念を掘り出す

暗黙的な概念を掘り出す手段としては、コミュニケーションと設計の両方のぎこちなさがあります。

コミュニケーションきごちさな

コミュニケーションのぎこちなさとは、例えば以下のような点があります。

  • 開発者が特定のフレーズを使ったときに、ドメインエキスパートが困惑した状況になるか。

  • ドメインエキスパートの言葉の選び方に注目するして、開発者が新しい言葉を耳にするか。

  • ドキュメントに記述された言葉と話す言葉にズレがないか。

コミュニケーションのぎこちなさに敏感になることによって、モデルにとって有益になり得る概念を示す手がかりとなります。

また、ドメインエキスパートがドメインモデルにない語彙を使用したら警告サインで、さらに、ドメインエキスパートと開発者の両方ともドメインモデルにない語彙を使用したら2倍の警告サインとなります。このような状況を好機と捉えて、その語彙を取り込むことでモデルとコードを改善するよい機会となります。

設計のきごちなさ

設計のぎこちなさとは、例えば以下のような点があります。

  • 新しい要求が出てくるたびに複雑さが増しているようなところ

  • 説明しにくい複雑な処理をおこなっているところ

僕の経験からすると、複雑すぎて、本来の責務外の仕事もやっていても気づけないという状況がありました。本来の責務外の仕事をしているので、より複雑になってしまう。という負のスパイラルです。

複雑な処理をしているところは、処理に対するドメイン側の活動における目的や背景を理解するところからスタートすべきかと思います。

それほど明白ではない概念をモデル化する方法

明示的な制約

制約は、モデルの概念において、とりわけ重要なカテゴリなので、明示的に表現することによって、設計が大幅に向上します。

制約を独立したメソッド・クラスにすることによって、メソッド・クラスに意図の明白な名前をつけることができドメインモデル上でも制約を表現できます。

制約を独立したオブジェクトにしたほうがいいケースとしては以下のような点があります。

  • 制約を評価するために、本来であればオブジェクトの定義に合わないデータが必要。

  • 関連するルールが複数のオブジェクトに出てきて、コードを重複する必要があったり、本来であれば同じ系統でないオブジェクトを継承する必要がある。

  • 設計や要求に関する会話が制約をめぐって行われるが、実装では制約が手続き型のコードの中に隠されている。

独立したオブジェクトにする場合の置き場所は、ドメイン層になります。

ただ、僕は実プロジェクトで失敗したんですが、制約を何でもかんでも独立したオブジェクトにしてしまって、魂の抜けたエンティティや値オブジェクトが出来上がってしまいました。

なので、制約を使い始めの時は、独立したオブジェクトにするかどうかを慎重に議論したほうがよいと思います。

ドメインオブジェクトとしてのプロセス

ドメインオブジェクトとしてのプロセスは明示すべきプロセスであり、ドメインモデルに表現すべきです。

明示すべきプロセスと隠蔽すべきプロセスを区別する鍵は、ドメインエキスパートが話題に上げるか、コンピュータプログラムに置ける仕組みの一部にすぎないか。です。

仕様

概要

ビジネスルールは、エンティティや値オブジェクトの責務にどれも合致しないことがあったり、合致してもエンティティや値オブジェクトの基本的な意味を圧倒しかねないです。

このようなケースの対処として、特殊な目的を持った値オブジェクトを明示的に作成します。

この値オブジェクトのことを仕様と呼び、仕様とはあるオブジェクトが何らかの基準を満たしているかどうかを判定することです。

仕様の適用と実装

仕様の具体的な用途としては、以下3点あります。

①検証

オブジェクトを検証して、何らかの要求を満たしているか。を調べます。

例えば、有料会員サービスでの請求業務だと、過去に料金未回収になった会員(ブラックリスト会員)か?という検証があります。

この検証を独立したオブジェクトで表現するのか?会員の1メソッドで表現するのか?は、その時次第かと思います。

②選択

一覧から特定の条件を満たしたオブジェクトを選択します。

先ほどの有料会員サービスの続きの例で進めますが、全会員の中からブラックリストの会員一覧を取得する。という選択があります。

具体的な手段として、DDD本では

  1. SQLで対象者を選択するのか?

  2. SQLでは全会員を抜き出して、アプリケーションコードで選択するのか?(検証の例で書いたクラスかメソッドを利用)

がありました。

ただ、どちらの手段でも、抽出条件はドメイン層で定義しています。なので、「1」案だとSQLのWHERE句をドメイン層に記述する必要が出てきます。

性能として問題なければ、「2」案の手段のほうがドメイン層の責務がはっきりします。(さすがにSQLドメイン層に記述したくないので。。)

③要求

何かの仕様に適合する新しいオブジェクトの生成を定義します。

先ほどの有料会員サービスの続きの例で進めますが、ブラックリストの会員一覧が必要なときに、

  1. ブラックリスト会員一覧というオブジェクトを明示的に表現する。

  2. 会員一覧というオブジェクトで表現され、その一覧がブラックリスト会員になっている。ただし、会員一覧なので、使い方次第ではブラックリスト会員以外も入ってくる。

の表現方法があります。

「1」案だと、ブラックリスト会員という仕様に適合したブラックリスト会員一覧の新しいオブジェクトを生成するという責務があるので、今回の要求に該当します。

「2」案だと、会員一覧というオブジェクトが必ずしもブラックリスト会員のみという仕様を満たしていないので、今回の要求に該当しません。

まとめ

暗黙的な概念を明示する一番の効果的な手段は、ドメインエキスパートとのコミュニケーションで、特にコミュニケーションのズレ・ぎこちなさに敏感になれるかだと思います。

実際、ズレ・ぎこちなさに気づけても、それをドメインモデルまで(コードも)反映されることが大事になります。

なので、まずはドメインエキスパートとしっかりコミュニケーションできる環境が大事ですね。

以上が「第9章 暗黙的な概念を明示的にする」のまとめです。

エリックエバンスのドメイン駆動設計の「第6章 ドメインオブジェクトのライフサイクル」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第6章 ドメインオブジェクトのライフサイクル」のまとめです。

概要

永続化を伴うようなライフサイクルの寿命が長いドメインオブジェクトを管理することは、以下のような課題を解決する必要があるので、難しくなります。

  1. ライフサイクルを通じて、不変条件を維持すること。

  2. ドメインオブジェクト間の複雑な相互依存関係を解決すること。

  3. ライフサイクルを管理するのが複雑でも、モデルが浸食されないようにすること。

ドメイン駆動設計では、3つのパターン(集約、ファクトリ、リポジトリ)を通して、これらの課題に対応しています。

  • 集約は課題の1.と2.に対応。

  • ファクトリは課題の3.に対応。

  • リポジトリは課題の3.に対応。

不変条件とは?

課題1.で説明されている不変条件とは、 何かの業務を行う時は維持すべき重要なビジネルルールが常に守られていること。です。

まずは、業務視点で守るべきビジネスルールを明確化した上で、システムとしての対応は

  • 一つの集約内で適用される不変条件はトランザクション完了時に強制する。

  • 複数の集約にまたがるルールは何らかの更新の仕組みを通じて、一定時間内で維持される。

ということになります。

※具体的な例は、集約のところで説明します。

集約

目的

複雑なドメインモデルにおいて、ライフサイクルの遷移に伴う一貫性を保証することは難しい状況になりえます。

その状況化で、重要なビジネスルールが守られることを保証するために、ドメインとして境界を定義して、境界内で重要なビジネスルールが守られることを保証します。

その境界の一つ一つが集約という単位になります。

定義

  • エンティティと値オブジェクトを集約の中にまとめ、各集約の周囲に境界を定義すること。

  • 各集約に対してルートとなるエンティティを1つ選んで、境界の内部に存在するオブジェクトへのアクセスは、ルートを経由して制御すること。

  • 外部オブジェクトが参照を保持できるのはルートのみ。

  • 集約で区切られた範囲で不変条件を維持すること。

具体例

DDD本で説明されていた例になりますが、『ダメな集約例』と『イケテル集約例』です。

f:id:mmm-mao:20150526083819j:plain f:id:mmm-mao:20150526083816j:plain

『ダメな集約例』のどこがダメかというと、以下の流れで注文明細が操作されたケースです。 集約の単位が購入注文明細になっているので、フェーズ2の同タイミングで操作された場合に、ビジネルルール(承認限度額>=注文明細の総額)が破られてしまいます。

f:id:mmm-mao:20150526084826j:plain

そのため、今回のビジネルルールを守るためには、購入注文と購入注文明細を1つの集約として捉える必要があります。

ファクトリ

目的

集約の生成に関しては、以下の課題があります。

  • 複雑な集約の生成は、生成される集約の責務に合わない。

  • 仮に生成の責務を集約が担ってしまうと、責務が明確になりずらくなる。

  • 集約のクライアントに直接生成させると、クライアントの本来の責務が不明瞭になり、生成される集約のカプセル化も違反することになる。

これらの課題を解決するためにファクトリという考え方を用います。

定義

  • ライフサイクルの始まりに対応して、複雑な集約を生成する。

  • 集約を生成するのが複雑になったり、内部構造をさらけ出しすぎていたりする場合、複雑な内部構造をカプセル化する。

  • 集約ごとにファクトリを一つ用意する。

リポジトリ

目的

ライフサイクルの初期以降後に、何かの業務イベントを実行するためには、既に存在するドメインオブジェクトが必要になります。 また、実行後は、ドメインオブジェクトを永続化する必要があります。

ドメイン駆動設計の目標は、技術ではなく、ドメインに焦点を合わせることなので、上のような具体的な手段をカプセル化するために、リポジトリという考え方を用います。

定義

  • 永続化されたオブジェクトにアクセスする手段を提供する。

  • クライアントに対して、永続化されたオブジェクトを取得し、ライフサイクルを管理するためのユビキタス言語に関するシンプルな操作を提供する。

  • ドメインの設計から、永続化技術などから分離する。

  • 集約ごとにリポジトリを一つ用意する。

具体例

ドメインの設計から永続化技術などを分離するために、ドメイン層としてはリポジトリのインターフェースだけを提供して、インフラストラクチャで具体的な技術手段を用いた操作を提供する。という責務にしています。

また、永続化する部分に関しても、よくsaveメソッドだけ提供されている例を見かけますが、僕が普段開発する場合は、ユビキタス言語に関するシンプルな操作(登録や退会)を提供しています。

f:id:mmm-mao:20150526081842j:plain

まとめ

集約を区切るスコープによって、不変条件の維持すべき範囲が示され、ライフサイクルの遷移における複雑さはファクトリとリポジトリによってカプセル化されます。

以上が「第6章 ドメインオブジェクトのライフサイクル」のまとめです。

エリックエバンスのドメイン駆動設計の「第5章 ソフトウェアで表現されたモデル」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第5章 ソフトウェアで表現されたモデル」のまとめです。

概要

基本要素

モデルと実装を結びつけるための基本要素は、以下の5つとなっています。

  1. 関連

  2. エンティティ

  3. 値オブジェクト

  4. サービス

  5. モジュール

これらの構成要素によって、モデルはソフトウェアという形に具現化されます。

また、実装の際に避けられない妥協を行う場合でも、基本要素を意識することにより、大きく道を外すこともなくなります。

モデルを表現する要素

ドメイン駆動設計において、モデル要素の持つ意味を明確にできるようなパターンが存在します。

概念を何らかのパターンで定義することで、その概念の責務が明確になります。

そのパターンとは、5つの基本要素のエンティティ、値オブジェクト、サービスの3つです。

1.関連

概要

ドメインモデルと実装を結びつけることが重要なので、モデルにおいて辿ることのできる関連は、コードにおいても同じように辿る仕組みが必要です。

ただ、概念間の関連は、モデル上で描くのは簡単かもしれないが、実装となると複雑になることがよくあります。

そのため、関連を扱いやすくする方法があります。

関連を扱いやすくする方法

関連を扱いやすくする方法は、以下の3つの方法があります。

  1. 関連を辿る方向を強制する。

  2. 制約をつけて、多重度を効果的に減らす。

  3. 本質的でない関連を除去する。

関連を辿る方向を強制する
  • 関連を双方向から一方向にすることにより、相互依存関係が減り、設計がシンプルになる。

  • ドメインを理解して、本来はどちらの方向性が重要であるのかを明確にする。

  • それでも双方向が残るなら、双方向であることがドメインの特徴となる。

制約をつけて、多重度を効果的に減らす
  • 制約をつけることによって、1対1の関連にする。

  • 制約により重要なルールが明示的にモデルへ組み込む

僕の現場での経験からすると、制約という考え方をよく使ってます。

1対多の関係になる2つの概念があった場合、1対多だとモデル上もコード上も扱いにくいケースがよくあるので、制約を用いて1対1にしています。

ただ、今はこのような制約を暗黙のルールにしているケースが多いので、これからは明示的なルールとなるようなモデルを作ろうと思いました。

本質的でない関連を除去する
  • ドメインにおける重要度を反映させるように、関連を限定する。

2.エンティティ

定義

  • ある概念が属性ではなくビジネス上の同一性によって識別される場合は、エンティティとして定義する。

例えば、ECサイトでAさんとBさんが同じ商品を購入して注文を受け付けるというケースがあった場合、Aさんの注文とBさんの注文を識別する必要があるかどうかでエンティティか値オブジェクトどうかが決まります。このケースだと識別する必要があるので、エンティティになります。

  • ビジネスのライフサイクルにおいて、エンティティの形態と内容が変化しても、連続性のつながりを維持する必要ある。

先ほどの例だと、注文という概念のビジネスライフサイクルとして、注文受付→発送→着荷→代金回収というイベントが発生していくことになるかと思います。

その際、各イベントが発生したら、エンティティの内容は変化するけど、同じ注文として扱う必要があるということです。

責務

  • エンティティを識別する手段を提供。

  • ライフサイクルにおける各振る舞いを提供。

  • 各ライフサイクルに必要な属性のみ保持。

3.値オブジェクト

定義

  • 属性値のみ関心の対象となる場合は、値オブジェクトとなる。

先ほどのECサイトの例だと、注文された商品名は値オブジェクトに該当します。

責務

  • 保持する属性に関係した振る舞いを提供する。

  • 値オブジェクトの粒度は、値オブジェクトを保持する側の関心ごとに合わせる。

例えば、以下だと「イケテル顧客」は保持する側の関心ごとに合っていますけど、「ダメな顧客」は住所部分が細かいので保持する側の関心ごとに合っていません。 f:id:mmm-mao:20150522075600j:plain

4.サービス

定義

  • 実体・概念よりも活動(クライアントに対して名にが実行できるか)を表現したい場合はサービスとなる。そのため、操作の名前が必ずユビキタス言語の一部にすること。

責務

先ほどのECサイトの例だと、注文を受け付けるというのがサービスとなります。

5.モジュール(パッケージ)

定義

  • ドメインの大きすぎる概念を分割していく。

分割することをモジュール化と呼んでいるが、モジュール化のメリットして、全体から見たらモジュールの詳細を無視して、モジュール間の関係を見ることができる。 また、モジュールの詳細を見たかったら、全体に圧倒されずに、見ることができる。

重要なこと

  • 相互に独立して理解でき、理論づけることのできる概念をモジュール化して、モジュール間を低結合で実現すること。

  • モデルがドメインの概念に従って分割され、対応するコードも同じように分離されるようになるまで、モデルを改良すること。

  • 採用した技術的なフレームワークの規約に沿って、ドメイン層のモジュール化を行うと、概念的な要素がバラバラになるので、モデルとコードが連動しなくなる。あくまでドメインから見た観点で分割すること。

  • モジュール名はユビキタス言語で定義され、ドメインに対する本質を反映する

以上が「第5章 ソフトウェアで表現されたモデル」のまとめです。

エリックエバンスのドメイン駆動設計の「第4章 ドメインを隔離する」まとめ

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

今回は、「第4章 ドメインを隔離する」のまとめです。

概要

ドメインオブジェクトはシステムの他の機能から切り離すことが重要です。

他の機能とは、画面やデータの永続化の機能を指します。

隔離手段 レイヤ化アーキテクチャ

ドメインオブジェクトを隔離する手段として、レイヤ化アーキテクチャがあります。

この考え方は、DDD以外でもよく見かける考え方です。

DDDでは、以下4層に分離させます。

1.ユーザインターフェース層

  • ユーザに情報を表示して、ユーザからのなんらかのリクエストを解釈する。

2.アプリケーション層

  • ビジネスルールや知識は含まない。

  • やるべき作業を調整するだけで、実際の処理は、ドメイン層に委譲する。

  • ビジネス状況を反映する状態は保持しない。

  • 画面遷移時のセッションなどの作業進捗を反映する状態は持つことが可能。

3.ドメイン

  • ビジネスの概念とビジネスが置かれた状況に関する情報・ビジネスルールを表す。

  • ビジネスの状況を反映する状態はドメイン層で制御され、実際の格納処理はインフラストラクチャに委譲される。

4.インフラストラクチャ層

  • ドメインオブジェクトの永続化処理を行う。

  • アプリケーションのためのメッセージ送信。

各レイヤの依存関係

DDD本ではドメイン層からインフラストラクチャ層への依存がありますが、僕がやっているやり方だと、『ドメイン層を隔離する』ということなので、下の図のようにドメイン層はどの層にも依存していません。

f:id:mmm-mao:20150509092245j:plain

ドメイン層からインフラストラクチャ層の依存の向きを逆にするやり方として、インフラストラクチャ層で実装するクラスのインターフェースをドメイン層で定義する。というやり方です。

このようにすることによって、永続化処理を実施したい場合、ドメイン層にあるインターフェースに対して処理を委譲することができます。

ただ、インターフェースへの委譲を実現することが可能なフレームワークを選定する必要があります。 僕が採用しているフレームワークSpring Frameworkで、DI(Autowired)の機能を用いて実現しています。

余談

実践において、アプリケーション層とドメイン層の責務を明確に分離することは難しいかと思っています。 理由としては、その時に置かれている状況(メンバのスキルやチーム間のメンバローテーションなど)によって、どちらの層の責務にするのか。都度判断かと思います。

例えば、僕のDDDでは、アプリケーション層のみ永続化のためのリポジトリの操作を実施するという制約にしています。本来なら、ドメイン層でリポジトリの操作を実施したほうが業務の概念にマッチする場合でも、アプリケーション層で実施しています。

仮に、ドメイン層でリポジトリの操作を実施する場合は、ドメイン層で実施して良いケース・悪いケースの判別をチームがする必要あります。 チームの中に熟練者が確実にいる状況なら安心なのですが、熟練者がいない状況もあるので、

①アプリケーション層でリポジトリ操作 ②ドメイン層の悪いケースでリポジトリ操作される可能性がある

のどちらを採用するか。だと思います。

僕の立場だと、チームを立ち上げて、半年後くらいには別チームの立ち上げなので、僕が抜けた後でも安全な設計にすべきなので、①を採用しています。

なので、全般的なことですが、『今』で判断するのではなく、『長期的な視点(2,3年後くらい)』で変更に強いシステムを作り上げることが大事だなっと最近思っています。

ただ、常に安全な選択肢をしていると、新しいことにチャレンジできず、組織としてDDDの成熟度が上がらないので、安全な選択肢/チャレンジの落とし所は難しいです。

隔離する目的

隔離する目的として、各レイヤが自らの責務を担うことによって、ドメイン層はドメインモデルを表現する責務に専念できる。が挙げられます。

また、ドメイン層を隔離するメリットとして

  • 本質的なビジネスの知識を捉えることが可能。

  • プロジェクトが進むごとに、進化しやすい設計。

  • ソフトウェアの技術(htmlやsqlなど)に関係する概念と混同しなくなるので、ドメインを見失うことを避ける。

となります。

以上が「第4章 ドメインを隔離する」のまとめです。

ドメインモデルと実装を一致させるためには、ドメイン層が隔離されていることが重要になってきます。

ぜひ、実際のコードで試してみて、ドメイン層を隔離した時のメリットを実感してもらえたらと思います。