20

貧血ドメインモデルと関心の分離に関するいくつかの質問を読みました。貧血のドメインオブジェクトでドメインロジックを実行/アタッチするための最良のテクニックは何ですか?私の仕事では、かなり貧血のモデルがあり、現在、ドメインオブジェクトでデータベース/ビジネスロジックを実行するために「ヘルパー」クラスを使用しています。例えば:

public class Customer
{
    public string Name {get;set;}
    public string Address {get;set;}
}

public class Product
{
    public string Name {get;set;}
    public decimal Price {get;set;}
}

public class StoreHelper
{
    public void PurchaseProduct(Customer c, Product p)
    {
         // Lookup Customer and Product in db
         // Create records for purchase
         // etc.
    }
}

アプリが購入を行う必要がある場合、StoreHelperを作成し、ドメインオブジェクトのメソッドを呼び出します。私にとって、Customer / Productが自分自身をリポジトリに保存する方法を知っていることは理にかなっていますが、ドメインオブジェクトにSave()メソッドは必要ないでしょう。Customer.Purchase(Product)のようなメソッドでも意味がありますが、それはエンティティにドメインロジックを配置します。

これが私が出くわしたいくつかのテクニックですが、どれが良いか悪いかわかりません:

  1. CustomerとProductは、基本的なCRUD操作を一般的な方法で提供する「Entity」クラスから継承します(ORMを使用する場合もあります)。
    • 長所:各データオブジェクトは自動的にCRUD操作を取得しますが、データベース/ORMに関連付けられます
    • 短所:これは、オブジェクトのビジネスオペレーションの問題を解決せず、すべてのドメインオブジェクトを適切でない可能性のあるベースエンティティに関連付けます。
  2. ヘルパークラスを使用して、CRUD操作とビジネスロジックを処理します
    • 「純粋なデータベース」操作用にDAOを用意し、よりビジネス固有の操作用に個別のビジネスヘルパーを用意することは理にかなっていますか?
    • これには、非静的または静的ヘルパークラスを使用する方がよいでしょうか。
    • 長所:ドメインオブジェクトはデータベース/ビジネスロジックに関連付けられていません(完全に貧血)
    • 短所:あまりOOではなく、アプリケーションコードでヘルパーを使用するのはあまり自然ではありません(Cコードのように見えます)
  3. エンティティが任意のリポジトリに保存するメソッドを持っている場合は、ダブルディスパッチ手法を使用します
    • 長所:関心の分離の改善
    • 短所:エンティティにはいくつかの追加のロジックがアタッチされています(ただし、分離されています)
  4. C#3.0では、拡張メソッドを使用して、ドメインオブジェクトに触れずにCRUD/ビジネスメソッドをドメインオブジェクトにアタッチできました。
    • これは有効なアプローチですか?長所/短所は何ですか?
  5. 他のテクニック?

これを処理するための最良のテクニックは何ですか?私はDDDにかなり慣れていません(私はエバンスの本を読んでいます-それでおそらくそれは私の目を開くでしょう)

4

4 に答える 4

15

貧血モデルを回避するために、ヘルパー クラスをリファクタリングします。


「Customer.PurchaseProduct(Product product, Payment payment)」、
「Customer.KillCustomer(Person killer、Weapon Weapon)」などのロジックは、
「Customer」ドメイン オブジェクト内に存在する必要があります。


「Customer.IsCustomerAlive()」
「Customer.IsCustomerHappy( )」のようなロジック
は仕様に記載する必要があります。


「Customer.Create()」、
「Customer.Update() 」などのロジックは、
明らかにリポジトリに移動する必要があります。


「Customer.SerializeInXml()」
「Customer.GetSerializedCustomerSizeInBytes( )」のようなロジック
は、サービスに送信する必要があります。

複雑なコンストラクターはファクトリに移動する必要があります。

それが私がそれを見る方法です。誰かが私の DDD アプローチの理解についてコメントしてくれたらうれしいです。


編集:

場合によっては、貧血ドメイン モデルを避けるべきではありません

私の回答を編集して、DDD はパターンのピックアップとドロップに関するものではないことを追加しました。
DDD は私たちの考え方です。

于 2009-05-21T13:46:04.607 に答える
7

Martin Fowlerは、貧血ドメインモデルを含むドメインモデルについて多くのことを書いています。彼はまた、役立つかもしれないドメインモデルとデータベースの多くのデザインパターンの簡単な説明(およびUMLクラス図)を持っています:「エンタープライズアプリケーションアーキテクチャのパターン」のカタログ

ActiveRecordDataMapperのパターンを確認することをお勧めします。問題の説明から、ヘルパークラスにはドメイン/ビジネスルールデータベース実装の詳細の両方が含まれているようです。

Active Recordは、ヘルパーのドメインロジックとデータベースコードを他のドメインオブジェクト(Entity基本クラスなど)に移動します。データマッパーは、ヘルパーのドメインロジックをドメインオブジェクトに移動し、データベースコードを別のマップオブジェクトに移動します。どちらのアプローチも、手続き型のヘルパークラスよりもオブジェクト指向です。

EricEvansの「ドメイン駆動設計」の本は素晴らしいです。少し乾きますが、間違いなく価値があります。InfoQには、Evansの本の良い入門書である「DomainDrivenDesignQuickly」ミニブックがあります。さらに、「ドメイン駆動設計をすばやく」は無料のPDFとして入手できます。

于 2009-03-07T08:13:37.983 に答える
2

私は常に、貧血ドメイン モデルをアンチ パターンと考えてきました。顧客が製品を購入することは明らかであり、その能力はインターフェイスの実装によって生成できます

Interface IPurchase
      Purchase(Product);

、したがって、ドメインオブジェクトのいずれかが必要に応じてそれを実装できます。このようにして、ドメイン オブジェクトに機能を導入することができます。これはまさに本来あるべき場所です。

于 2009-03-04T09:12:49.190 に答える
0

言及していないアプローチの1つは、AOPを使用してデータアクセスを処理することです。このアプローチの最近の使用例(投稿の目的で大幅に簡略化されていますが)は、アカウントからの引き落としを成功させるために必要なビジネスロジックをカプセル化Accountするメソッドを持つドメインエンティティがあったことです。debit

注意:すべてのコードは、AspectJAOP表記のJavaです。

public boolean debit(int amount) {
    if (balance - amount >= 0) {
        balance = balance - amount;
        return true;
    }
    return false;
}

適切なリポジトリをアスペクトに挿入した後、ポイントカットを使用してこのメ​​ソッドの呼び出しをインターセプトしました...

pointcut debit(Account account,int amount) :
    execution(boolean Account.debit(int)) &&
    args(amount) &&
    target(account);

...そしていくつかのアドバイスを適用しました:

after(Account account, int amount) returning (boolean result)  : debit(account,amount) {
    if (result) getAccountRepository().debit(account, amount);
}

私の意見では、これにより関心の分離がうまくいき、ドメインエンティティがアプリケーションのビジネスロジックに完全に集中できるようになります。

于 2009-12-19T10:19:08.613 に答える