当前位置 : 主页 > 手机开发 > 其它 >

DDD,封装和分层架构:我的域名是否过于贫血?

来源:互联网 收集:自由互联 发布时间:2021-06-22
我目前正在使用.net和nhibernate为旅游行业的客户开发一些相当大的应用程序,并且我遇到了一些实施DDD的问题,以及团队内部就最佳方式进行分歧的问题.我希望有人可以提供一些指导. 目前
我目前正在使用.net和nhibernate为旅游行业的客户开发一些相当大的应用程序,并且我遇到了一些实施DDD的问题,以及团队内部就最佳方式进行分歧的问题.我希望有人可以提供一些指导.

目前,我们已经在域外实现了一个服务层,每个聚合根([EntityName]服务)都有一个服务.
所有其他层使用这些服务通过GetByThis()和GetByTheOther()等方法获取对聚合根的引用.我们从其他层调用域名都是通过这些服务进行的.

这些服务包含对存储库的注入引用(这些引用在其他地方都没有引用),并且还负责所有保存/更新行为和管理事务性.
服务方法的复杂性越来越高,有时候行为似乎属于域,就像条件创建逻辑一样(如果property = this set child对象,则为其他东西).
我们的域实体主要有简单的方法,如GetByThis()和HasAThing().我觉得我们正在失去域名的表现力.

我的主要问题是:

>服务层是否应包含如此多的逻辑?如果不是在哪里
它走了?如果域,应该聚合根持有引用
库?如果是的话,如何注入(进入工厂)
创建聚合根?)
>如何处理交易?
>实体(或聚合根)是否包含对域的引用
服务?如果是这样,他们应该如何获得参考?
>为了获得实体的新ID,我们必须
调用存储过程,我们将其包装在存储库中.哪里
你会参考这个吗?需要实体的一些复杂方法
创建许多子实体需要引用这个吗?

编辑

感谢@ david-masters和@ guillaume31精心设计的答案.

你帮助我解决了我感觉到的“臭味代码”.

首先,我应该说我们有一个(非常)传统的oracle DB来应对,因此Id生成要求(以及其他问题).

对于任何看过这个问题的人来说,这两个答案都提出了很好的建议,但对我来说,这是最好的建议:

“从务实的角度来看,我会问自己:如果我想参与我的Domain层的一部分并在另一个应用程序中重用它,它是否包含我在新应用程序中利用域所需的所有业务规则和行为?如果没有,那么可能意味着当前在应用程序端的一些部分需要移动到域层.“

考虑到这一点,我重新评估了我们的域和服务层,现在我相信我已经解决了我们的设计问题

Should the service layer contain so much logic? if not where should it
go?

我假设你在这里谈论Application层中的服务,而不是Domain层.看起来你的域名对象几乎是贫血的,有些人认为这是一种反模式,但对此有很多争论.

从务实的角度来看,我会问自己:如果我想参与我的Domain层的一部分并在另一个应用程序中重用它,它是否包含在新应用程序中利用域所需的所有业务规则和行为?如果没有,则可能意味着当前在应用程序端的某些部分需要移动到域层.

请注意,我在这里谈的是纯粹的域业务规则,而不是特定于应用程序的规则.例如,某些操作必须通过具有4个步骤的向导执行,在最后一步结束时要求用户进行确认,并且在最后一步之后将所有修改刷新到持久性存储中特定的业务规则,而不是域规则.因此,不应将它们移动到Domain层.

if the domain, should aggregate roots hold references to repositories?

IMO一个聚合根不应该拥有对它自己的存储库的引用,并且知道如何存储它自己,因为它打破了持久性的无知,并在域对象中引入了一个额外的责任,这将使它混乱.但是,聚合根可能偶尔会持有对另一个实体的存储库的引用.

How should transactionality be handled?

我会说有两种类型的交易:

>“应用程序”层中的“用户”事务,也称为“工作单元”.这些是跨越用例的高级事务,或者是Web应用程序中网页生命周期的最后一个事务(“在视图中打开会话”). ORM框架通常提供管理这些事务的工具.
>域层中的事务.它们可以由域对象或服务启动.例如:FundsTransferService.Transfer()可以在内部使用事务.在这里,您可以使用平台的基本事务处理.

Should entities (or aggregate roots) hold references to domain
services? If so, how should they obtain the references?

是的,实体有时可以调用域服务.域服务包含不属于任何实体的域规则和行为.您可以对这些依赖项进行硬编码或将它们注入实体中,具体取决于所需的分离级别.

In order to get a new id for an entity, we must call a stored
procedure, which we have wrapped in a repository. Where would you
reference this? Some complex methods on Entities which need to create
many child entities would need to reference this?

我不建议为按需访问的实体生成ID生成.正如大卫指出的那样,当你新建实体时,在语言层面生成一个Guid通常是个更好的主意.

如果您仍想要ID生成路由,则调用ID生成器通常是您的实体的Factory而不是实体本身的作业.

网友评论