通过id

时间:2019-05-28 15:08:14

标签: foreign-keys domain-driven-design aggregateroot

最佳做法是通过ID而不是通过引用来引用另一个聚合根(请参阅第359页及之后的“实现域驱动的设计”)。

// direct reference
class AggregateRootA(val b: AggregateRootB)

// reference by ID (preferred)
class AggregateRootA(val b: AggregateRootBId)

class AggregateRootB(val id: AggregateRootBId)
class AggregateRootBId(val id: Long)

这种去耦的中心论点是,每个聚合根都应该是事务边界。

我想知道在代码中使用id引用时在数据库中使用外键关系是好是坏的主意。由于没有引用记录AggregateRootA就不可能有AggregateRootB的数据库记录,因此该约束将在数据库级别上实现一致性。这本质上就是我想要的,因为否则该对象将无效。

除了稍微麻烦一些的测试之外,在这里使用外键是否有不利之处?

2 个答案:

答案 0 :(得分:2)

  

除了稍微麻烦一些的测试之外,在这里使用外键是否有不利之处?

该约束引入了一些时间耦合;除非外键实际上可用,否则数据库将不允许写操作,这会在两者之间引入“先发生”的关系。

因此,如果您有一个真正的业务问题,关于B的簿记必须在簿记将A链接到B之前进行,那么在数据库中强制执行该约束就可以了。 / p>

但这不是普遍关注的问题,如果信息并非总是以自然顺序到达,那么外键约束可能会很痛苦。

简而言之,请仔细衡量拥有约束的成本以及拥有约束的好处。

答案 1 :(得分:1)

此时间太长,无法添加为评论,但我只是添加到@VoiceOfUnreason已回答的内容中,因此请接受该答案,而不是这个:)

只是想学究点:您指的是DRI(声明性参照完整性),因为甚至可以禁用外键关系。

因为DRI具有相当有效的用途,所以它本质上没有错。当然,在相同的有界上下文中,这与在同一数据库中的数据最有意义。对于不同的BC,您可以完全放弃执行关系,而仅对非规范化数据进行处理,或者通过将值对象存储在BC的数据存储中来强制执行关系。来自“外来” BC的聚合将是您的 BC中的VO。在这种情况下,@ VoiceOfUnreason提到的所有内容都会发挥作用。在某些情况下,确保状态的某些更改以合理的顺序发生可能是非常有意义的。例如,您可能希望在激活电话线路之前注册客户。

对于规则较不严格的情况,您可以采用弱关系进行处理,并可能以指示相关数据是否正在等待其他位完成的状态来跟踪过程,这是典型的异步/并行处理范例。