这是关于域模型设计的问题。
让我们说对于涉及用户和组的域设计,我们有以下接口来实现:
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()方法)
你通常如何做到这一点?
答案 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);
}
}
需要注意的一些要点:
为了避免在向User
添加User
时延迟加载大量Group
,我已将Group
管理方法移至User
- 根据您对Group
实际拥有的行为,您甚至可以考虑将其转换为枚举而不是类。请注意,如果您将实体框架POCO T4模板与FixupCollection
一起使用,这仍然会延迟加载User
中的所有Group
,但您可以获得以某种方式围绕它:)
服务层类将实现一个Create()
方法,就像您在存储库中一样。存储库将具有Add
方法,Find
方法和SaveChanges()
方法。 Add
会将服务层创建的对象添加到对象上下文中。
所有Repository
类都将设置为使用相同的基础请求范围的对象上下文,因此您调用SaveChanges()
的那个类并不重要。
SaveChanges()
会导致保存该请求期间对象所发生的所有更改,例如User
添加了新Group
个Groups
他们的{{1}}集合。
最后,另一种将实体与存储库和其他基础架构组件分离的好方法是Domain Events。