ゼロ引数コンストラクターと常に有効なエンティティ


8

私は最近、Always Validドメインエンティティについて多くの読書をしました。エンティティが常に有効であることを保証するために、私は次のことを行う必要があると信じるようになりました。

1)ここで説明されているように、プリミティブな強迫観念を取り除き、値オブジェクトコンストラクターに検証/ドメインルールを配置します:https : //enterprisecraftsmanship.com/2016/09/13/validation-and-ddd/。2)ここで説明されているように、検証またはドメインルールをエンティティまたはプロパティセッターのコンストラクタに配置します:http ://gorodinski.com/blog/2012/05/19/validation-in-domain-driven-design-ddd/ 。

ただし、次に、https//github.com/gregoryyoung/mrなどのいくつかのオープンソースプロジェクトを調べます。私が理解していることから、このプロジェクトの作成者は常に有効なドメインモデルの擁護者ですが、それでもInventoryItemクラス(https://github.com/gregoryyoung/mr/blob/master/SimpleCQRS/Domain.cs)を調べます。私はこれを行うことができることに気づきました:

InventoryItem inventoryItem = new InventoryItem();

またはこれ:

InventoryItem inventoryItem2 = new InventoryItem(Guid.Empty,null);

私の考えでは、これはエンティティが無効な状態で初期化されることを意味します。これは、私が最近見た他のすべてのオープンソースプロジェクトにも当てはまるようです。たとえば、次のプロジェクトです。https//github.com/dcomartin/DDD-CQRS-ES-Example/blob/master/src/Domain /Customer.cs

これらのオープンソースプロジェクト(https://martinfowler.com/bliki/ContextualValidation.html)にコンテキスト検証があることに気づきました。また、ドメインモデルにマップする場合、ORMにはデフォルトの空のコンストラクターが必要であることも理解しています。

ドメインオブジェクトは、引数なしのコンストラクタを使用してデフォルト値で初期化されている/空/ null値で初期化されている場合、有効な状態ですか?


2
あなたは本当に一般化することはできません。それは可能性があります。そうではないかもしれません。それは重要ですか?私見、いや、特に初期のDDDの話し合いでは、現実は異なると考えるのを忘れており、たとえば、ORM /言語/フレームワークはいくつかのルールを規定しています(そして、それらを回避するための価格は高すぎるか、価値がない場合があります)。わかりました、あなたがリストしたプロジェクトは、これまでで最高の.NETコードではないようです...
Adriano Repetti

@Adriano Repetti、あなたは何か良いCQRSオープンソースプロジェクトを知っていますか?コマンドに検証を入れますか?
w0051977

2
パラメータの検証とドメインの検証を区別します(該当する場合)。たとえば、nullパラメータはドメインとは関係なく、実装の詳細(使用している言語)から取得されます。前者はいつでもどこでも関係ありません。ドメインの検証はよりトリッキーですが、IMOの場合、壊れた不変条件を本当に心配する場合は、不変オブジェクトを検討する必要があります。それらは(ここでも私の意見ですが)多くの場合、私たちが実際に使用する言語に対する聖戦よりも優れたトレードオフです(そしてそれは、多くの古いDDDテイクに対する批判でした)
Adriano Repetti

回答:


7

オブジェクトの作成に関する考慮事項について説明する前に、まず質問の背後にある動機、「常に有効なドメインエンティティ」というフレーズについて説明しましょう。このフレーズは、せいぜい誤解を招くだけで、DDDのコンテキストではほとんど意味がありません。これは、2つの関連する理由から明らかです。

1つ目は、暗黙的にフォーカスをシステムの動作から遠ざけることです。代わりに、状態の観点からのみ検証を検討するように求めます。表面的にはこれは理にかなっているように見えるかもしれませんが(もちろん検証は状態の観点からです!)、DDDの基本原理はシステムが動作に従ってモデル化されるということを覚えておく必要があります。これの動機は、コンテキストまたはビジネスプロセス自体が、状態の一部が有効かどうかを判断するときに重要な考慮事項であることが多いことです。この方法でシステムをモデリングすると、システムの複雑さを大幅に減らすことができます。

これは、そのようなシステムが必要とする実際的な要件に関して、2番目の理由をもたらします。「常に有効なドメインエンティティ」のシステムを作成するには、状態が使用されるビジネスプロセスに従って、状態のすべての順列をモデル化する必要があります。簡単な例で、この制限を説明できます。

ルール:

  • Customer 登録するには18歳以上である必要があります
  • Customer 登録の割引を受けるには、25歳未満である必要があります
  • Customer 予約するには25歳以上である必要があります

最初に気付くべきことは、これらのすべてのルール(ほぼすべてのルールと同様)がいくつかのビジネスプロセスに適用されることです。それらは孤立して存在しません。これらのルールは、customer.Register()およびで検証されcustomer.Reserve()ます。これにより、ルールがどこで実行されているかが明確になるため、はるかに記述的で宣言的なパラダイムになります。

我々はそれが「常に有効なドメインエンティティ」のシステムにつながるようにこれらのルールをモデル化したい場合は、我々は我々の分割する必要があるだろうCustomerRegistrarReserverエンティティを。そして、この例ではそれほど悪くないように思えるかもしれませんが、ルールがより複雑で豊富になると、いくつかのコンテキストまたはプロセスの「内部」の状態を表す、このような爆発クラスになります。これは単に不要であり、そのような2つのオブジェクトが同じ状態のスライスに依存している場合、必然的に問題が発生します。

さらに、のようなものCustomer c = new Customer()は、どのビジネスルールが適用されるかが明確でないため、例外をスローするのに適さない場所です。これで、最後の議論が始まります。

コンストラクターで発生するビジネスルールの検証はゼロであるべきだと私は思います。オブジェクトの構築はビジネスドメインとは何の関係もありません。そのため、コンテキストと一貫性に関する上記の理由に加えて、すべてのビジネスルールはエンティティのメソッド本体内で適用する必要があります(メソッドはビジネスプロセスにちなんで名付けられている可能性があります) ?)。

さらに、オブジェクトを「更新する」ことは、ドメインに新しいエンティティを作成することと同じではありません。オブジェクトはどこからともなく出てくるわけではありません。新しいエンティティをシステムに取り込む方法に関するビジネスルールがある場合は、ドメインでモデル化する必要があります。本当のマスターhttp://udidahan.com/2009/06/29/dont-create-aggregate-roots/によるこのトピックに関するいくつかのさらなる議論はここにあります


3

ドメインオブジェクトは、引数なしのコンストラクタを使用してデフォルト値で初期化されている/空/ null値で初期化されている場合、有効な状態ですか?

かもね。

デフォルトのコンストラクターを使用して有効なドメインオブジェクトを作成することについての規則に反するものはありません。

空の値を持つ有効なドメインオブジェクトの作成に関する規則に反することはありません。

オプションの要素が欠けている有効なドメインオブジェクトの作成に関する規則に反することはありません。

nullを使用して有効なドメインオブジェクトを作成することに関する規則に反することはありません。

問題が発生する場所:独自のセマンティック代数に従わないドメインオブジェクトの作成。

正しい実装は、セマンティクスの正しい解釈に依存します。

ドメインコンセプトの寛容な表現を採用し、段階的に追加の制約を適用することは通常のことです。これは、型の追加を容易にする実装言語(例:F#)がJavaのような扱いにくい言語よりも優れている領域の1つです。

たとえば、数値を重視するドメインがあり、そのドメイン内に素数またはメルセンヌ素数に固有のいくつかの機能がある場合、使用する自然なメカニズムは3つの異なるタイプを作成することです。ソリューションのさまざまな部分の入力に適用される制約のさまざまなセットがあるという概念を明示します。

Number n = new Number(...)
Optional<Prime> p = n.prime()
if (p.isPresent()) {
    Optional<MersennePrime> mp = p.mersennePrime()
    // ...
} 

この種の設計では、数値が本当に素数であるという「検証」は、素数コンストラクター自体の中に存在します。パラメータがメルセンヌ素数であるという追加の制約が必要な状況では、Prime-> MersennePrime変換を使用して、メルセンヌ制約の知識に1つの権限があることを確認します(「自分を繰り返さないでください」)。

「暗黙的、明示的にする」


ありがとう。意味代数とは何か例を使って説明できますか?コードサンプルがどのように関連しているかについても説明してください。+1「反対するものは何もない...」。
w0051977

確かに空のIDのないエンティティは悪いことですか?ドメインクラスをデフォルト値で初期化してから段階的に構築するシナリオがない限り、
w0051977
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.