Model view controller 如何解耦存储库和实体

Model view controller 如何解耦存储库和实体,model-view-controller,model,ddd-repositories,Model View Controller,Model,Ddd Repositories,这是一个关于领域模型设计的问题 假设对于涉及用户和组的域设计,我们需要实现以下接口: 接口IUser { 字符串名称{get;} 日期时间DOB{get;} } 接口群 { 字符串名称{get;} bool-IsUserInGroup(IUser-user);/#1 void IncludeUser(IUser用户);//#2 无效排除用户(IUser用户);//#3 } 接口IUSER存储库 { IUser-Create(字符串名); IUser GetByName(字符串名称); 无效删除(

这是一个关于领域模型设计的问题

假设对于涉及用户和组的域设计,我们需要实现以下接口:

接口IUser
{
字符串名称{get;}
日期时间DOB{get;}
}
接口群
{
字符串名称{get;}
bool-IsUserInGroup(IUser-user);/#1
void IncludeUser(IUser用户);//#2
无效排除用户(IUser用户);//#3
}
接口IUSER存储库
{
IUser-Create(字符串名);
IUser GetByName(字符串名称);
无效删除(IUser用户);
作废保存(IUser用户);
}
接口IGroupRepository
{
i组创建(字符串名称);
i组GetByName(字符串名称);
空洞清除(IGroup组);
作废保存(IGroup组);
}
棘手的一点是实现#1#2和#3,同时保持实体类(用户、组)与存储库类(UserRepository、GroupRepository)的解耦

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

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


你通常是怎么做的?

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

我没有遇到任何
存储库
示例不使用存储库类中的实体实现类型(例如),也没有想到使用接口的任何真正优势。接口为基础架构组件(如
存储库
)提供了支持,因为在测试时可以更轻松地模拟整个系统层,而使用域对象接口并不能获得相同类型的优势

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

我从未让域对象访问
存储库
——域对象毕竟应该在现实生活中代表域中的某些东西,而存储库是现实生活中不存在的基础结构组件,那么为什么域对象会知道一个呢

对于将
用户
添加到
的特定示例,我将使用一个类,并执行以下操作:

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);
    }
}
需要注意的几点:

  • 为了避免在将
    用户添加到
    时延迟加载大量
    用户
    ,我将
    管理方法移动到
    用户
    ——这取决于
    的实际行为,你甚至可以考虑把它变成一个枚举而不是一个类。请注意,如果将实体框架POCO T4模板与
    FixupCollection
    s一起使用,这仍然会延迟加载
    组中的所有
    用户
    ,但您可以通过某种方式绕过:)

  • 服务层类将实现一个
    Create()
    方法,类似于存储库中的方法。存储库将有一个
    Add
    方法、
    Find
    方法和一个
    SaveChanges()
    方法
    Add
    将服务层创建的对象添加到对象上下文中

  • 所有
    Repository
    类都将被设置为使用相同的底层、请求范围的对象上下文,因此无论调用哪个
    SaveChanges()

  • SaveChanges()
    将导致保存该请求期间对象发生的所有更改,例如
    用户
    将新的
    添加到其
    集合中


  • 最后,将实体与存储库和其他基础设施组件分离的另一个好技术是。

    接口的使用是提供一个更高级别的概念,而不仅仅是一个要读/写的属性桶。例如,与其使用
    类用户{public byte[]PasswordHash{get;set;}}
    ,不如使用
    接口IUser{bool VerifyPassword(string password);}
    ?在该示例中,您不能将
    PasswordHash
    属性设置为私有(或者受保护,如果您受到ORM的限制)
    User
    仍将有一个
    VerifyPassword()
    的实现,因此它不仅仅是一个属性桶。对于简单的方法,这是非常正确的。但对于IncludeUser()和ExcludeUser()等其他对象(例如#2和#3),实现需要访问存储库对象,而不一定是它自己的存储库对象。对于IncludeUser(),Group类需要与GroupRepository、UserGroupAssociationRepository进行对话。然而,让实体知道存储库是非常不可取的耦合。在您的示例中,唯一剩下的灰色区域是Group.Users集合。如果一个组可以包含大量用户,那么在需要时只延迟加载集合当然是个好主意,但它需要从集合中添加和删除实体的功能。这是如何实现的?我已经更新了关于延迟加载的答案。乐意帮忙:)