読者です 読者をやめる 読者になる 読者になる

かとじゅんの技術日誌

技術の話をするところ

DDDのバリューオブジェクトは不変性が本質ではない

id:ryoasaiさんから紹介してもらったエントリで「バリューオブジェクトは不変性が本質ではない」ということを知って、「あれ?」っと思ったので、もう一度DDD本を読んで簡単にまとめてみました。

不変性は本質ではない

DDD P97-98より引用

The system has to cope with all that tracking, and many possible performance optimizations are ruled out. Analytical effort is required to define meaningful identities and work out foolproof ways to track objects across distributed systems or in database storage.

バリューオブジェクトがなんで必要かというと、このあたり。
エンティティの同一性を使うとシステムでは追跡を行うわけですね。そういった追跡すべてに対処するためのコストが必要で、それは性能面では最適化が不可能になる。分散システムやDBが関係すると分析のコストもかかると。人工的な同一性もモデルをダメにすると。ということで、なんでもかんでもエンティティじゃないよと。

DDD P99より引用

We don't care which instance we have of a VALUE OBJECT. This lack of constraints gives us design freedom we can use to simplify the design or optimize performance. This involves making choices about copying, sharing, and immutability.

どのインスタンスか 気にしない(識別しない)ことで、設計が自由でシンプル、性能面を最適化できると。複製と共有、不変性はそのための選択肢。そして、不変というのは、あくまで設計戦略のひとつだという印象。

DDD P101より引用

Immutability of an attribute or an object can be declared in some languages and environments but not in others. Such features help communicate the design decision, but they are not essential.

ここでも、バリューオブジェクトの不変性は、"本質的ではない"とはっきり書かれていますね。つまり、バリューオブジェクト=不変オブジェクトとは限らないということ。これちょっと見落とした。。

id:koichikさんのコメントから

DDD においてより重要なのは値オブジェクトのセマンティクスではなく,いつでもどこでも頻繁に作成しては渡すことが出来るという使われ方で,PofEAA の値オブジェクトはたまたまそれに適しているということのように思われます.

原文から考えると納得です。

DTOとしてのバリューオブジェクト

DDD P99より引用

VALUE OBJECTS are often passed as parameters in messages between objects. They are frequently transient, created for an operation and then discarded.

PofEAAでは、「メッセージ呼び出しの数を削減するため、プロセス問のテータを伝送するオブジェクト」をDTOと呼んでいますが、この文脈では、まさに「PofEAAのDTOのような扱い」をすると言ってますね。DTOとは明確に書いてませんが、DDD P102「Tuning a Database with VALUE OBJECTS」には、DTOとしか解釈できない例が示されている。
ほぼ間違いないということですが、完全に含まれるとはいえない可能性もあるってことかな。

DDDのバリューオブジェクトの関係を、id:koichikさんが述べています。

自分は aufheben さんのところのコメントで,DDD の ValueObject(3) は DTO を「包含している」とか「許容している」,DDD 読書会の担当者は wiki で「混じっている」と表現しています.

つまり、DDDのバリューオブジェクトには、PofEAAのDTOは「包含」もしくは「許容」という関係。個人的には「混じっている」が好きです。

DDDとPofEAAのバリューオブジェクトの関係

また、PofEAAでは、このDTO以外に「IDに基づいた等価性を確保していない、MoneyやDateRangeなどのシンプルな小型オブジェクト」という説明があり、それをバリューオブジェクトと呼んでいます。名前がややこしいですねw PofEAA本を見る限り、不変の値を扱うオブジェクトと見受けられます。

で、またもやあの方のコメント。

PofEAA は値のセマンティクスを扱うパターン == Value Object(2) と分散システムにおける効率を扱うパターン == DTO を明確に区別していますが (すべての Value Object(2) は DTO として利用できるので排他的ではない),DDD はそうではなく,両方を一つのパターン == Value Object(3) で扱っているわけです.

PofEAAのバリューオブジェクトには、PofEAAのDTOが含まれるという解釈をされていますね*1
そして、DDDでは区別せず、その両方をひとつのパターンとして扱っているいうことか。

それをまとめると以下。

これらは,DDD の ValueObject(3) の大部分が PofEAA の ValueObject(2) であることを否定しているわけではありません.つまり,

・DDD の ValueObject(3) ⊃ PofEAA の ValueObject(2)
かつ
・DDD の ValueObject(3) ⊃ PofEAA の DTO

ということを言っています.なお,PofEAA においては

DTO ⊃ VlaueObject(2)

です.
全ての ValueObject(2) は DTO としても利用できます (Fowler は可変の DTO を好むと書いてるけれど) が,全ての DTO が ValueObject(2) ではありません.DDD の ValueObject(3) とは包含関係が逆になっていることに注意.

若干、分かりにくいと思うので、図にしてみました。

個人的な感想は、やはりDDDとPofEAAで共に出てくる概念に関連性を求めてしまうと混乱する可能性があるなと思いました。似てるところが多いとは言え、完全に同じかというとそれはわからないですね。そこはFowlerとEvansを呼んで概念の関連性を確認しないと、どうにもはっきりしない領域です。

ともあれ、PofEAAのことはさておき、DDDのバリューオブジェクトは、可変も不変もあるので、複製と共有もありと解釈した。大変参考になるエントリでした。気づきを得ることができました。先人に敬意を払いたいですね。

可変と不変の使い分けはどうするのか

もうちっと自分なり考えを出してみる。
DDD P101より引用 右の端にあるやつ

Special Cases:When to Allow Mutability

  • If the VALUE changes frequently
  • If the object creation or deletion is expensive
  • If replacement(rather then modification) will disturb clustering(as discussed in the previous example)
  • If there is not much sharing of VALUES, of if such sharing is forgone to inprove clustering or for some other technical reson

簡単にまとめると、値の更新が頻繁な時、オブジェクトの生成や破棄のコストが高い時、インスタンスの置き換えでクラスタリングに問題が出る場合、値を共有しない場合(クラスタリングの改良、その他 技術的な理由の場合も含む)は、可変を許してもよいと。

特別なケースと言っているので、通常は「不変で共有する」設計戦略だと思いますが、場合によっては可変もあり得るということを認識しておくべきだと思いました。実際の現場でも、バリューオブジェクトは「不変で共有する」としているのですが、さすがに性能面でボトルネックが出る場合や、クラスタリングで問題が起きる場合は「可変で共有しない」とするのがよいのでしょうね。

あわせて読みたい
Value と Entity - 感想 - Aufheben - GLAD!! の日記
2009-05-29 - yyamanoの日記

*1:ここについてはPofEAAの学習不足もあってなぜそう解釈するのかが完全に理解できませんでした。