3

これは、ドメイン モデルの設計に関する質問です。

ユーザーとグループを含むドメイン設計の場合、実装する次のインターフェイスがあるとします。

interface IUser
{
    string Name{get;}
    DateTime DOB {get;}
}

interface IGroup
{
    string Name {get;}
    bool IsUserInGroup(IUser user); // #1
    void IncludeUser(IUser user);   // #2
    void ExcludeUser(IUser user);   // #3
}

interface IUserRepository
{
    IUser Create(string name);
    IUser GetByName(string name);
    void Remove(IUser user);
    void Save(IUser user);
}

interface IGroupRepository
{
    IGroup Create(string name);
    IGroup GetByName(string name);
    void Remove(IGroup group);
    void Save(IGroup group);
}

ややこしいのは、エンティティ クラス (User、Group) をリポジトリ クラス (UserRepository、GroupRepository) から切り離したまま、#1 #2 と #3 を実装することです。

考慮すべきもう 1 つの技術的事項は、ほとんどの RMDB システムは多対多の関係を実装していないことです。実際には、外部キーを介してユーザーとグループを関連付けるレコードを持つ個別のテーブル (UserGroupAssociation など) が常に存在します。この実装の詳細をドメイン インターフェイスから隠し、メンバー #1 #2 および #3 を介して同等のロジックを公開したいと考えています。

#2 と #3 を呼び出した効果は、問題のグループ オブジェクトが保存される (つまり、リポジトリ オブジェクトの Save() メソッドに渡される) まで保持されません。

普段はどのようにしていますか?

4

1 に答える 1

1

私はしません。私Repositoryのオブジェクトは、それらが関連する集約のルートに密結合されており、(余談ですが) ドメイン モデル オブジェクトのインターフェイスを作成することは、正当な理由がない限り気にしません。これを行う特定の理由がありますか?

Repositoryリポジトリ クラスでエンティティ実装タイプを使用しない例 (たとえばthis one )に遭遇したことがなく、代わりにインターフェイスを使用することの実際の利点を考えることができません。インターフェイスは、テスト時にシステムのレイヤー全体を簡単にモックアウトできるようにすることで、インフラストラクチャ コンポーネント ( などRepository) の保持を獲得します。ドメイン オブジェクトのインターフェイスを使用すると、同じタイプの利点は得られません。

そして、おそらく実際に質問に答えるために...

ドメイン オブジェクトがアクセスすることは決してありません。ドメイン オブジェクトRepositoryは、実際にはドメイン内の何かを表すものであり、リポジトリは現実には存在しないインフラストラクチャ コンポーネントです。

に を追加する具体的な例として、 Service Layerクラスを使用して、次Userのようにします。Group

public class UserService
{
    private readonly IGroupRepository _groupRepository;
    private readonly IUserRepository _userRepository;

    public UserService(
        IGroupRepository groupRepository,
        IUserRepository userRepository)
    {
        this._groupRepository = groupRepository;
        this._userRepository = userRepository;
    }

    public void IncludeUserInGroup(string groupName, string userName)
    {
        var group = this._groupRepository.FindByName(groupName);
        var user = this._userRepository.FindByName(userName);

        group.IncludeUser(user);

        this._userRepository.SaveChanges();
    }
}

public class User
{
    public void AddToGroup(Group group)
    {
        this.Groups.Add(group);
    }

    public void RemoveFromGroup(Group group)
    {
        this.Groups.Remove(group);
    }
}

注意すべき点:

  1. aに aをUser追加するときに多数の を遅延ロードするのを避けるために、管理メソッドを に移動しました- 実際に に対してどれだけの振る舞いをしているかによっては、クラスではなく列挙型に変えることを検討することもできます。s でEntity Framework POCO T4 テンプレートを使用している場合、これでもa 内のすべての s が遅延ロードされますが、何らかの方法でそれを回避できることに注意してください :)UserGroupGroupUserGroupFixupCollectionUserGroup

  2. Service Layer クラスはCreate()、リポジトリにあるようなメソッドを実装します。リポジトリには、Addメソッド、Findメソッド、メソッドがありSaveChanges()ます。Addサービス層によって作成されたオブジェクトをオブジェクト コンテキストに追加します。

  3. すべてのRepositoryクラスは、基になるリクエスト スコープの同じオブジェクト コンテキストを使用するように設定されるため、どのクラスを呼び出しSaveChanges()ても問題ありません。

  4. SaveChanges()Userは、コレクションに新しいGroupが追加されるなど、そのリクエスト中にオブジェクトに発生したすべての変更を保存しGroupsます。

最後に、リポジトリやその他のインフラストラクチャ コンポーネントからエンティティを分離するためのもう 1 つの優れた手法は、Domain Eventsです。

于 2012-05-30T15:08:39.527 に答える