Domain driven design 无法将真实世界的逻辑放入DDD域层

Domain driven design 无法将真实世界的逻辑放入DDD域层,domain-driven-design,Domain Driven Design,尽管我已经研究了领域驱动设计很长一段时间了,但我仍然有一些基础知识需要我去理解 似乎每次我试图设计一个丰富的域层,我仍然需要大量的域服务或一个厚的应用层,我最终得到的是一堆近乎贫乏的域实体,除了“GetTotalAmount”之类的东西之外,它们没有真正的逻辑。关键问题是实体不知道外部的东西,将任何东西注入实体都是不好的做法 让我举几个例子: 1。用户注册一项服务。将用户持久保存在数据库中,生成并保存一个文件(用户帐户需要),并发送一封确认电子邮件。 确认电子邮件的例子在其他线程中已经过大量讨论

尽管我已经研究了
领域驱动设计
很长一段时间了,但我仍然有一些基础知识需要我去理解

似乎每次我试图设计一个丰富的
域层
,我仍然需要大量的
域服务
或一个厚的
应用层
,我最终得到的是一堆近乎贫乏的域实体,除了“GetTotalAmount”之类的东西之外,它们没有真正的逻辑。关键问题是实体不知道外部的东西,将任何东西注入实体都是不好的做法

让我举几个例子:

1。用户注册一项服务。将用户持久保存在数据库中,生成并保存一个文件(用户帐户需要),并发送一封确认电子邮件。

确认电子邮件的例子在其他线程中已经过大量讨论,但没有真正的结论。一些人建议将逻辑放入
应用程序服务
,该服务从
基础架构层
注入
电子邮件服务
文件服务
。但是我会有领域之外的业务逻辑,对吗?其他人建议创建一个
域服务
,注入
基础设施服务
,但在这种情况下,我需要在
域层
IEmailService
IFileService
)中提供
基础设施服务
的接口,这看起来也不太好(因为
域层
无法引用
基础结构层
)。其他人建议实施,然后让EmailService和FileService订阅这些事件。但这似乎是一个非常松散的实施-如果服务失败会发生什么?请告诉我您认为什么是正确的解决方案

2.从数字音乐商店购买歌曲。购物车被清空。购买被保留。调用支付服务。发送电子邮件确认。

好的,这可能与第一个示例有关。这里的问题是,谁负责协调此事务?当然,我可以将所有内容都放在带有注入服务的MVC控制器中。但是如果我想要真正的DDD,所有业务逻辑都应该在域中。但是哪个实体应该具有“Purchase”方法?
Song.Purchase()
Order.Purchase()
OrderProcessor.Purchase()
(域服务)?
ShoppingCartService.Purchase()
(应用服务?)

在这种情况下,我认为很难在域实体内部使用真正的业务逻辑。如果向实体中注入任何东西都不是好的做法,那么他们除了检查自身(及其聚合)的状态之外,还能做其他事情吗


我希望这些例子足够清楚地说明我正在处理的问题。

您的大部分请求都与面向对象设计和责任分配有关,您可以想到,并且,您可以从面向对象设计书籍中受益,推荐以下内容

一个用户注册了一项服务。该用户将保留在 数据库,生成并保存一个文件(用户帐户所需), 并发送确认电子邮件

您可以在此处应用。定义如下域接口:

void ICanSendConfirmationEmail(EmailAddress address, ...)

接口可由其他域类使用。在基础结构层中使用真正的SMTP类实现此接口。在应用程序启动时注入此实现。这样,您在域代码中声明了业务意图,并且您的域逻辑没有直接引用SMTP基础结构。这里的键是接口的名称,它是应该基于无处不在的语言

从数字音乐商店购买歌曲。购物车 已清空。将持久化购买。将调用支付服务。 已发送电子邮件确认。好的,这可能与第一个示例有关。这里的问题是,谁负责协调此事务

使用OOP最佳实践来分配职责(把握和巩固)。单元测试和重构将为您提供设计反馈。编排本身可以是应用程序层的一部分。来源:

应用层:定义软件应执行的作业并指导 用于解决问题的表达域对象。此层的任务 负责对业务有意义或必要的 与其他系统的应用程序层的交互

此层保持精简。它不包含业务规则或 知识,但仅协调任务和学员工作 下一层中域对象的协作。它不会 有反映业务情况的状态,但也可以有状态 这反映了用户或程序的任务进度


Dimitry的回答指出了一些值得寻找的好东西。通常/很容易,您会发现自己置身于场景中,通过不同的层将数据从db铲到GUI

吉米·尼尔森的简单建议“价值对象、价值对象和更多价值对象”启发了我。通常,人们倾向于关注名词并将其建模为实体。很自然,您在查找DDD行为时会遇到困难。动词更容易与行为关联。一个好办法是让这些动词作为值对象出现在您的域中

在尝试开发域时,我为自己提供了一些指导(必须说,构建一个丰富的域需要时间,通常需要多次重构迭代……):

  • 最小化属性(获取/设置)
  • 尽可能多地使用值对象
  • 尽可能少地公开。让你更直观
别忘了,你的域名可以通过验证变得丰富。它只是你的域名
void ICanNotifyUserOfSuccessfulRegistration(EmailAddress address, ...)