これらのすべてのサービスで、どうすれば貧血にならないのですか?


90

ビジネスロジックの委任とカプセル化の境界はどこにありますか?委任すればするほど、貧血になります。ただし、委任は再利用とDRYプリンシパルも促進します。それでは、委任するのに適切なものと、ドメインモデルに残すべきものは何でしょうか?

例として、次の懸念事項を取り上げます。

認証。ドメインオブジェクトは、そのアクセス制御ルール(CanEditプロパティなど)を維持する必要がありますか、それともアクセスの管理のみを行う別のコンポーネント/サービス(IAuthorizationService.CanEdit(object)など)に委任する必要がありますか?それとも、2つの組み合わせにする必要がありますか?おそらく、ドメインオブジェクトには、実際の作業を実行するために内部IAuthorizationServiceに委任するCanEditプロパティがありますか?

検証。上記と同じ議論は検証に関するものです。誰がルールを維持し、誰がルールを評価する責任がありますか?一方では、オブジェクトの状態はそのオブジェクトに属している必要があり、有効性は状態ですが、すべてのドメインオブジェクトのルールを評価するために使用されるコードを書き直したくありません。我々は可能性があり、この場合の継承を使用して...

オブジェクトの作成。ファクトリクラスとファクトリメソッド、インスタンスの「更新」。別のファクトリクラスを使用する場合、作成ロジックを分離してカプセル化できますが、オブジェクトの状態をファクトリに開くことを犠牲にします。これは、ファクトリが使用する内部コンストラクターを公開することでドメインレイヤーが別のアセンブリにある場合に管理できますが、複数の作成パターンがある場合は問題になります。そして、すべてのファクトリーが適切なコンストラクターを呼び出している場合、ファクトリーを持つことのポイントは何ですか?

クラスのファクトリメソッドは、オブジェクトの内部状態を開く問題を排除しますが、静的であるため、別のファクトリクラスの場合のように、ファクトリインターフェイスの注入によって依存関係を解除することはできません。

永続性。ドメインオブジェクトがCanEditを公開し、承認チェックを実行する責任を別の関係者(IAuthorizationService)に委任する場合、同じことを行うドメインオブジェクトにSaveメソッドがないのはなぜでしょうか。これにより、オブジェクトの内部状態を評価して、カプセル化を中断せずに操作を実行できるかどうかを判断できます。もちろん、リポジトリオブジェクトをドメインオブジェクトにインジェクトする必要がありますが、これは少し気になりますが、代わりにドメインイベントを発生させ、ハンドラーが永続化操作を実行できるようにしますか?

これでどこに行くのかわかりますか?

Rockford Lhotkaは、CSLAフレームワークのクラスインチャージルートに進む理由について素晴らしい議論をしており、そのフレームワークには少し歴史があり、ドメインオブジェクトと並行するビジネスオブジェクトの彼のアイデアをさまざまな方法で見ることができます。しかし、良いDDDの理想にもっと忠実になろうとしているので、コラボレーションが過剰になりすぎるのではないかと思っています。

集約ルートのIAuthorizationService、IValidator、IFactory、およびIRepositoryで終わる場合、何が残っていますか?オブジェクトの状態をDraftからPublishedに変更するPublishメソッドを使用して、クラスを非貧血ドメインオブジェクトと見なすのに十分ですか?

あなたの考え?


いい質問ですね。多くの異なるコンテキストまたは異なるアプリケーションとの間でサービスを使用/消費/公開するのとまったく同じ理由で、ほとんど常にデザインが完全に貧弱なため、答えはありません。
hromanko

すばらしい質問です。アンクレボブ、マーティンフォーラー、エリチェバンスらがこれについて話し合うのを楽しみにしています。今、私は離れて行くと、長いと思う持っているために
マルタインVerburg

私は常に貧血モデルに進化しています。そして、私はこれとまったく同じことに苦労しています。いい質問です!
L-Four

これも私のDDDでの経験です。私は職場でそれをやっており、常に貧血に陥っています。私は以前(そして今でも実際に)Cslaを使用しています。私たちの建築家は、私たちが貧血であることを嫌いますが、あなたが指摘するすべてのことをオブジェクト内で行うことができない場合、オブジェクトが何をすべきかについて良い答えを私に与えることができませんでした。結局のところ、純粋なDDDになろうとすることは、その価値以上の仕事を生み出しているようです。私は個人的には、CDDとDDDはうまくいくと思います(プリンシパルでは同じように見えます)。
アンディ

以下は、行動(非データ中心)の観点からドメインをモデリングするときに使用されるいくつかのテクニックの例です。medium.com
@wrong.about

回答:


66

混乱のほとんどは、ドメインモデルには存在しないはずの機能に関するものと思われます。

  • 永続性をドメインモデルに含めることはできません。決して。これIRepositoryが、モデルの一部がモデルの別の部分を取得するような何かをする必要があり、依存性注入または同様の手法を使用して実装を結び付ける必要がある場合など、抽象型に依存する理由です。記録からそれを打つ。

  • セキュリティソフトウェアを作成している場合など、実際にドメインの一部でない限り、承認通常、ドメインモデルの一部ではありません。アプリケーションで何を実行できるかという仕組みは、通常、ビジネス/ドメイン層の「エッジ」で処理されます。UIおよび統合部分が実際に通信できるパブリック部分-MVCのコントローラー、サービスまたは、SOAのメッセージングシステム自体...

  • ファクトリー(そしてここで抽象的なファクトリーを意味すると思います)は、ドメインモデルに含めるのは必ずしも悪いことではありませんが、ほとんどの場合不要です。通常、オブジェクト作成の内部メカニズムが変更される可能性がある場合にのみファクトリーがあります。ただし、ドメインモデルの実装は1つしかありません。つまり、常に同じコンストラクターや他の初期化コードを呼び出すファクトリーは1種類しかありません。

    必要に応じて「コンビニエンス」ファクトリーを作成できます-コンストラクターパラメーターなどの一般的な組み合わせをカプセル化するクラス-しかし、正直なところ、ドメインモデルに多くのファクトリーが存在する場合、行を無駄にしていますコードの。

そのため、これらすべてをひとまとめにすれば、検証は終了します。それは一種のトリッキーな唯一のものです。

検証 ドメインモデルの一部ですが、アプリケーションの他のすべてのコンポーネントの一部でもあります。UIとデータベースには、同様でありながら異なる概念モデルに基づいて、独自の類似するが異なる検証ルールがあります。オブジェクトにValidateメソッドが必要かどうかは実際には指定されていませんが、オブジェクトがある場合でも、通常はそれをバリデータクラスに委任します(インターフェイスではなく、検証はドメインモデルで抽象的ではなく、基本です)。

バリデータはまだ技術的にはモデルの一部であることに注意してください。データや状態が含まれていないため、集約ルートにアタッチする必要はありません。ドメインモデルは概念的なものであり、通常はアセンブリまたはアセンブリのコレクションに物理的に変換されます。委任コードがオブジェクトモデルの非常に近くにある場合は、「貧弱な」問題を強調しないでください。まだカウントされます。

このすべてが本当に得られるのは、DDDを実行する場合、ドメインとは何かを理解する必要があるということです。永続性や承認などについてまだ話している場合は、間違った方向に進んでいます。ドメインは、システムの実行状態(物理的および概念的なオブジェクトと属性)を表します。オブジェクトおよび関係自体に直接関連しないものは、ドメインモデルの期間に属しません。

経験則として、ドメインモデルに何かが属するかどうかを検討するときは、次の質問を自問してください。

「純粋に技術的な理由でこの機能を変更できますか?」 言い換えれば、実際のビジネスやドメインに目に見える変化があったためではないのですか?

答えが「はい」の場合、ドメインモデルに属していません。ドメインの一部ではありません。

いつか、永続性と承認のインフラストラクチャを変更する可能性が非常に高いです。したがって、これらはドメインの一部ではなく、アプリケーションの一部です。これは、並べ替えや検索などのアルゴリズムにも適用されます。ドメインは検索の抽象概念にのみ関係し、その仕組みには関係がないため、ドメインモデルにバイナリ検索コードの実装を押し込んではいけません。

関係のないものをすべて削除した後、ドメインモデルが本当に貧弱であることがわかった場合、それはDDDが単にプロジェクトの間違ったパラダイムであることのかなり良い指標として役立つはずです。

いくつかのドメインは本当にある貧血。ソーシャルブックマークアプリには、実際にはあまり「ドメイン」がありません。すべてのオブジェクトは基本的に機能のない単なるデータです。一方、販売およびCRMシステムにはかなり重いドメインがあります。あなたがアップロードしたときにRate実体を、あなたが実際にできる合理的な期待があるものを行うなど、注文数量に適用し、それはボリュームディスカウントやプロモーションコードとすべてが楽しいものを把握していて、その速度では。

単にデータを保持するドメインオブジェクトは、通常はないあなたは貧血ドメインモデルを持っていますが、それはしないという意味では、必ずしもそれだけで、ドメイン自体が貧血であることを意味するかもしれない、あなたが使用してする必要があること-あなたが悪いのデザインを作成したことを意味します異なる方法論。


2
さらに、@ SonOfPirate、セキュリティモデル全体をいつか変更したいという可能性はまったくありません。役割ベースのセキュリティは、主張ベースまたは権利ベースのセキュリティを優先して廃止されることが多く、フィールドレベルのセキュリティが必要になる場合もあります。これが起こった場合、ドメインモデル全体を作り直そうとすることを想像してください。
アーロンノート

1
@SonOfPirate:古い「ビジネス層」モデルにまだ少し立ち往生しているように聞こえます。それはドメイン層ではありません。ドメインは他のすべて依存するものであり、システムが管理することを意図されている現実のオブジェクトと関係を表します。
アーロンノート

1
@ LaurentBourgault-Roy:すみません、信じられません。すべての企業は、すべてのアプリケーションについてそれを言うことができます。結局のところ、データベースの変更難しいです。それはあなたのドメインの一部にはなりませんし、永続化レイヤーに結合されたビジネスロジックは、不十分な抽象化を意味します。ドメインモデルは振る舞いに焦点を当てています。これはまさに永続性ではありません。これは、人々が独自の定義を発明できる主題ではありません。DDDではかなり明確に記述されています。多くの場合、CRUDやレポートアプリにドメインモデルは必要ありません、必要ないときに所有していると主張すべきではありません。
アーロンノート14年

1
承認は絶対にドメイン層に属します。誰がどの許可が存在するかを決定しますか?ビジネスはします。誰が何をすることができるかを決めるのは誰ですか?ビジネスはします。数週間前に、システム内の特定のオブジェクトを編集するために必要な権限を変更する機能のリクエストがありました。テンプレートがマスターテンプレートに基づいている場合、テンプレートを編集する(マスターの値をオーバーライドする)には、通常必要とされるよりも高い特権が必要でした。ドメインにない場合、そのロジックはどこに属しますか?
アンディ

1
別の種類の認証には、顧客アカウントの制限があります。通常の顧客サービス担当者は一定のポイントまで引き上げることができますが、上限を引き上げるには管理者の承認が必要な場合があります。それが承認ロジックです。
アンディ

6

認可。ドメインオブジェクトは、そのアクセス制御ルールを維持する責任がありますか

いいえ。認可はそれ自体が懸念事項です。権限がないために有効ではないコマンドは、ドメインの前にできるだけ早く拒否する必要があります-つまり、UIを構築するために、潜在的なコマンドの承認を確認したい場合もあります(ユーザーに編集のオプションを表示することもできます)。

承認戦略がドメインモデルとは別にコンポーネント化されていると、承認戦略を(UIで、さらにサービスまたはコマンドハンドラーで)レイヤー間で共有するのが簡単になります。

遭遇する可能性のあるトリッキーな部分の1つは、コンテキストロール承認です。コマンドでは、ユーザーロールだけでなくビジネスデータ/ルールに基づいてコマンドが許可される場合と許可されない場合があります。

検証。上記と同じ議論は検証に関するものです。

私はまた、ドメイン内ではなく、(ほとんど)ノーと言うでしょう。検証は異なるコンテキストで行われ、検証ルールは多くの場合コンテキストによって異なります。集約によってカプセル化されたデータを考慮するとき、有効であるか無効であるかの単純で絶対的な意味はめったにありません。

また、承認と同様に、UI、サービス、またはコマンドハンドラなどのレイヤー全体で検証ロジックを使用します。これも、別個のコンポーネントである場合、検証でDRYを使用する方が簡単です。実用的な点から、検証(特にフレームワークを使用する場合)では、そうでなければカプセル化されるデータを公開する必要があり、多くの場合、フィールドとプロパティにカスタム属性を付加する必要があります。私はこれらが私のドメインモデル以外のクラスにあることを大いに好みます。

検証フレームワークの要件をエンティティに強制しようとするのではなく、いくつかの類似したクラスでいくつかのプロパティを複製したいです。必然的に、エンティティクラスの混乱を招くことになります。

オブジェクトの作成。ファクトリクラスとファクトリメソッド、インスタンスの「更新」。

間接参照の1つのレイヤーを使用します。私の最近のプロジェクトでは、これは何かを作成するためのコマンド+ハンドラーですCreateNewAccountCommand。代わりに、常にファクトリを使用することもできます(ただし、エンティティ操作の残りの部分がファクトリクラスとは別のサービスクラスによって公開されている場合は扱いにくい場合があります)。

ただし、一般的には、オブジェクト作成の設計の選択肢をより柔軟にしようとしています。 new簡単で使い慣れていますが、必ずしも十分ではありません。ここで判断を使用し、システムのさまざまな部分が必要に応じてさまざまな戦略を使用できるようにすることが重要だと思います。

永続性。...ドメインオブジェクトにSaveメソッドを持たないのはなぜですか

これはめったに良いアイデアではありません。それをサポートする多くの経験が共有されていると思います。

集約ルートのIAuthorizationService、IValidator、IFactory、およびIRepositoryで終わる場合、何が残っていますか?オブジェクトの状態を下書きから発行済みに変更するPublishメソッドを使用して、クラスを非貧弱なドメインオブジェクトと見なしますか?

おそらく、ドメインモデルは、アプリケーションのこの部分にとって正しい選択ではありません。


2
「遭遇する可能性のある厄介な部分の1つは、ユーザーの役割だけでなくビジネスデータ/ルールに基づいてコマンドが許可される場合と許可されない場合があるコンテキスト承認です。」-そして、これにどのようにアプローチしますか?少なくとも私にとって、承認規則は役割と現在の状態の組み合わせです。
-SonOfPirate

@SonOfPirate:ドメインイベントをリッスンし、承認を確認するクエリのニーズに非常に固有のテーブルを更新するイベントハンドラー。状態を確認し、ロールまたは個人が許可されているかどうかをイベントハンドラーで判断するロジック(したがって、ほとんどの場合、テーブルは単純なyes / noであり、認証チェックは単純なルックアップになります)。また、そのロジックのいずれかが複数の場所で使用されるとすぐに、ハンドラーから共有サービスにリファクタリングされます。
クエンティン・

1
一般に、ここ数年、私はすべてをドメインまたはビジネスオブジェクトに統合しようとすることから遠ざかりました。私の経験では、物事をよりきめ細かくし、結合性を低くすることは長期的な勝利であるようです。そのため、ある観点から見ると、このメソッドはビジネスロジックをドメインの外部に(ある程度)配置しますが、後でアジャイルな変更もサポートします。バランスをとることがすべてです。
クエンティン・スターリン

4
ここで答え自体を参照します:権限とビジネスルールの両方に依存する検証を処理するときに、状態パターンが非常に役立つことがわかりました。ドメインモデルに挿入され、ドメインオブジェクトをパラメーターとして受け取り、ドメイン固有のアクションを検証するメソッドを公開する抽象状態を作成します。このように、許可ルールが変更された場合(ほとんどの場合)、状態の実装はセキュリティコンポーネントに存在するため、モデルに手を加える必要はありません。
アーロンノート

1
認可のテーマにとどまり、具体的な例をテーブルに投げて、(両方の)あなたがどのようにそれを処理するかを見てみましょう。ドメインオブジェクトに発行操作があり、ユーザーが発行者ロールに属している必要があり、オブジェクトが特定の状態にある必要があります。UIで[公開]ボタンを非表示にするか無効にします。これをどのように達成しますか?
-SonOfPirate

4

OK、ここに私のために行きます。次のように言ってこれを先取りします。

  • 時期尚早の最適化(および設計を含む)は、多くの場合問題を引き起こす可能性があります。

  • IANMF(私はMartin Fowlerではありません);)

  • 汚い小さな秘密は、小規模なプロジェクト(おそらく中規模のプロジェクトであっても)では、アプローチの一貫性が重要になるということです。

認可

私にとって、認証と承認は常に横断的な関心事です。私の幸せな小さなJavaの世界では、SpringセキュリティまたはApache Shiroフレームワークに委任されます。

検証 私にとって、検証はオブジェクトの一部を定義するものとして見ているため、オブジェクトの一部です。

たとえば、Carオブジェクトには4つの車輪があります(いくつかの奇妙な例外がありますが、今のところ奇妙な3車輪のCarは無視してください)。Carは4(私の世界では)でない限り有効ではないため、検証はCarの定義の一部です。これは、ヘルパー検証クラスを使用できないという意味ではありません。

私の幸せなJavaの世界では、Bean検証フレームワークを使用し、ほとんどのBeanフィールドで簡単な注釈を使用しています。その場合、どのレイヤーにいてもオブジェクトを検証するのは簡単です。

オブジェクト作成

Factoryクラスを注意して表示します。あまりにも頻繁にxyxFactoryFactoryクラスを見ました;)

私はnew、依存性注入が正当化されるケースに出くわすまで、必要に応じてオブジェクトを作成する傾向があります(そして、TDDアプローチに従うことを試みるため、これは頻繁に起こります)。

私の幸せなJavaの世界ではますますGuiceになっていますが、Springの王は今でもここにいます。

持続性

だから、これは円と回り道で行われる議論であり、私は常にそれについて2つの考えを持っています。

オブジェクトを「純粋な」方法で見た場合、永続性はコアプロパティではなく、単に外部の関心事であると言う人もいます。

他の人は、ドメインオブジェクトが暗黙的に「永続化可能な」インターフェイスを実装していると考えています(ええ、私はここでストレッチしていることを知っています)。したがって、さまざまなsavedeleteなどのメソッドをそれらに設定しても構いません。これは実用的なアプローチと見なされており、多くのORMテクノロジー(私の幸せなJavaの世界ではJPA)はこの方法でオブジェクトを処理します。

横断的なセキュリティの問題から、オブジェクトのsave / update / deleteメソッドを呼び出すサービスにedit / delete / add / whateverアクセス​​許可が正しく設定されていることを確認します。本当に妄想している場合は、ドメインオブジェクト自体にアクセス許可を設定することもあります。

HTH!


2

ジミーニルソンは、DDDに関する本でこのトピックに触れています。彼は貧血モデルから始め、後のプロジェクトで非貧血モデルに行き、最終的に貧血モデルに落ち着きました。彼の推論は、異なるビジネスロジックを持つ複数のサービスで貧血モデルを再利用できるということでした。

トレードオフは、発見能力の欠如です。貧血モデルの操作に使用できる方法は、他の場所にある一連のサービス全体に広がっています。


特定の要件-データの再利用(ス​​トレス 'データ')構造-は、その共通部分を単純なDTOに削減することにつながります。
ビクターセルギエンコ

貧血モデルはより良い再利用を可能にしますか?DTOのよ​​うに聞こえますが、個人的にはプロパティ定義の再利用について気にしません。むしろ振る舞いを再利用したいです。
アンディ

@Andy-ただし、あなたの行動がドメインサービス内にあり、それらが貧弱なオブジェクト(必要に応じてDTOを使用)で動作する場合、それらの行動の再利用は増加しませんか?悪魔の擁護者を演じるだけです。
jpierson

@jpiersonただし、動作は通常、特定のユースケースに固有のものであることがわかりました。再利用がある場合、それは別のクラスに属しますが、コンシューマはそれらのクラスを使用しないため、ユースケースに固有のクラスを使用します。つまり、再利用はいわば「舞台裏」です。さらに、通常、モデルを再利用しようとすると、消費者がモデルを使用するのが難しくなります。そのため、UIレイヤーでビュー/編集モデルを作成することになります。多くの場合、DRYに違反して、より豊かなユーザーエクスペリエンスを提供します(たとえば、編集モデルのDataAnnotations)。
アンディ

1
むしろ、ユースケース用に構築されたドメインモジュールを使用し、意味のある場所で再利用します(つまり、動作をほとんどまたはまったく変更せずに再利用できます)。したがって、貧弱なドメインモデル、サービスクラス、および編集モデルの代わりに、1つのスマートで編集可能なドメインモデルがあります。使いやすく、保守がずっと簡単だとわかりました。
アンディ

2

この質問はずっと前に聞かれましたが、Domain Driven Designとタグ付けされています。質問自体には、実践全体の根本的な誤解が含まれており、受け入れられた回答を含む回答が根本的な誤解を永続させていると思います。

DDDアーキテクチャには「ドメインモデル」はありません。

認可を例としてみましょう。質問について考えてみましょう。2人の異なるユーザーがシステムで認証することを想像してください。1人のユーザーには特定のエンティティを変更する権限がありますが、他のユーザーにはありません。何故なの?

単純で不自然な例は嫌いです。なぜなら、彼らはしばしば啓発する以上に混乱させるからです。しかし、2つの異なるドメインがあるふりをしましょう。1つは、マーケティング代理店向けのCMSプラットフォームです。この代理店には、コピーライターやグラフィックアーティストによる管理が必要なコンテンツがすべてオンラインである多くの顧客がいます。コンテンツには、さまざまな顧客向けのランディングページだけでなく、ブログ投稿も含まれます。

もう1つのドメインは、靴会社の在庫管理です。システムは、フランスの製造業者から米国大陸の流通センター、地元の市場の小売店、そして最終的に小売店で靴を購入する顧客まで、在庫を管理します。

認可ルールが両方の会社で同じであると考える場合、それはドメイン外のサービスの良い候補です。しかし、認可ルールは同じだとは思いません。ユーザーの背後にある概念も異なります。確かに言語は異なります。マーケティング代理店にはおそらく投稿者や資産所有者などの役割がありますが、靴会社にはおそらく配送担当者や倉庫管理者、または店長などの役割があります。

これらの概念にはおそらく、ドメインでモデル化する必要があるすべての種類の許可ルールが関連付けられています。しかし、それは、同じアプリ内であっても、すべてが同じモデルの一部であることを意味しません。なぜなら、異なる境界コンテキストがあることを覚えておいてください。

そのため、認可のコンテキストにおける非貧血ドメインモデルは、クリックした広告に応じて、靴の在庫を在庫の少ない店舗にルーティングしたり、サイト訪問者を適切なランディングページにルーティングしたりするコンテキストとは異なると考えるかもしれません。

貧弱なドメインモデルを使用している場合は、コードの記述を開始する前に、コンテキストマッピングにより多くの時間を費やす必要があるでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.