[実践ドメイン駆動設計]の1章を読んだので、自分なりに消化できた内容をまとめました。
じっくりとコードを追いながら読みすすめていけば理解できる良書でしたので、初心者にもオススメです。
まずはじめに
DDDの会話をしていると、側から見ると開発者が”技術側の都合でイケている設計にしていこう!”と意気込んでいるように見えてしまうことがしばしばあるようです。
しかしDDDを取り入れようという会話は、むしろ技術側の視点ではなく「業務側の視点を踏まえたソフトウェア設計」にしようという話をしています。
DDDとは
DDDは「業務側の視点も踏まえた」(開発者とドメインエキスパートが同じ土俵で、ドメインモデル(業務を反映させたモデル)を作り上げていく)ソフトウェア設計手法です。
DDDでより良く設計されたソフトウェアは非開発者が見ても(プログラミングの基本文法くらいを学習すれば)ソフトウェア処理が明確な状態に近づいていきます。(この辺りの話のキーワードがユビキタス言語の構築)
DDDの強力なところ
DDDすなわち業務側の視点を踏まえたソフトウェア開発手法で得られる旨味をまとめると、(p.7)
- ドメインエキスパートと開発者を同じ土俵に乗せることで、プログラマーの視点だけでなく業務側の視点も踏まえたソフトウェアを作れるようになる 。
- 業務担当のリーダーやエキスパートがこんな風にソフトウェアを作るであろうという結果にできるだけ近づける。
- そのソフトウェアに関して理解しているのが一部の人(たいていは開発者)たちだけという状況をなくすことができる 。
- ドメインエキスパートとソフトウェア開発者、そしてソフトウェアそのものとの間で、 一切の通訳が不要になる 。
- 設計がコードであり、かつコードが設計でもある 。
ソフトウェア開発でよくあること
事業価値をもたらすソフトウェアは事業戦略あってのものであるので、業務ソフトウェア開発は単に技術だけにクローズされた話ではなく当然ビジネスも絡んできます。
ソフトウェア開発で良くあるケースとして、
- ドメインエキスパートは事業価値をいかにもたらすかだけに注力
- 開発者は業務的な問題をいかに技術で解決するかだけに注力
してしまい、ドメインエキスパートと開発者との間が断絶されることがあります。
これが引きおこす悪影響はいくつかあります。例えば、
- 業務側の要件を開発者が解釈した要件がソフトウェアに反映されてしまう。
- 開発メンバーの入れ替わりとともにドメインに関する知識の解釈が消え去る。
- ソフトウェア開発の際の技術的アプローチによっては、事業の進め方を間違った方向に変えてしまう。(ドメインエキスパートが間違った方向に気づけない。)
DDDでは、ドメインエキスパートと開発者との間の断絶を許しません。これを踏まえて、DDDには3つの重要な側面があります。(それぞれの詳細は後の章で登場してきます。)(pp.9-pp.10)
- ドメインエキスパートとソフトウェア開発者が共同で、ユビキタス言語を確立させ、ユビキタス言語を使用してドメインモデルを構築していく。
- 事業の戦略構想に対応するために、戦術的設計より戦略的設計に関心を向ける。
- 戦術的設計を利用してドメインエキスパートのメンタルモデルを正確に踏まえつつ、ソフトウェアに対する真の技術的要求を満たす。
しかしながらソフトウェアを構成するドメインはとても複雑です。全てに対してDDDを取り入れようとすると開発工数が発散してしまうので、導入する際は、事業における最も重要な領域であり複雑なドメイン、要するに「価値があって重要で、見返りが一番大きなところ」からDDDを導入するべきとされています。そして「価値があって重要で、見返りが一番大きなところ」のことをコアドメインと呼びます。何を持って複雑とするかは業務によって異なるので、費用対効果を考慮し、DDDを導入するに見合うかを判断する必要があります。本書では判断基準の例としてチェックシートが提供されています。
ドメインモデル貧血症
ドメインモデル貧血症 [Anemic domain model] はオブジェクト指向のアンチパターンのひとつとして提唱されていおり、作成したドメインモデルにあるべきロジック(「domain logic - validations, calculations, business rules - whatever you like to call it」)がドメインモデルの外で実施されてしまっていることでドメインモデルの中身が欠如している状態(この状態が貧血症状に似ているから)であることを指します。
仮にユーザーのフルネームを表示することが業務であるドメインモデルを作成する例で考えてみましょう。Userクラスではフルネームを表示するためのfirstNameとlastNameを保持するとします。UserUtilクラスではUserクラスのfirstNameとlastNameを使ってフルネームを返すメソッドを定義するとします。
このソースコードはどうでしょう?ユーザーのフルネームを表示することが業務のドメインモデルを作ろうとしていたのに、domain logicがUserクラスではないクラスで実施されてしまっています。
data class User(val firstName: String,
val lastName: String)
class UserUtil() {
// domain logicがdomain modelの外で実施されている。
fun getFullName(user: User): String {
return user.firstName + user.lastName
}
}
例がシンプルなので一見問題なさそうに見えるかもしれませんが、ドメインモデル貧血症を患い始めています。このままUserクラスにフィールドだけが追加され単なるデータの置き場になったり、Userクラスを使用するクライアントのUserUtilにUserクラスのロジックで肥大化されたりすると重度のドメインモデル貧血症と言われてしまうでしょう。
ユーザーのフルネームを表示することが業務であるドメインモデルを作成するなら、Userクラスにデータの保持と共に、domain logicであるgetFullName()メソッドを記述するべきです。
data class User(val firstName: String,
val lastName: String) {
// domain logicがdomain modelで実施されている。
fun getFullName(): String {
return firstName + lastName
}
}
では、なぜドメインモデル貧血症は良くないのでしょうか。それは貧血症が記憶喪失を引き起こすからとされています。
ドメインモデル貧血症になっているものは、インタフェースに意図がほとんど反映されておらず、処理内容が明確でないので、修正を繰り返すうちに当初の開発者の意図とは全く異なる変更がされ続けてしまいます。本書では様々なユースケースに対して使用できてしまうsave()メソッドの例が紹介されていますが、まさにこのsave()メソッドは実装当初の開発者がいなくなったりそもそも当人の記憶が消え去ると、当初の意図がわからなくなる危険性を孕んでいます。このように貧血症によって当初の記憶が失われて行くことを意味して「貧血誘発性記憶喪失」と呼んでいるのではないかと解釈しています。
ユビキタス言語の構築
具体的にDDDを実践していく方法のひとつに、ユビキタス言語の構築することが挙げられています。本書では「ユビキタス言語とは、ドメインエキスパートやソフトウェア開発者を含めたチーム全体で作り上げる共通言語のこと 」と表現しています。
例えばsave()メソッドの例であれば、何をどう保存するユースケースなのかをチームで議論し、チームで合意の上で最善の用語を選択します。ユビキタス言語にはdomain logicも含まれますので、チームで育てたユビキタス言語をもとにドメインモデルをインターフェースとしてソースコードに起こします。よって、「最も長期間ユビキタス言語の状態をきちんと表し続けるのは、チームでの会話やコード内のモデル」です。また、DDDでは常により良い設計を求め続ける必要があります。