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);
}

棘手的一点是实现 #1 #2 和 #3,同时保持实体类(用户、组)与存储库类(用户存储库、组存储库)分离。

另一个需要考虑的技术问题是大多数 RMDB 系统不实现多对多关系,并且在实践中总是有一个单独的表(例如,UserGroupAssociation)来记录每个记录通过外键关联一个用户和一个组。我想从域接口中隐藏这个实现细节,并通过成员 #1 #2 和 #3 公开等效逻辑。

调用#2 和#3 的效果不应该持续到有问题的组对象被保存(即传递给存储库对象的 Save() 方法)

你通常是怎么做的?

4

1 回答 1

1

我不这样做。我的Repository对象与它们相关的聚合的根紧密耦合,并且(顺便说一句)我不会费心为我的域模型对象创建接口,除非我发现我有充分的理由这样做 - 你呢有特别的理由这样做吗?

我没有遇到任何Repository不使用存储库类中的实体实现类型的示例(例如this),并且想不出使用接口的任何真正优势。接口通过在测试时更容易模拟系统的整个层来赢得基础设施组件(如 a )的保留,Repository但使用域对象的接口不会获得相同类型的优势。

也许要真正回答这个问题......

我从来没有一个域对象访问 a Repository- 毕竟域对象应该代表现实生活中域中的某些东西,而存储库是现实生活中不存在的基础设施组件,那么为什么域对象会知道一个呢?

对于将 a 添加User到 a的具体示例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时延迟加载大量Users,我将管理方法移到了- 根据您实际拥有的行为数量,您甚至可以考虑将其转换为枚举而不是类。请注意,如果您使用带有s 的实体框架 POCO T4 模板,这仍然会延迟加载a 中的所有 s ,但您可以通过一种或另一种方式解决这个问题:)UserGroupGroupUserGroupFixupCollectionUserGroup

  2. 服务层类将实现一个Create()方法,就像您在存储库中拥有的那样。存储库将有一个Add方法,Find方法和一个SaveChanges()方法。Add将由服务层创建的对象添加到对象上下文中。

  3. 所有Repository类都将设置为使用相同的底层、请求范围的对象上下文,因此调用哪个类并不重要SaveChanges()

  4. SaveChanges()将导致在该请求期间发生在对象上的所有更改都被保存,例如User将新Group的 ' 添加到其Groups集合中。

最后,将实体与 Repositories 和其他基础设施组件分离的另一种好技术是Domain Events

于 2012-05-30T15:08:39.527 回答