はじめに
データベーススペシャリストの過去問(2021年秋午後Iの問1)を例に、
ドメイン駆動設計でモデリングをしてエンティティと値オブジェクトのサンプルプログラムとテストコードを
作成したので(Java + Springで作成)、
ドメインモデル図をベースにエンティティと値オブジェクトを実装していく際に感じたことや気付き、
僕のエンティティや値オブジェクトに対する役割の解釈などについて、
本記事にまとめました。
>>モデリング対象(データベーススペシャリスト2021秋午後I問1)
ドメイン駆動設計の練習モデリング編についてはこちらにまとめています。
ドメイン駆動設計のモデリング練習|データベーススペシャリストの過去問を題材に
本記事では、
松岡さんの「サンプルコード&FAQ」、「モデリング/実装ガイド」を参考にさせて頂きました。
サンプルプログラムの作成及びテストコードの作成は本をなぞった形になります。
(その点お含みおきください)
2022/03/22追記
ドメイン駆動設計でモデリング〜コーディング〜テストコード作成
までのまとめ記事を作ったので興味がある方は是非みてください。
ドメイン駆動設計でモデリング〜サンプルプログラム・テストコード作成(まとめ記事)
エンティティと値オブジェクトの違いとは??
エンティティと値オブジェクトの違いについて本ではこのように述べられていました。
エンティティと値オブジェクトは、ドメインモデルを「モノ」として表現するオブジェクト
「モノ」として表現するオブジェクトが2種類ありますが、この違いは何でしょうか?
それは、同一判断の方法です。エンティティは同一判定を識別子で行い、値オブジェクトは同一判定を
保持する値で行います。
ドメイン駆動設計 モデリング/実装ガイド 第6章(松岡 幸一郎)
エンティティはユーザIDのようなユーザを一意に特定できる属性を持ち、
他の属性例えばニックネームとかが変わっても、ユーザIDで一意に特定するので同一ユーザと判断できる。
一方、値オブジェクトは金額というような値を持ち、
金額オブジェクトを比較する際は金額オブジェクトが持つ金額の値によって同一金額かどうか判断できます。
また、エンティティは後から変更可能で、値オブジェクトは一度作成したら不変なものとして扱います。
ユーザの名前などは後から変更できる想定だけど、
10円として値として生成した値オブジェクトは何があっても10円のままで、20円にしたければ、
20円の値オブジェクトを別途生成するイメージ。
エンティティと値オブジェクトはこれら違いを意識してモデリングしたり使い分ける必要があります!!
値オブジェクトをなぜわざわざ使うのか??使うメリットは??
僕が値オブジェクトを使うかどうかの判断をする際は、以下メリットのために利用します。
- 副作用による意図しないオブジェクトの変更を回避できる
- 本来取り扱いたい範囲の値のみ生成できる
とある巨大なメソッド(中で何をどう変更しているのか全て把握するのは難しい)があって、
誰が作ったかわからないけど、スケジュール的にもそれをブラックボックス的に使うしかない。。。
みたいなケースを妄想してください。そんなケースないか・・・・
こういう、オブジェクトがどこかで変更されちゃうかも??という心配が値オブジェクトにはなくなります。
(そもそも副作用ありまくり巨大メソッドを直せばいい話ではあるのですが、、、)
また、金額を取り扱うシステムでシステムとしては0~1,000,000円までを取り扱えば良いのに、
int型で定義すると-2147483648~2147483647を扱える状態になります。
そうなるとマイナスを入れられた時に、
貰うべき金額が逆に支払うような予期せぬ事態が発生したり。。。といったことに繋がります。
これを値オブジェクトにしておく事で、
例えばコンストラクタで扱う金額の範囲をチェックしてやれれば、金額の範囲のチェックを1箇所だけに
集められ、尚且つマイナスを入れられて予期せぬ事態に陥るといったことも避けられます。
僕はこれらメリットがあると判断した場合のみに値オブジェクトを使うようにしています。
逆に、
〇〇IDだから必ず値オブジェクトにしよう!!とかはせず、モデリングを進めて必要に応じて
値オブジェクトを使うようにしています。
エンティティと値オブジェクトの実装〜テストコード作成
ここからは、
作成したエンティティと値オブジェクトとそれらのテストコードについて紹介していきます。
支払エンティティ
モデリングでいう支払集約のところになります。
支払が発生すると必ず支払明細、支払方法明細ができる想定なので、
支払エンティティのコンストラクタでそのチェックをするようにし、
支払明細がないようなオブジェクトは生成できないようにしました。
価格値オブジェクト
システムで取り扱う金額の範囲を値オブジェクトインスタンス化の際にチェックするようにしました。
こちらもシステムで取り扱う金額の範囲外の値オブジェクトは生成できなくしています。
支払エンティティのテストコード
支払エンティティのオブジェクト作成時のチェックをテストしています。
松岡さんの本を参考にテスト用のインスタンス作成用FactoryをJavaで作ってみたのですが、、、
本と同じようにする場合、
デフォルト引数がないせいでオーバーロードで結構記述が必要でした。
(Kotlinのよさを1つ知りました、、、このサンプルコードを後でKotlinでも書いてみようと思ってます)
価格値オブジェクトのテストコード
こちらも支払いエンティティと同じく価格オブジェクト作成時のチェックについてテストしています。
エンティティや値オブジェクトのサンプルコードを作成してみた感想や気付き
本の通りモデリングを先に実施してドメインモデル図があって、
そこからエンティティを作成するとモデリングの結果がきれいにソースコードにマップされていく感じが
体験できました。
この方式でモデリングしてからその通りにエンティティに紐づくドメインルールをエンティティに記載できれば、
あちこちビジネスルールが散らばることが避けれると思います。
現場で昔から僕がよく見てためちゃくちゃ長い上から下に諸々チェックのやり方がずらっと書かれた
エクセルの設計書だと、それをベースに作成するプログラムもどうしてもビジネスルールを
神Serviceで上から下にずらっと書いてしまいたくなりますが、
ドメインモデル図をベースにプログラムを書くと、
ドメインルールは紐づくエンティティや値オブジェクトに書くのが自然の流れになると思います。
(結果ドメインルールが1箇所に纏まる)
ドメインモデル図のあのフォーマットの良さが、
プログラムを実際に書くことで改めて実感することになりました。
ユースケース層は従来の設計書風に上から下に書きつつ、ビジネスルールはドメインモデル図の方を参照
みたいにさせるなど設計書のフォーマットの工夫によって、
少ない資料でよりプログラムに自然に落とし込める流れが作れそうな気もします。
早速現場で提案して試してみます!!!