在域驱动设计的背景下:
我的域名是关于会议和邀请发送 一个例子:
用户可以创建名为“MyMeeting”的会议,他可以通过一些专门的“邀请”邀请他选择的用户。
我阅读了Vaughn Vernon的书(IDDD),我得出结论:Meeting
和Invitation
之间没有不变。
所以,我开始创建两个不同的聚合根:
Meeting
(id,name等)Invitation
(meetingId,happenOn,receiver(目标用户)) 我意识到可能会立即发送多个邀请。
所以我考虑在InvitationRepository
中创建一个特殊查询,旨在立即保存所有邀请:
void addAll(List<Invitation> invitations)
问题在于它引入了Invitation
聚合结构的不连贯性
说明:
该结构涉及每个Invitation
都有自己的关联meetingId
鉴于addAll
旨在添加有关同一MeetingId
的多个邀请;换句话说:每个邀请都很常见。
因此,如果我想避免多个数据库查询,由于当前的结构,这种实现会很难...:
void addAll(List<Invitation> invitations) {
//meetingId supposed to be the same across those invitations,
//an ugly way to retrieve it would be :
MeetingId meetingId = invitations.get(0).getMeetingId();
Date occurredOn = invitations.get(0).getOccurredOn();
List<User> receivers = new ArrayList();
for(invitation: Invitations) {
receivers.add(invitation.getReceiver());
}
//then perform the query saving all invitations at once
//=> one invitation for each receiver
}
处理一次保存多个Invitations
的情况有什么好办法?
域结构应该改变吗?
我想到了InvitationRepository
中的签名:
void add(List<Invitation> invitations)
void addAll(List<Invitation> invitations, MeetingId meetingId, DateTime: occurredOn)
甚至使用名为Invitations的包装器:
class Invitations { //note the plurality
MeetingId meetingId;
DateTime occurredOn;
List<Invitation> invitations;
//........
}
=&GT; void addAll(Invitations invitations)
在同一数据库查询中保存嵌套邀请(聚合根)。
这样,在addAll
实施的情况下,我不会从meetingId
中选择occurredOn
和invitations itself
,从而导致干扰,但保留整体远不那么难看。
答案 0 :(得分:3)
addAll
方法的正确实现是不关心业务规则的方法。如果邀请是独立的聚合,那么存储库应该对它们进行处理。在循环中调用add
或调用addAll
之间不应该有不同的语义。
Set<Invitation> invitations = meeting.inviteAll(invitees);
repository.addAll(invitations);
此处inviteAll
方法封装了确保所有邀请都链接到同一会议所必需的业务逻辑。
如果在所有邀请共享相同meetingId
时都可以进行一些持久性优化,那么没有什么可以阻止您检查存储库中的所有邀请并查看是否存在可能的优化。
例如,您可以循环所有邀请,并使用meetingId
按Map
对其进行分组。
答案 1 :(得分:1)
为什么不简单地这样做?
public class InvitationBulkRequest {
private final List<Invitation> invitations = new ArrayList<>();
private final MeetingId meetingId;
private final DateTime occurredOn
public InvitationBulkRequest(MeetingId meetingId, DateTime occurredOn) {
this.meetingId = Objects.requireNonNull(meetingId);
this.occurredOn = Objects.requireNonNull(occurredOn);
}
public void invite(User recipient) {
invitations.add(new Invitation(meetingId, occurredOn, recipient);
}
public void send() {
invitations.forEach(Invitation::send);
}
}
通过这种方式,您可以很好地封装所有邀请中的公共数据,并确保它们都是一致的。
答案 2 :(得分:1)
你确定你的设计是合理的吗?您可能正试图将略微偏斜的设计用于DDD说话。
我认为会议有一些不变量。也许我们需要至少2名参与者。与一个人会面相当乏味。现在我假设邀请一些与此相关。可能是邀请是用于通知参与者并跟踪接受的机制吗?
改变其中的一些内容可能会为您提供如何解决问题的线索。也许您可以Meeting
1- * Participant
由Invitation
表示,方式与Order
1- * Product
由OrderItem
表示的方式相同{1}}。