数据访问层和 DDD
Data Access Layer and DDD
我正在尝试学习领域驱动设计的思想,并试图弄清楚我们应该把数据库持久性代码放在哪里。
阅读本书 "Implementing Domain Driven Design by Vaughn Vernon",我了解到存储库或数据库操作(连同连接等)必须保存在模型项目(域模型上下文)本身中。我的理解正确吗?
我指的是他的 IdentityAccessContext、AgilePMContext 等示例,其中有存储库的接口。我一直有一个想法,那就是拥有一个单独的数据层,在这里添加一个数据层会导致循环依赖。因为接口、实体是在数据层所需的模型中声明的,并且数据层需要从模型中引用以实现持久化。
这是正确的还是我遗漏了什么?
我不会说有数据访问层本身。
实际上,存储库使用数据映射器将域转换为数据,反之亦然。
模型、服务或事务脚本必须使用存储库来获取和保留对象。这里没有循环引用:
- 服务层注入repository/repositories来实现域操作。也就是说,如果服务需要 objects 并且它不关注如何将它们转换为数据,它就会知道该怎么做。
- 存储库使用数据映射器来保存和获取数据 (1).
- 数据映射器通常是 OR/M,例如 Entity Framework、NHibernate、Dapper...
此外,一个好的 DDD 将执行 inversion of control,这意味着:
- 上层了解下层。
- 下层不拥有对上层的任何引用。
总而言之,DDD 并没有你想象的 DAL,但它试图抽象和封装每个关注点,以便让上层与底层数据方法解耦。
关于循环引用的事情...
OP 说:
Because the Interfaces, Entities are declared in Model which is
required for the Data layer and the Data layer needs to be referred
from model for persistence.
仔细检查你的陈述。如果模型代表比数据更高的抽象层,为什么数据应该引用模型?
需要数据的是模型。并且应该使用接口来访问数据,以使模型与数据访问策略无关。 这叫做 inversion of control,正如我之前在这个回答中所说的那样。
也许您认为数据层需要对实体的引用,因为您仍在思考旧方法,但如果您实践良好的关注点分离并使用数据映射器(或者您自己实现它) ,数据映射器只是将对象映射到原始数据,反之亦然,它不需要像域对象那样引用具体的 classes(你应该问问自己 Entity Framework 如何持久化您的实体到您最喜欢的数据库引擎,甚至不知道这些实体 (2)).
无论如何,正如我之前在这个答案中所说的那样,您不应该考虑 DAL,而应该考虑一堆存储库和数据映射器,或者只是存储库(参见 exception (1)).
通常使用依赖注入模式(inversion of control的可能实现)实例化依赖的下层并提供给上层。
(1) 这条规则的例外:如果我们不是在谈论关系存储,可能根本就没有数据映射器,存储库通过访问abstraction/encapsulation 级别较低的数据。例如,如果存储库实现其在 XML 中存储数据的操作,那么它将在内部使用 XDocument 或 XmlDocument,根本没有实际的数据映射器。
(2) 实际上 OR/M 像 Entity Framework 这样的框架可以通过配置知道你的模型怎么样。虽然它不需要引用您的具体域来编译,但它需要您使用 Code First 或其他方法提供 class 映射)
Going through the book "Implementing Domain Driven Design by Vaughn
Vernon", I understand that the Repository or the Database operations
(along with connections etc.,) have to be kept in the Model project
(Domain Model Context) itself. Is my understanding correct?
存储库抽象(接口)应该在域层,但它们的具体实现在基础设施层。
查看他的本书代码示例:https://github.com/VaughnVernon/IDDD_Samples/tree/master/iddd_agilepm/src/main/java/com/saasovation/agilepm,例如 domain/model 中的 TeamRepository
接口和 port/adapter/persistence 中的 LevelDBTeamMemberRepository
。
没有循环引用,因为持久性与域强耦合,但域仅在需要时才与持久性松散耦合(多亏了控制反转,大部分时间通过依赖注入实现)。
我正在尝试学习领域驱动设计的思想,并试图弄清楚我们应该把数据库持久性代码放在哪里。
阅读本书 "Implementing Domain Driven Design by Vaughn Vernon",我了解到存储库或数据库操作(连同连接等)必须保存在模型项目(域模型上下文)本身中。我的理解正确吗?
我指的是他的 IdentityAccessContext、AgilePMContext 等示例,其中有存储库的接口。我一直有一个想法,那就是拥有一个单独的数据层,在这里添加一个数据层会导致循环依赖。因为接口、实体是在数据层所需的模型中声明的,并且数据层需要从模型中引用以实现持久化。
这是正确的还是我遗漏了什么?
我不会说有数据访问层本身。
实际上,存储库使用数据映射器将域转换为数据,反之亦然。
模型、服务或事务脚本必须使用存储库来获取和保留对象。这里没有循环引用:
- 服务层注入repository/repositories来实现域操作。也就是说,如果服务需要 objects 并且它不关注如何将它们转换为数据,它就会知道该怎么做。
- 存储库使用数据映射器来保存和获取数据 (1).
- 数据映射器通常是 OR/M,例如 Entity Framework、NHibernate、Dapper...
此外,一个好的 DDD 将执行 inversion of control,这意味着:
- 上层了解下层。
- 下层不拥有对上层的任何引用。
总而言之,DDD 并没有你想象的 DAL,但它试图抽象和封装每个关注点,以便让上层与底层数据方法解耦。
关于循环引用的事情...
OP 说:
Because the Interfaces, Entities are declared in Model which is required for the Data layer and the Data layer needs to be referred from model for persistence.
仔细检查你的陈述。如果模型代表比数据更高的抽象层,为什么数据应该引用模型?
需要数据的是模型。并且应该使用接口来访问数据,以使模型与数据访问策略无关。 这叫做 inversion of control,正如我之前在这个回答中所说的那样。
也许您认为数据层需要对实体的引用,因为您仍在思考旧方法,但如果您实践良好的关注点分离并使用数据映射器(或者您自己实现它) ,数据映射器只是将对象映射到原始数据,反之亦然,它不需要像域对象那样引用具体的 classes(你应该问问自己 Entity Framework 如何持久化您的实体到您最喜欢的数据库引擎,甚至不知道这些实体 (2)).
无论如何,正如我之前在这个答案中所说的那样,您不应该考虑 DAL,而应该考虑一堆存储库和数据映射器,或者只是存储库(参见 exception (1)).
通常使用依赖注入模式(inversion of control的可能实现)实例化依赖的下层并提供给上层。
(1) 这条规则的例外:如果我们不是在谈论关系存储,可能根本就没有数据映射器,存储库通过访问abstraction/encapsulation 级别较低的数据。例如,如果存储库实现其在 XML 中存储数据的操作,那么它将在内部使用 XDocument 或 XmlDocument,根本没有实际的数据映射器。
(2) 实际上 OR/M 像 Entity Framework 这样的框架可以通过配置知道你的模型怎么样。虽然它不需要引用您的具体域来编译,但它需要您使用 Code First 或其他方法提供 class 映射)
Going through the book "Implementing Domain Driven Design by Vaughn Vernon", I understand that the Repository or the Database operations (along with connections etc.,) have to be kept in the Model project (Domain Model Context) itself. Is my understanding correct?
存储库抽象(接口)应该在域层,但它们的具体实现在基础设施层。
查看他的本书代码示例:https://github.com/VaughnVernon/IDDD_Samples/tree/master/iddd_agilepm/src/main/java/com/saasovation/agilepm,例如 domain/model 中的 TeamRepository
接口和 port/adapter/persistence 中的 LevelDBTeamMemberRepository
。
没有循环引用,因为持久性与域强耦合,但域仅在需要时才与持久性松散耦合(多亏了控制反转,大部分时间通过依赖注入实现)。