领域驱动的设计理念



我在使用域驱动设计构建的应用程序中遇到了一些概念问题。

我有以下几层:

  • 应用程序
  • 基础设施

所以,让我们假设我有以下类:

  • Order
  • EmailService
  • OrderNotificationService
  • OrderApplicationService

显然,Order进入域层,OrderApplicationService进入应用层。EmailService是一种用于发送电子邮件的通用服务,在基础设施层中实现。OrderNotificationService是用于发送订单通知的特定实现。OrderNotificationService使用EmailService来发送实际的电子邮件。

所以,我的第一个问题是:OrderNotificationService会被实现为域服务、应用程序服务还是基础设施服务?

对于我的下一个问题,让我们假设以下对象:

  • Employee
  • SalesforceService

假设当一名员工被添加到系统中时,他们也应该被添加到Salesforce中。SalesforceService是一种使用Salesforce api注册用户的服务。SalesforceService是作为域服务还是应用程序服务将通过发送员工信息来使用的通用基础设施服务来实现?

谢谢你的建议。

这听起来不像DDD

你的问题中没有太多关于你的领域模型的内容。您提到了"员工被添加到系统和Salesforce中",但除此之外,我对您的应用程序的意图没有真正的了解,在商业意义上。我对"员工"one_answers"销售人员"的理解是基于对这些词的熟悉,而不是基于对它们在您的系统中的模型的解释。

我真的不知道"订单应用程序服务"在您的模型中应该代表什么。我收到"订单"one_answers"订单通知"。这看起来像是一个"客户"发出了一个"订单",一些"订阅者"得到了"通知",但这在很大程度上是基于我之前对设计模式的了解。

您应该能够描述业务域模型,因为如果计算机不存在,它会起作用如果你需要一个计算机术语来解释这个想法,它可能是混合了模型中外在的东西。

显然,您需要将应用程序逻辑与域逻辑集成,但这不应导致混淆您的关注点。然而,这是有办法的。

如何修复

  1. 您可以使用接口而不是特定于实现的类来为域层中的服务建模。这就是所谓的界面分离原理。您的模型的一部分包括在客户下订单时通知负责的员工,这是有道理的。然而,不一定有任何理由将此通知的实现与其建模目的相结合。在拥挤的办公室里,可能会有主管通过对讲机发布公告,也可能会有自动化系统发送电子邮件。这两个例子都有相同的商业目的。您可以有一个IOrderNotificationService接口,它只包含一个方法void NotifyOrderReceived(Order order);,而不是在域层中嵌入电子邮件服务。这样,您就可以在域层中获得所需的所有业务逻辑,而不会引入不必要的问题,如"电子邮件"实现或数据库的持久性。我猜是这样,但您的OrderNotificationServiceOrderApplicationService实际上是对同一事件的响应,但其中一个具有数据库依赖关系,另一个具有SMTP依赖关系。两者都是基础设施问题

在许多情况下,针对单个接口进行编码就足够了。但是,存在一个问题,即您的域对象现在依赖于注入的服务或全局变量。此外,这可能表明这些实体对其依赖性了解太多。客户可能不需要知道订单是如何处理的,只需要知道订单已经处理,他们就会收到订单。同样,员工可能不会加入Salesforce,因为他在成为员工之前并不知道这件事。他只知道他刚找到一份工作。

这个问题有一个很好的解决方案。。。

  1. 您可以使用"域事件"模式。基本思想是,域对象只知道刚刚发生了什么以及它们的状态是如何改变的,但不需要知道任何关于持久化或传播改变后的状态的信息。它们处理内部逻辑,然后引发"域事件"。这更类似于客户下订单,然后大喊"哟,我订购了这个产品!"或者员工被雇佣,然后大喊:"哟,我们找到工作了!"。采用这种方法将极大地简化您的域逻辑,并允许您实现更干净的关注点分离。Udi Dahan有一系列出色的博客文章,他在其中解释了利用域事件模式背后的逻辑,并提供了一个非常简单但非常有效的实现。以下是一些链接:

    • 如何创建完全封装的领域模型

    • 域事件-采用2个

    • 域事件-拯救

现在,如果你是那种喜欢在完全理解代码之前复制和粘贴代码的开发人员,我建议你从下面的第三篇文章开始。他们记录了乌迪·达汉对这种模式的思考和经验的演变。最合理的代码示例出现在第三篇文章中。理想情况下,你应该按顺序阅读这三篇文章,这样你就可以遵循他的逻辑,真正理解你如何从他的方法中受益,以及为什么它准确地代表了"领域驱动设计"。

作为"领域事件模式"的补充,埃里克·埃文斯(Eric Evans)在2009年对他自写下开创性的书以来对领域驱动设计的了解进行了回顾,他指出领域事件是他书中被严重忽视的核心构建块。他的演讲摘要可在此处查看,演讲链接可在此处获取。

其他一些可能帮助您成功应用此模式的资源包括Jimmy Bogard的文章《加强您的域:域事件》和Martin Fowler的文章《域事件》。Bogard从引用的链接中获得了其他有用的DDD博客文章的链接。

一般来说

如果你真的想尝试应用领域驱动的设计,那么通过真正了解你正在建模的内容以及你正在实现的目的,你会更成功。简单地使用一个您不熟悉的术语并将代码标记为这样并没有任何隐含的价值。DDD可能非常有用,但如果你不花时间理解为什么要做出区别和设计决策,你最终会得到很多不必要的抽象和普遍的困惑。

OrderNotificationService属于域层。如果您认为它是Order类的合作者,那么它应该位于Order类附近。IMO,我认为OrderNotificationService可以称为OrderNotificationer,其职责是在订单状态更改时发布通知。它显然是一个接口,而不是一个具体的类。它可以由属于基础结构层或应用程序层的OrderNotificationoinService来实现。没有必要使OrderNotificationService成为一个接口。

这同样适用于SalesforceService。将其视为Employee类的合作者,负责在创建员工时注册新员工信息。它属于域层。与前面的情况相同,您可以考虑将其重命名为EmployeeRegister或类似的名称,这些名称描述了它的角色而不是实现。而是用SaleforceService实现了它。

一个副作用是您的域对象(Order/Emplayee)间接地依赖于应用程序/基础结构层的类。如果域对象是由其他域对象实例化的,那么在创建域对象时,您可能会发现注入第三方依赖关系很难。这篇文章可能有用。http://thinkinginobjects.com/2012/09/05/abstract-factory-in-domain-modelling/

最新更新