如何解耦存储库和实体

时间:2012-05-30 14:30:05

标签: model-view-controller model ddd-repositories

这是关于域模型设计的问题。

让我们说对于涉及用户和组的域设计,我们有以下接口来实现:

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,同时保持实体类(User,Group)与存储库类(UserRepository,GroupRepository)分离。

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

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

你通常如何做到这一点?

1 个答案:

答案 0 :(得分:1)

我不这样做。我的Repository对象紧密耦合到它们所涉及的聚合的根,并且(作为一种旁边)我不打扰为我的域模型对象创建接口,除非我发现我有一个好的理由这样做 - 你有特别的理由这样做吗?

我没有遇到任何Repository示例,这些示例不会在存储库类(例如this one)中使用实体实现类型,并且无法思考使用界面的任何真正优势。接口通过在测试时更容易模拟系统的整个层来获得基础架构组件(例如Repository)的保留,使用域对象的接口不会获得相同类型的优势。 / p>

也许实际上回答了这个问题......

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

对于将User添加到Group的具体示例,我使用了Service Layer类,并执行此操作:

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. 为了避免在向User添加User时延迟加载大量Group,我已将Group管理方法移至User - 根据您对Group实际拥有的行为,您甚至可以考虑将其转换为枚举而不是类。请注意,如果您将实体框架POCO T4模板与FixupCollection一起使用,这仍然会延迟加载User中的所有Group,但您可以获得以某种方式围绕它:)

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

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

  4. SaveChanges()会导致保存该请求期间对象所发生的所有更改,例如User添加了新GroupGroups他们的{{1}}集合。

  5. 最后,另一种将实体与存储库和其他基础架构组件分离的好方法是Domain Events