私は最近、ウラジミールホリコフによるDDDのいくつかのPluralshighコースを見ました。彼はanemicドメインモデルの代わりにrichを作成することを奨励していました。小さなテストプロジェクトではすべてとても素敵に見えましたが、extensiveビジネスロジックをrich domain modelの中にどのように配置するかまだわかりません。
リッチドメインモデルでは、ドメインロジックをエンティティに組み込むことになっています。したがって、給与をEmployer
に支払うEmployees
をモデル化するとします。ここでは、Employerを集約ルートとして見ています。
そこで、Employer.PayTo(someEmployeeIdentificator)
メソッドを追加します。給与を計算するためのビジネスルールは非常に複雑で、次のような要素に依存する場合があります。
潜在的に、数十のアルゴリズムが存在する可能性があります。外部サービスとの通信が必要な場合もあります。戦略パターンの完璧なケースですが、
Employees
はEmployer
集約ルート内に隠されていますIOC
を使用します(これらは通常、いくつかのORMによって作成されます)。そして、依存関係ツリーを提供するのは楽しいことではありません。Employer
エンティティに注入することは、電卓が 'pure'。一部の外部リソース(課題トラッカーなど)への参照がある場合があります。どのようにモデル化しますか?
ロジックはエンティティー内に実装されることになっていた
はい、通常はそれよりも具体的ですが、エンティティの新しい状態を計算するロジックは、エンティティの「内部」にあるはずです。
従業員はEmployer集計ルートの中に隠されています
これは、この種の問題に使用するには効果的なモデルではない可能性があります。人々よりも取引(お金の交換)や元帳に焦点を合わせる方が理にかなっているかもしれません。
集計はデジタルであることを覚えておくと役に立ちます。それらは、物そのものではなく、何かを説明するドキュメント(の一部)です。
IOCを使用してエンティティにデータを注入することはできません
制御の反転は、「実際には引数をとると言っても大げさな方法です。」通常、エンティティにアイテムを挿入することはありません。代わりに、エンティティに引数としてデータを渡します(ドメインサービス)。
SalaryCalculatorのいくつかの実装をEmployerエンティティに注入することは、電卓が「純粋」でない可能性があるため、悪い考えかもしれません。一部の外部リソース(課題トラッカーなど)への参照がある場合があります。
ドメインモデルの初期の議論(ブルーブックのエヴァンス、エンタープライズアプリケーションアーキテクチャのパターンにおけるファウラー)では、純粋である必要はありません。副作用が予想される他のオブジェクトを呼び出すオブジェクトです。
私が言えることですが、実際には2つのオプションがあります。オブジェクト間のオーケストレーションをドメインモデルの一部にするか、モデル自体を単一のコラボレーターとして扱い、オーケストレーションを他の場所で管理することができます。
どのようにモデル化しますか?
"場合によります"。私はおそらく異なるプロセスを分離することから始めます
さらに、微妙な違いを正しくモデル化することに細心の注意を払ってください。給与報酬、時間給、および委託販売がドメインに存在する場合、それらはモデルで個別に識別できる必要があります。 「ほぼ同じ」は、希望に満ちたスペルの方法differentです。
リッチドメインモデルでは、ドメインロジックをエンティティに組み込むことになっています。
この文の問題は、人々がそれを間違って解釈することです
より良い解釈は:
ここで、「一部」は、最も意味のあるドメインロジックを意味します。どこに線を引くかを決定する「ハードで速い」ルールはありませんが、オブジェクト自体のプロパティを通じてほとんど表現できるロジックをオブジェクトに配置することから始めるのがよいでしょう。
すでに説明したように、PayTo
のような大きな演算はそのカテゴリに分類されないので、Employer
クラスに直接実装することをお勧めしますnot。つまり、貧血ドメインモデルになるということではありません。プログラムが進化すると、Employer
クラスに完全に適合する多くの小さな操作が見つかるはずです。
ここでは、Employerを集約ルートとして見ています。
OOモデリングを行う場合、通常、ビジネス関数はアクションのsubjectで定義され、アクション。
もちろん、要件によって異なりますが、おそらくEmployee.receiveSalary()
としてモデル化します。
これが「より良い」かもしれないもう1つの指標は、あなたの場合、メソッドがその作業を行うために従業員ID(基本的には別のオブジェクトへの参照)を必要とすることです。これは、メソッドが別のオブジェクトに存在することを望んでいることを示しています。
SalaryCalculatorのいくつかの実装を注入しています...
おそらくnotSalaryCalculator
という名前のオブジェクトを作成します。つまり、「ドメイン」の一部ではないの問題です。異なる「戦略」を作成したい場合でも、それをドメイン自体に組み込む方法を見つけた方がよいでしょう。
これは、Salary
の代わりにSalaryCalculator
と名前を付けるだけで簡単かもしれません。 HourlySalary
、MonthlySalary
など、何でもかまいません。それはそれに取り組むのではなく、ドメインで理にかなっています。
ロジックはエンティティー内に実装されることになっていた
はい。物事を行うのに適した場所が見つからない場合は、優れた抽象化が欠落している可能性があります。 (SalaryCalculator
の例を参照)。正しく行うと、notにはサービスや計算機など、ドメインに含まれていないものが必要になります。
従業員はEmployer集計ルートの中に隠されています
それが問題であるなら、それをしないでください。適切なモデリングが最初に来て、技術的なものが2番目に来ます!たぶんEmployee
は集約ルートである必要があります。または、この特定のケースのルートとして機能する3番目の抽象化があります。
IOCを使用してエンティティにデータを注入することはできません...
ここでも、まず適切なモデリング、次にテクノロジーです。テクノロジーが正しいモデルをサポートしていない場合は、別のテクノロジーを選択できます(たくさんあります)。
オブジェクトのセットはコラボレーターを定義できないと言って、オブジェクトは互いに協調しているように見えます。
ビジネスエンティティが永続エンティティと同じである必要はありません。それらの間のマッピングを作成するための任意のツールを使用できます(C#では、Automapperがあります)。
このようにして、必要な依存関係をビジネスエンティティに注入できます。その上、それはあなたのORMがそれらを必要としたからといってあなたが公開したかもしれないプロパティ/属性を隠すのを助けることができます。