14

私が直面している問題の簡単な例を次に示します。これは、ここで提示されているアイデアの一部や、DDD に関する他の場所と一致していません。

人を作成/操作する ASP.NET MVC 3 サイトがあるとします。コントローラーはアプリケーション サービス レイヤー (PersonService) にアクセスし、ドメイン エンティティ (EF 4 POCO) と PersonRepository を使用して変更を行い、それらを永続化します。簡単にするために、ここではすべてのインターフェイスを省略しています。この場合、Person がルートであり、簡単にするために電子メール アドレスのみを持ちます (また、電子メールは不変ではなく、更新できると仮定します)。

オプション 1: エンティティに直接関連する動作がエンティティの一部として実装される DDD の基本 [私の理解] に固執するようにしてください (Person は AddEmail、ChangeEmail などを実装します)。これに関する唯一の問題は、Add* メソッドを除いて、Person がコンテキストまたはエンティティ フレームワークの部分について知る必要があること (これにより永続性の無視が取り除かれます)、または「サービス」またはリポジトリを使用してマークを付ける必要があることです。変更されたメール。

// Person Service
public class PersonService {
    // constructor injection to get unit of work and person repository...
    // ...methods to add/update a person
    public EmailAddress AddEmailAddress(int personId, EmailAddress email)
    {   
        Person p = personRepository.Find(p => p.Id == personId).First();
        p.AddEmail(email);   
        uow.SaveChanges();
        return email; 
    }

    public EmailAddress ChangeEmailAddress(EmailAddress email)
    {
        Person p = personRepository.Find(p => p.Id == personId).First();
        p.ChangeEmail(email);   
        // change state of email object here so it's updated in the next line???
        // if not here, wouldn't the Person entity have to know about the context
        // or use a service?
        uow.SaveChanges();
        return email;    
    }
}

// Person Repository
public class PersonRepository
{
   // generic repository implementation
}

// Person Entity
public class Person
{
    public string Name { get;set; }
    public IEnumerable<EmailAddress> EmailAddresses { get;set; }

    public void AddEmail(EmailAddress email)
    {
        this.EmailAddresses.Add(email);
    }

    public void ChangeEmail(EmailAddress email)
    {
        EmailAddress orig = this.EmailAddresses.First(e => e.Id == email.id);

        // update properties on orig

        // NOW WHAT? [this] knows nothing about the context in order to change state,
        etc, or do anything to mark the email add updated
    }
}

// Email 
public class EmailAddress
{
    public string Email { get;set; }
    public bool IsPrimary { get;set; }
}

オプション 2: 個人サービスでリポジトリを使用して電子メール アドレスを追加/更新し、個人エンティティに動作を実装しないようにします。これは、多対多の関係 (作業を完了するために 2 つのテーブルを更新する必要があるアドレスなど) の場合ははるかに単純ですが、その場合、モデルは単なる getter と setter の集まりである「貧血」になります。

// Person Service
public class PersonService {
    // constructor injection to get unit of work and person repository...
    // ...methods to add/update a person
    public EmailAddress AddEmailAddress(int personId, EmailAddress email)
    {   
        Person p = personRepository.Find(p => p.Id == personId).First();
        personRepository.AddEmail(personId, email);   
        uow.SaveChanges();
        return email; 
    }

    public EmailAddress ChangeEmailAddress(EmailAddress email)
    {
        personRepository.ChangeEmail(email);   
        uow.SaveChanges();
        return email;    
    }
}

// Person Repository
public class PersonRepository
{
   // generic repository implementation
}

// Person Entity
public class Person
{
    public string Name { get;set; }
    public IEnumerable<EmailAddress> EmailAddresses { get;set; }
}

// Email 
public class EmailAddress
{
    public string Email { get;set; }
    public bool IsPrimary { get;set; }
}

とにかく、これについて何か考えはありますか?

ありがとう、ブライアン

4

2 に答える 2

2

オプション 1 が有効です。

理由は簡単です。電子メール アドレスの変更はドメインの問題です。あなたのドメインの専門家は、メールを変更する必要があると言っているに違いありません。これにより、電子メール変更ロジックの一部が、ドメイン モデルに存在するはずのビジネス ロジックとして自動的にマークされます。オブジェクトは主に、オブジェクトが保持するデータではなく、動作によって定義されます。

また、作業単位パターンを使用してすべてをサービスでラップすることを選択する前に、よく考えてください。集約ルートはトランザクション境界を描画することになっているため、サービスは通常、リポジトリとドメイン オブジェクトの呼び出しをラップするだけでは役に立ちません。

私は次のようなものがあります:

public class Person{
  public Email Email{get;private set;}
  public void SpecifyEmail(Email email){
    //some validation, if necessary
    EnsureEmailCanBeChanged();
    //applying state changes
    Email=email;
    //raising event, if necessary
    Raise(new EmailChanged(this));
  }
  public class EmailChanged:Event<Person>{
    public EmailChanged(Person p):base(p){}
  }
}
public class Email{
  public Email(string email){
    //validations (e.g. email format)
    Value=email;
  }
  //implicit to string, explicit from string conversions
}

public class PersonController{
  public ActionResult SpecifyEmail(int person, string email){
    _persons.Get(person).SpecifyEmail((Email)email);
    return RedirectToAction("Person",new{person});
  }
}

私は NHibernate を使用しています。これは、Person が前回永続化されてから何が変更されたかを把握するのに十分スマートです。エンティティ フレームワークがこれをどのように処理するかを正確に言うのは難しいです。

于 2011-06-16T09:50:14.083 に答える
0

私は NH ユーザーであり、EF のすべての制限を知っているわけではありませんが、一般的に言えば、ORM の制限が何であれ、エンティティはできるだけクリーンなままにしておく必要があります。Service Layer はすでに Data Access と結合されているため、害はありません。

そして、EF4 はコレクションの変更を追跡する方法を知っているべきだと思います。そうでない場合、最善の方法は、追加/削除ロジックを Person エンティティに残して、PersonService に保持することです。

ところで、あなたの EmailAddress はここではエンティティではなく、ID もありません (ただのタイプミスだと思います)。また、EmailAddress を Person にどのようにリンクしますか?

于 2011-06-16T08:06:33.527 に答える