我的汇总包含实体A
,AbstractElement
,X
,Y
和Z
。根实体是A
,其中还包含AbstractElement
列表。实体X
,Y
和Z
继承自AbstractElement
。我需要将X
,Y
和Z
的实例添加到A
的实例中。一种方法是对每种类型使用一种方法,即addX
,addY
和addZ
。这些方法将创建X
,Y
和Z
的实例所需的值作为参数。但是,每次我添加一个继承自AbstractElement
的新类型时,我都需要修改实体A
,所以我认为这不是最好的解决方案。
另一种方法是使用抽象添加方法addAbstractElement
来添加AbstractElement
个实例。但是,在这种情况下,该方法将AbstractElement
的实例作为参数。由于此方法将由位于聚合之外的实体调用,因此在DDD规则/建议之后,这些外部实体是否有权创建AbstractElement
的实例?我在Eric Evans一书中读到,外部实体无权持有除根之外的聚合实体的引用?
这类问题的最佳做法是什么?
谢谢
答案 0 :(得分:7)
来自Evan的书,第139页:
“如果你需要在预先存在的AGGREGATE中添加元素,你可以在AGGREGATE的根目录上创建一个FACTORY方法”
意思是,您应该在根(A)上创建一个工厂方法,它将获得AbstractElement的详细信息。此方法将根据某个决策参数创建AbstractElement(X / Y / Z),并将其添加到其AbstractElements的内部集合中。最后,此方法返回新元素的id。
最诚挚的问候,
答案 1 :(得分:2)
一些评论。正如之前的回答者所说,使用工厂方法是一个好习惯。如果你可以避免它,永远不要创建对象。通常,这是一个非常大的气味,错过了从你的领域更有意义的机会。
我写了一个小例子来说明这一点。在这种情况下,视频是聚合根。在聚合的边界内是视频对象及其相关的注释。注释可以是匿名的,也可以由已知用户编写(为了简化示例,我用用户名表示用户,但很明显,在实际应用程序中,你会有类似UserId的东西)。
以下是代码:
public class Video {
private List<Comment> comments;
void addComment(final Comment.Builder builder) {
this.comments.add(builder.forVideo(this).build());
// ...
}
}
abstract public class Comment {
private String username;
private Video video;
public static public class Builder {
public Builder anonymous() {
this.username = null;
return this;
}
public Builder fromUser(final String username) {
this.username = username;
return this;
}
public Builder withMessage(final String message) {
this.message = message;
return this;
}
public Builder forVideo(final Video video) {
this.video = video;
return this;
}
public Comment build() {
if (username == null) {
return new AnonymousComment(message);
} else {
return new UserComment(username, message);
}
}
}
}
public class AnonymousComment extends Comment {
// ...
}
static public class UserComment extends Comment {
// ...
}
要思考的一件事是聚合边界包含对象而不是类。因此,很有可能在许多聚合中表示某些类(主要是值对象,但也可以是实体的情况)。