我正在努力掌握DDD,我觉得我已经很好地掌握了实体、聚合、聚合根、存储库和值对象,以及如何在代码中实际表示这些概念。
我仍在努力解决的问题是如何在实际代码中表示引用(即不构成聚合的关系)。假设我有以下两个聚合
DISCUSSIONGROUP (entity, aggregate root)
+name: string
+ENTRY (entity)
+title: string
+message: string
+timestamp: date
+madeByUser: USER
USER (entity, aggregate root)
+name: string
+email: string
+memberOf: DISCUSSIONGROUP
用户可以是讨论组的成员,但用户实体不属于同一集合。然而,两者之间仍然存在关系(参考文献)。在查看实际代码时,我会实现聚合关系,例如(使用C#):
interface IDiscussionGroup {
string Name { get; }
IList<IEntry> { get; }
...
}
即使用简单的C#引用。你会以同样的方式实施DDD参考(如上所述)吗,即:
interface IUser {
IDiscussionGroup MemberOf { get; }
...
}
是否应该使用其他构造来表示较弱类型的关系?
实际上这取决于您的需求。没有一种方法可以很好地做到这一点,但我想做的是将DISCUSSIONGROUP实体中的madeByUser属性设置为ValueObject。
为什么?因为当显示来自DiscusionGroup的条目时,我对条目上显示的用户的姓名和电子邮件很感兴趣。当我的条目从数据库中删除时,我不希望该用户在我的条目上消失。如果用户实体有更多的属性和行为,例如在显示用户信息时,我对此不感兴趣,所以ValueObject会很好。此外,您可以在多个条目之间共享VO,而无需为每个条目检索具体User的实例。
另一方面,用户聚合是否与讨论组在同一上下文中使用?我的意思是,DiscussionGroup可以是一个上下文中的实体(例如,在前台用例中),也可以是用户管理后台中的价值对象(我们对管理讨论组不感兴趣,只对用户感兴趣,所以关于讨论组的简单信息就足够了)。但是,在这种情况下,您可能希望有一个从DiscussionGroup实体到User实体的引用,或者也将DiscussionGroup作为VO。是否需要检索给定用户的所有讨论组和条目?或者您只需要一些可以嵌入到Value对象中的信息?
如果我没有很好地回答你的问题,我很抱歉,但直接引用或只是在聚合之间使用某种共享Id或使用VO实际上取决于用例和你必须实现的行为。
我个人会检查我是否定义了一个好的聚合边界,如果我可以在不进行懒惰加载和其他类似的研究的情况下检索聚合中的所有数据。
也许其他人有另一种观点。
"持有某物"one_answers"属于某物"看起来类似。
区别在于——什么是构建的。
public class User{
public IList<DiscussionGroup> GroupMembership{get; private set;}
public User(){
GroupMembership=new List<DiscussionGroup>();
}
public void JoinGroup(DiscussionGroup group){
GroupMembership.Add(group);
}
public bool IsMemberOf(DiscussionGroup group){
return GroupMembership.Contains(group);
}
public void EnsureMembership(DiscussionGroup group){
ThrowIf(!IsMemberOf(this),
"User is not a member of this discussion group");
}
}
public class DiscussionGroup{
public IList<Discussion> Discussions {get;private set;}
public DiscussionGroup(){
Discussions=new List<Discussion>();
}
public Discussion CreateDiscussion(string name, Post firstPost){
CurrentUser.EnsureMembership(this);
var discussion=new Discussion(this, name, firstPost);
Discussions.Add(discussion);
return discussion;
}
}
public class Discussion{
public DiscussionGroup Group{get; private set;}
public Discussion(DiscussionGroup group, string name, Post firstPost){
CurrentUser.EnsureMembership(group);
Guard.Null(group);
Group=group;
Name=name;
Posts=new List<Post>{firstPost};
}
public void WritePost(string text){
CurrentUser.EnsureMembership(group);
Posts.Add(new Post(this,text));
}
}
public class Post{
public Discussion Discussion{get; private set;}
public string Text {get; private set;}
public Post(Discussion discussion, string text){
Guard.Null(discussion);
Discussion=discussion;
Text=text;
}
}
//usage
var me=new User();
var you=new User();
var stackOverflow=new DiscussionGroup();
me.JoinGroup(stackOverflow);
you.JoinGroup(stackOverflow);
LoginAs(you);
var question="I'm trying to get a handle on DDD and feel that...";
var discussion=stackOverflow.CreateDiscussion
("How to represent references in DDD entites",question);
LoginAs(me);
var answer="'Holds something' and 'belongs to something'...";
discussion.WritePost(answer);