【読書録】ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本
前回の読書録で「現場で役立つシステム設計の原則」を扱い、その「この本と一緒に読むと面白そうな本」欄でこの本を書きました。「現場で役立つシステム設計の原則」を読んでからこの本を読みましたので、早速読書録を書き残したいと思います。
読んでみようとした経緯
- DDDを理解して、分かりやすくて変更しやすいコードを書けるようになりたいから
- エヴァンス本を理解出来なくて辛かったところ、この本が良いという噂を聞いたため
- 著者の成瀬さんのDDDに関する講演がとても分かりやすくて面白かったので、本も読みたいと思ったため
読むモチベーション
- 意図が伝わりやすいコードを書きたい
- 適切な場所で適切な戦術的DDDの技法を使えるようになりたい
- 戦術的DDDを実践するときになぜそれを実践したのか説明できるようになりたい
概要
ドメイン駆動設計をコードに落とし込むための手法「戦術的DDD」のうちいくつかのパターンを紹介しています。
最初は値オブジェクトから始まり、エンティティ、ドメインサービス、リポジトリ、アプリケーションサービス、集約…とだんだん抽象的になっていく構成です。
具体的でイメージのつきやすいものから始めてくれているおかげで、「途中から急に難易度があがって辛い!」ということがなくて読みやすかったです。
「本書の対象読者」の章にも書いてありますが、この本を読むには一般的なオブジェクト指向プログラミングの基礎知識が必要だそうです。
そういうこともあり、「現場で役立つシステム設計の原則」でオブジェクト指向プログラミングを学んでからこの本を読んだのは正解だったな〜と我ながら感心しています。
この本で特に学びになったこと
1. 値オブジェクトの考え方
値オブジェクトとは、システム固有の型のようなものです。
プログラミング言語レベルだとintegerやstringなどのプリミティブ型があります。でも、intやstringと定義しただけではどんな制約のあるのか分かりづらい時があります。例えば、作っているシステムで名前を扱う場合、名前は名字と名前から成り立っているというルールがあるとしましょう。'山田 太郎'というstring型でも表せます。しかし、これだとどこからどこまでが名字なのかわかりません。仮に' 'で区切ったとしても、'コウ ウラキ'のように名前と名字が逆で書かれる場合もあります。
そこで、名字と名前をもったオブジェクトにします。名字の属性と名前の属性をもった型を使用することで、それぞれの属性に値を渡せば'山田 太郎' 'コウ ウラキ'のような問題がなくなります。
「値オブジェクトは不変」という説明の意味も今までモヤっとしていましたが、integerやstirngの変数に「int_hoge.changeValue(1)」ようにできないのと同じ考えという説明は目から鱗でした。
また、Pythonで値オブジェクトを扱う時はfrozen=Trueにしたdataclassを使っていますがこれは間違っていないようで自信を持てました。pydanticを使う場合、allow_mutation=Falseにしたpydantic.BaseModelを継承したクラスでも良さそうです。
値オブジェクトを難しく考えていましたが、スッキリしました。
2. エンティティの考え方
エンティティと値オブジェクトの違いが良くわかっていなかったけれども、この本の説明はわかりやすいと思いました。
バリューオブジェクトのような値ではなく、変化されるオブジェクトです。10という数字は10という数字のままで、10.change_value()のようにはできません。社員オブジェクトのように部署や名前が変わりうるものはエンティティです。可変と不変がいまいちピンときていませんでしたが、納得できました。
また、社員オブジェクトの部署や名前は変わります。現実世界では、社員の名前が変わっても同じ人だと分かります。プログラムでも属性が変わっても同じと認識する必要があります。そのために同じだと認識させる属性を用意します。それがIDなどの一つしかないものになります。それを「同一性」と説明されるものだと分かりました。
何が可変なオブジェクトで何が不変かを整理すれば、不変であるべきオブジェクトを不変のままにでき、より正確なプログラムが出来そうだと思いました。
3. 凝縮度という観点
凝縮度という言葉を良く聞くけれども、よくわかっていませんでした。
凝縮度とはモジュールやクラスの責任範囲がどれだけ集中しているかを測る尺度です。凝縮度を高めると堅牢性、信頼性、再利用性、可用性の点で好ましいと言えます。凝縮度を具体的にどう測るかの計算式「LCOM」があります。端的に言うと、インスタンス変数はすべてのメソッドで使われるべきというものです。いくつかメソッドがあって、使われている変数と使われていない変数が多いほど凝縮度が低いと言えます。
その箇所を読んで、最近自分もレビューで似たようなことを言われてクラスを分けて実装したことを思い出しました。確かに、使われている変数が同じものほど同じ関心ごとを扱っていました。改めて整理すると、とある計算をする業務を扱うまとまりになったので計算を責務とするクラスに分けました。結果、計算を責務とするクラスは計算の内容の見通しが良くなりました。クラスだけでなくモジュールの見通しも良くなりました。文章に例えると、見出しが1つしかなかったのが内容ごとに見出しがついた感覚です。
その修正をしていた時は名前のない営みでしたが、あれが凝縮度を高くするという営みか、と凝縮度の話が身近に感じるようになりました。
4. ファクトリを使う判断
コンストラクタが複雑になった時にファクトリを使うと良く聞きますが、今までコンストラクタで事足りるケースばかりだったので具体的な判断基準がよく分かっていませんでした。
コンストラクタ内で別オブジェクトを生成するのはファクトリを使用する指標となると書かれていました。オブジェクトが変更される際にコンストラクタも変更しないといけない恐れがあり、複雑になる可能性が高いからです。
また、ファクトリの置き場も迷っていました。ファクトリ自体はドメインを表現していませんが、ドメインを表現するために必要であるからです。
生成対象となるオブジェクトのクラスと同じ階層に置くことで、俯瞰した時にひと目でファクトリを利用してオブジェクトを生成することを察することができます。また、同じ階層だけれどもドメインには入れないことでドメインモデルのオブジェクトはドメインルールの表現に集中できます。
この本と一緒に読むと面白そうな本
1. ユースケース駆動開発実践ガイド
読んだことないけど。「ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本」でもユースケースの話は出てきているので関連付けられそうだなあと思いました。
ユースケース図書かないと頭の中が混沌としたままでプログラム書けないのでそれらしきものは書いていますが、なんせ野良です。ちゃんとした書き方を知ってより分かりやすく書きたい&本に書いてある考え方で良いものがあれば取り入れたいです。