DDDおよび値オブジェクト。変更可能な値オブジェクトは非Aggrの良い候補です。ルートエンティティ?


9

ここで少し問題があります

値オブジェクトを持つエンティティーがあります。問題ない。新しい値の値オブジェクトを置き換え、nhibernateが新しい値を挿入し、古い値を孤立させてから削除します。わかりました、それは問題です。

被保険者は私のドメイン内のエンティティです。彼はアドレス(値オブジェクト)のコレクションを持っています。アドレスの1つはMailingAddressです。郵送先住所を更新したい場合、たとえば郵便番号が間違っていたとしましょう。エヴァンス氏の教えに従い、古いオブジェクトは不変なので、新しいオブジェクトに置き換える必要があります(値オブジェクトは正しいですか?)。

ただし、そのアドレスのPKはMailingHistoryテーブルのFKであるため、行を削除したくありません。したがって、エヴァンス氏の教義に従って、私たちはここでかなりねじ込まれています。私がエンティティをアドレス指定しない限り、それを「置き換える」必要はなく、単に古き良き時代のようにその郵便番号のメンバーを更新するだけです。

この場合、私に何を提案しますか?私の見たところ、ValueObjectsは、データベーステーブルの列(nhibernateのコンポーネント)のグループをカプセル化する場合にのみ役立ちます。データベースに永続IDがあるものはすべて、エンティティ(必ずしも集約ルートではない)にすることをお勧めします。これにより、特に深くネストされたオブジェクトの場合は、オブジェクトグラフ全体を再作成せずにメンバーを更新できます。

同意しますか?エバンス氏は可変値オブジェクトを持つことを許可されていますか?または、可変値オブジェクトはエンティティの候補ですか?

ありがとう


2
「可変値オブジェクト」などはありますか?常に印象値オブジェクトは不変でした。
herby

@herbyコードでDDD値オブジェクトを表す可変オブジェクトがあると思いますが、オブジェクトを変更すると、同じ論理DDD値オブジェクトを参照するのではなく、新しいDDD値オブジェクトを参照することになると考える必要があります。これ望ましいかもしれませんが、混乱の元になっています。コードで値オブジェクトを不変にすることは賢明な慣例です。
MattDavey

回答:


8

アイデンティティを持つものはすべてエンティティである必要があり、アイデンティティを持たないものはすべて単純な値、つまり値オブジェクトです。

マーティンファウラー(エリックエヴァンスを順に引用)を引用するには

  • エンティティ:時間と異なる表現を介して実行される明確なアイデンティティを持つオブジェクト。これらは「参照オブジェクト」と呼ばれます。
  • 値オブジェクト:重要なオブジェクトは、属性の組み合わせのみを持っています。

住所を値オブジェクトにする理由:

あなたのアドレスが変更可能である場合、あなたはおそらく最終的にあなたの郵送履歴を台無しにするでしょう。たとえば、商品を顧客に発送する場合、MailingHistoryテーブルが参照しているアドレスが変更されていると、過去に実際に何を発送したのかがわかりません。

MailingHistoryのエントリは、A764を657に発送したということは、昨日ボストンにA764 を発送し、明日ニューヨークにA764を発送したことを意味します

メールアドレスを変更する必要がある場合は、古いアドレスを削除する必要はありません。それを保持し、非アクティブとしてマークし、新しいものをアクティブとしてマークます。


もちろん、住所をエンティティとして扱うこともできますが、更新した場合にのみ、住所が参照している実際の場所は変更されないため、タイプミスの修正のみが可能になります。

あなたがそれを確実にすることができると確信しているならば、エンティティを使用することは可能でしょう。


しかし、IMHOの最善の解決策は、メーリング履歴のアドレスエンティティを参照せず、特定のアドレスをメーリング履歴テーブルに直接保存することです(基本的にはアドレスのデータをコピーします)。

このようにして、あなたは常にあなたが自分のもの(またはあなたが郵送しているもの)をどこに出荷したかを知っており、変更可能なエンティティを使用するので、あなたのアドレステーブルは乱雑になりません。

私はいくつかのERPシステムを使って作業したことがありますが、それらのほとんどすべてがこのアプローチを使用しています。

データベースには冗長性がありますが、これが私見の最も実用的な方法です。


これはおそらく最も頭痛のないソリューションです。将来の通信チャネルで追加の列が必要になると予想され、データベースが大きすぎるALTER場合は、別のテーブルでエンティティを使用する必要がある場合があります。そのため、クエリでは「常に最新のアドレス/電話/電子メールに参加する」などの戦略が求められますがSELECT、これは保守性効率性を維持するのが困難です。可能な場合は、シンプルにしてください。
Timo

@Timoは、単にactive-flagを追加するだけでデータを少し非正規化すれば、「常に最新のアドレス/電話/電子メールに参加する」ことは難しくありません。もちろんand active = true、Joinで常に使用し、フラグを最新の状態に保ち、テーブルに制約を追加して、たとえばこのフラグをtrueに設定できるのは、顧客ごとに1つの電子メールだけにできるようにする必要があります。
ナマケモノ2018年

これにより、以前のものを非アクティブ化する問題が発生します。コードでインスタンスの「現在の」Addressオブジェクトを置き換えた場合、データアクセスコードにアクセスすると、(1)新しいものがあるかどうか、(2)潜在的に古いものはわかりません。ひとつは。そのため、すべての保存操作では、「データベース内の関連するすべてのアドレスを最初に非アクティブ化して無効にする」などの複雑な処理を実行してから、で現在のアドレスを保存する必要がありactive=trueます。これは私がシンプルと呼ぶものではありません。それがまさに私があなたのソリューションを気に入っている理由です。
Timo

2

2つのことがわかります。

  1. 郵便番号の変更が履歴レコードに影響を与えることは問題ありませんか?履歴レコードが古い変更されていないアドレスをポイントすることは論理的であると思うので、間違ったアドレスに送信したことがわかります。

  2. MailingHistoryがアドレスにFKを持つ瞬間、そのアドレスは値オブジェクトでなくなり、エンティティーになりました。値オブジェクトにはIDがないため、他のエンティティがこのIDを参照できます。単一のテーブルにアドレスを設定し、他のテーブルがそれをポイントするようにできますが、効果はスペースの節約です。ドメインの観点から見ると、2つのエンティティが同じタイプの値オブジェクトを参照している場合、それらはいかなる種類の情報も共有しません。


2

IMOアドレスオブジェクトはドメイン内のエンティティです。複数のエンティティによって共有され、独自のIDを持ち、システム全体で一意です。

エバンスさんのコメント:

主にアイデンティティによって定義されるオブジェクトは、エンティティと呼ばれます。


ドメインIDは、私の理解では、永続性IDとは何の関係もありません。エヴァン氏の本によると。
Pepito Fernandez

あなたが正しいです。回答を編集します。つまり、オブジェクトアドレスはこの特定のドメインでは重要であり、一意です。IMOの外部キーと主キーは、実際にはドメイン全体で一意のオブジェクトであるため、IDを持っていることを示しています。
margabit

1
「アドレスオブジェクト...独自のIDがあります -アドレスのどの属性がそれを一意に識別しますか?アドレスの単一の属性は一意ではありませんが、属性の組み合わせはIDとして機能します。これがまさに値オブジェクトの
MattDavey

@MattDavey:それは良い結論ですが、トニーが「そのアドレスのPKはMailingHistoryテーブルのFKであるため、行を削除したくない」と言ったときに混乱します。これは、私にとって、アドレスオブジェクトは、集計された「被保険者」以外にも意味があることを意味します。これは、「Address」オブジェクトがValueObjectであってはならないことを示しています。どう思いますか?
margabit

値オブジェクトは、常に親によって完全に所有される構成(UML)であると言えるでしょうか。さらに、値オブジェクトはその親なしでは意味がなく、親の間で共有できませんか?
Sudarshan
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.