在ddd建立多对多的关系

时间:2017-09-24 18:23:52

标签: domain-driven-design

我有两个模型:UserRoom。用户属于许多房间,房间有很多用户 目前我有

public class User extends BasePersistable {

  private static final long serialVersionUID = 1492535311821424305L;

  @Column(nullable = false, unique = true)
  private String login;

  @Column(nullable = false)
  private Integer uid;

  @ManyToMany(targetEntity = Room.class)
  @JoinTable(name = "room_users", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "room_id")})
  private Set<Room> rooms = new HashSet<>();

public class Room extends AbstractAggregateRoot implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @JsonIgnore
  private Long id;

  @ManyToMany(mappedBy = "rooms", targetEntity = User.class, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
  @JsonIgnore
  private Set<User> users = new HashSet<>();

当用户登录时,我找到或创建他,然后根据一些规则为他找到或创建房间。
对我来说,有两点非常清楚:
1)房间有很多消息,没有房间就不存在消息,因此,房间是聚合根 2)用户需要在房间范围之外进行操作,因此,用户也应该是聚合根。

所以这就出现了问题:我已经知道聚合根不会引用另一个聚合根(只能通过值对象)。聚合根应该包含它需要存在的所有内容,而不依赖于外部源(在本例中为User aggregate root)。

如何在它们之间创建这种关系?用户登录后,如何创建我需要为他创建的房间?我以为我可以发布一个事件然后根据这个(UserCreatedEvent)创建房间......我是朝着正确的方向吗?

2 个答案:

答案 0 :(得分:2)

是的,你的方向正确。

对于跨多个聚合的流程,您可以使用Saga/Process manager。该组件通过监听相关事件(即UserCreatedEvent)并将命令发送到相关聚合来工作。在您的情况下,Saga会为每个需要创建的房间发送一个或多个CreateRoom命令。

您应该记住,此过程最终是一致的,即从发出事件到发送命令的时间有延迟。

答案 1 :(得分:1)

我看到它的方式,用户不在任何房间。但他可能会订阅接收在一个房间发布的所有消息。订阅可以创建为用户和房间之间的中介。他们可以用完,然后删除。他们携带的唯一信息是与房间和用户的关系(可能是有效间隔),他们没有id,因为他们只是这个,价值对象。

如果你想避免用户引用房间,但是中间有一个值对象,那么订阅可能会为你做到这一点。