little hands' lab

ドメイン駆動設計、アジャイルプラクティスを実践し、解説しています。

DDD基礎解説:エンティティ、値オブジェクトってなんなんだ

はじめに

DDDの実装パターンとして、エンティティと値オブジェクトというものがあります。
ドメイン駆動一般に複雑な抽象論が多い中で、コードに近く一番イメージがつきやすいコード事例として出てくるため、ここだけは何となくわかるぞ!という方もいらっしゃるのではないでしょうか。
今日はこちらの概要とそれぞれの使い道について書きたいと思います。

先にざっくりイメージ図をお伝えすると、こういう図を使って解説します。

何の目的で作るのか?

ドメイン駆動設計は何を解決しようとしているのか こちらの記事で、ドメイン駆動設計のアプローチは以下の2ステップがあるということを書きました。

  1. ドメインの問題を解決するための抽象的なモデルを作る.
  2. モデルをソフトウェア(コード)に落とし込む

※ ドメイン=ソフトウェアを適用して問題解決しようとする領域

DDDでは、このStep2の モデルをコードで表現するためのパターン として、以下の4つを定義しています。

  1. エンティティ
  2. 値オブジェクト
  3. ドメインイベント
  4. ドメインサービス

DDD Refference より一部抜粋 "Express Model With"と書かれている4つ

このうち、 ドメインの知識を「モノ」として表現する のがエンティティと値オブジェクトの2つです。使用頻度が高く理解しやすいため、初学者はまずこの2つから学ぶとよいでしょう。本記事では、エンティティと値オブジェクトについて詳しく解説します。

ドメインサービスとドメインイベント

今回の趣旨からは一瞬それますが、残りの二つを軽くだけ説明します。

ドメインサービスは、ドメインの知識を「手続きやプロセス」として表現するものです。 「モノ」「コト」として表現すると無理があるものの表現に使います。例えば、集合に対する操作などです。

例として、「予約」というオブジェクト自体があったとします。
"指定された特定の時間帯に予約の空きがあるか?"と尋ねられたとき、その知識を予約オブジェクト自身が答えられる、と表現するのは無理があります。自分自身の予約時間を知っていても、それ以外のオブジェクトの状況については情報として持っていないからです。
こういう場合には、「時間の重複をなく予約を生成するという手続き」をドメインサービスというものを使用して表現します。

ただ、ドメインサービスには注意が2点あります。

まず、ドメインサービスはつい手続き的な書き方になってしまうということです。極力エンティティと値オブジェクトで表現できないかを検討して、どうしてもできない時のみ使うようにします。
もう一つは、使うとしても「サービス」という名称はお勧めしないということです。「サービス」と名付けられたクラスは責務が曖昧になり、肥大化していきがちです。「それは何をするクラスなのか?」と責務を明確にし「XxxRegisterer」「XxxApprover」などと責務を表現した名前にすることをお勧めします。


ドメインイベントは、ドメインの知識を「コト」として表現するものです。「予約」をエンティティ/値オブジェクトとするなら、「予約が行われた」と言った形で表現されるのがドメインイベントです。

このモデルを別のドメインサービスなどで拾って別の処理を行うといった使い方をします。
ただ、残りの3つに比べるとかなり応用編といったところなので、初学者は一旦は置いておいて後からの理解で大丈夫です。


それでは、ドメインイベントとドメインサービスについては軽く抑えたところで、エンティティと値オブジェクトの紹介に移りたいと思います。

エンティティと値オブジェクトの違い

同じ「ドメイン知識を『モノ』として表現するもの」であるのに、なぜ2つあるのでしょうか?その違いさえ理解できれば、使い分けることができるようになるでしょう。

エンティティ 値オブジェクト
同一性判定 識別子が同一であれば同一 保持する属性が全て同一であれば同一
可変性 可変でもよい 必ず不変

定義としては、同一性の判定方法がどちらか、というものになりますが、可変性も性質として重要なので併記しています。

エンティティ

エンティティの同一性判定と可変性

社員というエンティティについて考えます。

例えば、山田さんという社員は、ある会社においては社員番号123という識別子で同一判定されます。山田さんは部署が変わろうが、所持金が変わろうが、体重が変わろうが同じ「山田さん」であり、別人にはなりませんね。

一方、新しく名前が同じ山田さんという社員が入ってきて社員番号456が割り振られたとします。この人は部署、所持金、体重が仮に全部同じだったとしても、123の山田さんとは別の人物です。これがエンティティの同一性の考え方となります。

値オブジェクト

値オブジェクトの同一性判定

一方、お金について考えます。

2つの10円玉が並んでいて、これを「同じ」と判断したいでしょうか?

それは 文脈、モデリングの目的による ものとなります。
モデリング時の興味が、そのものが表す金銭的価値にしか興味がないとすると、2つの10円玉は同じと考えてよいことになります。
例えば、山田さんが1つ目の10円玉を持っている状況と2つ目の10円玉を持っている状況では、等しく山田さんのの所持金は10円と考えたいのではないでしょうか。この場合、二つをの10円を区別する必要はありません。このような場合、10円は値オブジェクトとしてモデリングする方が適切です。

もしこれが造幣局やコインコレクターだとするならば事情は異なるかもしれません。それぞれの10円玉を区別して扱いたい可能性はあり得ます。これが先ほど書いた「文脈、モデリングの目的による」という意味です。
その場合はお金をエンティティとしてモデリングするということも検討することになります。

値オブジェクトの不変性

山田さんが所持金として10円を持っていたところ、100円に増えたとします。この時に10円玉の数字の10に取り消し線を書いて、100と書き直すでしょうか?いや、そんなことはしませんね。

実際には、10円玉(という値オブジェクト)を、100円玉(という値オブジェクト)と交換するでしょう。10円玉は製造された時に保持する金銭的価値は確定しており、あとからいかなることがあっても変わることはない。これが値オブジェクトが不変であるということです。

エンティティと値オブジェクトの関係

先ほどの社員とお金のように、エンティティが自身の属性として値オブジェクトを保持するという関係になるのが基本となります。
エンティティの属性は可変ですが、値オブジェクトとして持つ属性の値が変わる場合は、値オブジェクト自体が示す値を変えるのではなく、新しい値を持つ値オブジェクトを生成し、前の値オブジェクトを置き換えるという使い方します。10円玉を100円玉で置き換えるというのは、まさにこの事例です。

このような設計をする意図

エンティティと値オブジェクトを区別する利点

なぜこのような区別をするのでしょうか? これは端的に実装上の利点があるからです。 これは私の実体験からの解釈ですが、以下の2つが挙げられると考えています。

① ドメインの知識をそのまま表現しやすくなる
② 可変性を最小限に抑えられる

この2つを実現することで、保守性を向上できます。

先ほどの例で、値をオブジェクトをエンティティとして表現すると、「10円玉自体が硬貨IDを保持し、数値部分を書き換えられる」という実装になってしまいます。これは直感的に現実世界(ドメイン)の知識の表現からは乖離してしまいますね。今回の硬貨のようなものは、「識別子ではなく属性で判断する値」とすると表現しやすい、というパターンだと理解してもらえると良いと思います。

また、可変性を最小限に、というのは、最近は多くのプログラミング言語にも取り入れられている方針であり、一般的なプログラミング言語のプラクティスであるため、ここではあまり掘り下げません。

プリミティブ型ではなく値オブジェクトとして設計する利点

値オブジェクトについて、エンティティとの対比として利点を書きましたが、プリミティブ型と対比すると、きちんと値自体に振る舞いを持たせることで凝集度を上げることが可能になります。

例えば、社員というエンティティがメールアドレスを持つ場合、メールアドレスの書式チェックを社員エンティティが持つよりも、メールアドレスという値オブジェクトの生成時のロジック(コンストラクタ等)に書式チェックを持たせる方が、オブジェクトの持つ値と振る舞いの関連度が近いので、凝集度が高いということができます。

もっと詳しく知りたい方は

DDDを学び、実践したい方のために、2冊の書籍を執筆しました。

①基礎的な概念や考え方を学びたい方は

little-hands.booth.pm

初めてDDDを学ぶ方、もしくは実際に着手して難しさにぶつかっている方向けの書籍です。

DDDの目的やモデリングの考え方から始まり、具体的な実装パターンまで幅広く解説しています。特に「第6章 ドメイン層の実装」では、本記事で触れなかったドメインサービス、ドメインイベント、リポジトリ、ファクトリーなどの重要な実装パターンについて解説しています。
基礎的な概念や用語を理解する際にお役に立ちます。

②実際のコードを見ながら実践したい方は

little-hands.booth.pm

実践にあたって頻出の疑問に対して、トピックごとに詳しく解説した書籍です。

重要トピック「モデリング」「集約」「テスト」について詳細に解説し、その他のトピックでは頻出の質問への回答と具体的なサンプルコードをふんだんに盛り込みました。現場で実践して、困っていることがある方はぜひこちらもご覧ください。

この本の「第3章 エンティティ/値オブジェクト」では、「データモデルやOR マッパークラスとの違い」「エンティティの生成方法」「DB のオートインクリメントの値をID に使用してよい?」と言った具体的な疑問に詳細なコード付きで解説しています。

「モデリング/実装ガイド」で基礎概念を理解した後は、こちらの書籍を読んでいただけると実際のコードを見ながら実践するのに役立ちます。

現場での導入で困ったら

DDDを導入しようとすると結構試行錯誤に時間がかかります。
現場で導入してすぐに効果を発揮したい!!という方向けに、基礎解説とライブモデリング/コーディングを行う勉強会の開催や、設計相談を受付けております。事例紹介もあるのでご関心あれば覗いてみてください。
開催形式は柔軟に対応できるのでお気軽にご相談ください。

little-hand-s.notion.site

Twitterでも、DDDに関して発信したり、「質問箱」というサービスを通じて質問を受け付けています。こちらもよろしければフォローしてください。

https://twitter.com/little_hand_s

また、YouTubeで10分でわかるDDD動画シリーズをアップしています。概要を動画で理解したい方はこちらもどうぞ。チャンネル登録すると新しい動画の通知を受け取ることができます。