8

我了解聚合根的概念,并且我知道一个聚合根必须通过身份引用另一个(http://dddcommunity.org/wp-content/uploads/files/pdf_articles/Vernon_2011_2.pdf)所以我不明白的是如何强制实体框架在两个聚合之间添加外键约束?

假设我有一个简化的域:

public class AggregateOne{
    [Key]
    public Guid AggregateOneID{ get; private set;}
    public Guid AggregateTwoFK{get; private set;}
    /*Other Properties and methods*/
}

public class AggregateTwo{
    [Key]
    public Guid AggregateTwoID{get; private set;}
    /*Other Properties and methods*/
}

通过这种域设计,Entity Framework 不知道 AggregateOne 和 AggregateTwo 之间存在关系,因此生成的数据库中没有外键。

4

2 回答 2

8

在 DDD 中,EF 不存在。域关系与数据库关系不同。不要试图将 EF 与域建模混合使用,它们不能一起工作。所以简而言之,你所拥有的不是 DDD,只是伪装成 DDD 的普通旧关系数据库。EF 将由存储库使用,并且会关心持久化一个聚合根 (AR)。

两个 AR 可以一起工作,但是您需要根据领域对流程进行建模。EF 充当应用程序的数据库,它关注持久性问题,不应该关心域。持久性是关于存储而不是反映域关系(EF实体不是域实体,尽管它们可以具有相同的名称并且可以看起来相似。重要的细节是它们属于不同的层并处理不同的问题)。域存储库只关心以一种在 AR 发生变化时可以轻松恢复的方式来持久化 AR。如果需要将更多的 AR 持久化在一起,请接受最终的一致性并学习如何使用服务总线和 sagas。它将大大简化您的生活(将其视为工作单元模式的一种实现)。

对于查询,最简洁和优雅的方式是生成/更新适合查询用例的读取模型,这通常是在域事件告诉“世界”域中发生了某些变化之后完成的。

正确地做 DDD 并不简单,而且很容易掉入陷阱,认为您应用 DDD 而实际上您只是在使用 DDD 术语进行 CRUD 处理。如果您喜欢轻松的生活,那么 IMO CQRS 也是 DDD 的必备品。

理解领域而不匆忙和肤浅,识别有界上下文,对领域概念及其用例进行建模(非常重要!!!),根据需要定义存储库接口,并仅在没有其他需要时才实施存储库做(真正的存储库,同时您可以使用像内存存储库中的假存储库 - 它们实现起来非常快,并且您的应用程序被解耦意味着它不应该关心持久性是如何实现的,对吧?)。我知道这听起来很奇怪,但这就是你知道你有一个可维护的 DDD 应用程序的方式。

最后实现存储库的目的是将应用程序与持久性细节真正分离,并定义应用程序对持久性的期望(存储库方法)。定义后,您可以编写测试 :D 然后实现存储库。好处是你可以只关注 repo 的实现是隔离的,当所有的测试都通过时,你就知道一切正常。

于 2013-10-16T20:09:56.927 回答
0

为什么你应该有两个完全不同的对象?为什么不仅仅通过域接口将实体公开为域对象?在这种情况下,让您的实体也充当域对象,并将其实现细节巧妙地隐藏在接口后面,这没有问题。

另一个用 EF 表示聚合根的巧妙方法是确保外键列也构成依赖实体的主键。在您的情况下,这意味着 AggregateOneId 和 AggregateTwoFk 一起将形成 AggregateOne 的复合主键。这将确保 EF 不需要用于从 AggregateOne 中删除实例的存储库,只要它已从 AggregateTwo 的集合中删除,它将被正确标记为从数据库中删除(如果您没有这样的密钥,则需要将其从设置 AggregateOne 是因为 EF 会抛出异常,不理解开发人员应删除 AggregateOne 的意图。

于 2016-09-20T21:19:29.010 回答