如何使用 DDD 和 SRP 实现可维护且松耦合的应用程序?

Posted

技术标签:

【中文标题】如何使用 DDD 和 SRP 实现可维护且松耦合的应用程序?【英文标题】:How to implement a maintainable and loosly coupled application using DDD and SRP? 【发布时间】:2012-11-15 14:02:47 【问题描述】:

问这个问题的原因是我一直想知道如何将所有这些不同的概念拼接在一起。有很多关于 DDD、依赖注入、CQRS、SOA、MVC 的示例和讨论,但关于如何以灵活的方式将它们组合在一起的示例并不多。

我的目标:

    开发只需很少或无需修改即可独立运行的模块 更改或修改 UI 应尽可能简单(即 UI 应尽可能少做,并且“愚蠢” 使用记录在案的模式和原则

为了更容易提出具体问题,主要架构现在如下所示:

该示例显示了如何向员工添加注释。员工管理是一种有界上下文。 Employee 有几个属性,其中一个是ICollection<Note>

绑定上下文在我的理解中是分隔代码的逻辑位置。每个 BC 都是一个模块。大多数时候,我发现他们每个人都可以在需要时保证自己的 UI(即某些模块可能可用于 Windows phone)。

包含所有业务逻辑。

基础架构包含存储库实现,以及发送邮件、保存文件和不属于域的实用程序的服务。我正在考虑制作一些我必须在多个域(如发送电子邮件)中使用的通用服务功能,作为一种 API,我可以引用它来保存一些跨多个 BC 实现相同事物的代码。

查询层包含我在存储库中获取对象所需的除 GetById 之外的所有查询。查询层可以查询其他持久化实例,并且可能需要为每个 UI 更改一些。

Wcf 或 Web Api 是我的应用程序层,它可能属于基础设施而不是外部。该服务还设置了依赖关系,因此 UI 需要做的就是询问信息并发送命令。

该过程从蓝色箭头开始。阅读模型,因为它包含大部分信息。

在第 1 步中,本示例中的 EmployeeDto 只是一些员工属性,用于向用户显示他们需要记录的员工的相关信息(例如关于新体验的注释或类似内容)。

所以,问题是:

    实现这样的分层架构真的需要这么多的映射,还是我错过了什么? 是否推荐(甚至是智能)使用 Wcf 服务来运行这样的主逻辑(它实际上是我的应用程序服务) 在我的 UI 层中没有我的域对象的情况下,是否有 Wcf 的替代方案?

    这个实现有什么问题吗?有什么需要注意的坠落坑?

    你有什么好的例子可以推荐看看,可以帮助我理解所有这些概念应该如何协同工作。

更新: 我现在已经阅读了大部分文章(相当多的阅读),除了付费书籍(需要更多时间)。它们都是非常好的指针,将 Wcf 更多地视为适配器的方式似乎是对问题 2 的一个很好的回答。如果我打算走那条路,JGauffins 在他的框架上的工作也很有趣.

但是,正如在下面的一些 cmets 中提到的,我觉得其中一些示例倾向于推荐或实现事件和/或命令源、消息总线等。对我来说,现在就计划这种级别的扩展是过大的。与许多业务应用程序一样,这是一个“大量”(就内部应用程序而言,认为最多几千个)用户处理大量数据,而不是需要实现事件和命令的高度协作域队列通常与 CQRS 相关联来解决这个问题。

基于下面的答案,我将开始使用的方法将基于上面的模型和这样的答案:

    我只需要处理映射。利大于弊。

    我会将应用程序服务拉回基础架构并 将 Wcf 视为“适配器”

    我将使用命令对象并发送到应用程序服务。不是 用域对象污染我的域。

    为了降低复杂性,我尝试在没有事件/命令的情况下进行管理 采购、消息总线等。

此外,我只是想链接到this blog post by Udi Dahan 关于 CQRS,我认为这样的事情可以降低复杂性,除非真的需要它们。

【问题讨论】:

对问题 1 感兴趣:Is Layering Worth the Mapping?. 如果你很好地设计了你的 WCF 或 Web API(问题 2),它将是命令和查询的薄包装,并且几乎免维护。有趣的文章:Writing Highly Maintainable WCF Services @Steven 感谢您的文章!我会浏览它们。 我发现这非常有用 (msdn.microsoft.com/en-us/library/jj554200.aspx)。我认为这可能对您的一些问题有所帮助。 谢谢。您是否建议一直使用事件溯源?我已经阅读了很多关于它的内容,但是由于就许多人处理一小部分数据而言,这不是一个高度协作的领域——它更像是许多人处理一组相当大的数据,所以我发现Udi Dahan 的这篇文章在 CQRS 方面非常有趣:udidahan.com/2011/10/02/….../ 【参考方案1】:

实现这样的分层架构真的需要这么多映射,还是我错过了什么?

是的。问题是它不是同一个对象。它是同一对象的不同表示,但专门针对每个用例。视图模型包含更新 GUI 的逻辑,DTO 专门用于传输(可能会被规范化以简化传输)。等等等等。它们可能看起来一样,但实际上并非如此。

您当然可以尝试将所有的适配都放在一个类中,但是当您的应用程序增长时,使用这将不是很有趣。

是否推荐(甚至是智能)使用 Wcf 服务来运行这样的主逻辑(它实际上是我的应用程序服务)

您需要某种网络层。我不会让所有客户端应用程序都接触我的数据库。如果您弄乱了数据库架构(如果某些客户端仍在运行旧版本),则会造成维护噩梦。

通过使用服务器,维护版本差异要容易得多。

请注意,一旦使用 WCF 服务定义,就应将其视为常量。任何更改都应在新接口中定义(例如MyService2)。

如果我的 UI 层中没有我的域对象,是否有 Wcf 的替代品?

你可以看看我的框架。开始发帖:http://blog.gauffin.org/2012/10/writing-decoupled-and-scalable-applications-2/

这个实现有什么问题吗?

我看不到。看起来您已经很好地掌握了这些概念以及应该如何使用它们。

有什么需要注意的坠落坑吗?

不要对查询和命令偷懒。不要让它们更通用以适应多个用例。当应用程序增长时,它会回来咬你。较小的类更容易维护。

你有什么好的例子可以推荐看看,可以帮助我理解所有这些概念应该如何协同工作。

我链接的博客文章以及该系列中的所有其他文章。

【讨论】:

感谢您的回答。我看过你的帖子,真的很好。我什至稍微研究了你的框架。我真的很想了解更多关于您如何实现 DomainEventBase 和 ISubscribeTo 接口的信息,但它开始变得复杂。我将尽量远离事件和命令采购,并尝试使用 Udi Dahan 建议的 CQRS 方式:udidahan.com/2011/10/02/….../。不过,我可能会改变主意,如果我这样做了,我会更多地研究你的框架,它似乎非常适合这种用途。【参考方案2】:

    在映射和层之间存在权衡。存在某些映射的原因之一是适当的抽象不可用或不可行。因此,在层之间进行显式映射通常比尝试实现推断映射的框架更容易,但我离题了;这取决于对该问题的哲学讨论。

    WCF 或 WebAPI 服务应该非常精简。将其视为hexagonal architecture 中的适配器。它应该将所有内容委托给应用程序服务。术语服务的混淆会导致混淆。总体而言,WCF 或 WebAPI 的目标是使您的域“适应”特定技术,例如 HTTP。 WCF 可以被认为是在 DDD 术语中实现一个开放主机服务

    您提到了 WebAPI,如果您想要 HTTP,它是一种替代方法。最重要的是,要意识到这个适配层的作用。正如您所说,最好让 UI 依赖于 DTO,并且通常是使用 WCF 或 WebAPI 或其他任何东西实现的服务合同。这使事情变得简单,并允许您在不影响开放主机服务的消费者的情况下改变您的域的实现。

    您应该始终注意不必要的复杂性。分层是一种权衡,有时它可能是矫枉过正的。例如,在一个主要是 CRUD 的应用程序中,没有必要分层这么多。此外,如上所述,不要将 WCF 服务视为应用程序服务。相反,请将它们视为传输技术和应用程序服务之间的适配器。反过来,无论您的域是使用 DDD 还是事务脚本方法实现的,都将应用程序服务视为您域的外观。

    真正帮助我理解的是关于六边形架构的参考文章。这样,您可以将您的域视为核心,并围绕它进行分层,使您的域适应基础架构和服务。你所拥有的似乎已经遵循了这些原则。 Vaughn Vernon 的 Implementing Domain-Driven Design 是一个很好的、深入的资源,特别是关于架构的章节。

【讨论】:

谢谢。我将通读这些文章,但只需要澄清一个问题:应用程序服务是否应该是 WcfService 域的单一入口点?即应用程序服务应该“使用”域实体并发送电子邮件通知等,Wcf 只是将信息传递给正确的服务? 5 中的链接将我指向付费资源。你推荐它足够买吗? 是的,WCF 只是将所有内容委托给应用服务。 WCF 服务的职责是序列化、正确的 url 结构等。 这是一个付费资源,我推荐它。但是,我可能有偏见,因为我和作者互动过,因为他在写这本书,现在仍然是。我担任这本书的审稿人,每个月都会在当地的 DDD 聚会上亲自与作者交谈。要了解书中的内容,请查看here。 好的,感谢您提供新链接,并告知与本书和作者的关系。我明天看看。您提供的第一个链接是一个很好的解释。不过还是有疑问。如果 Wcf 服务不知道域对象(如 Note),而 DDD 应用程序服务不应该知道我在 WCF 服务中使用的 NoteWcfDto,那么 DDD 应用程序服务是否应该在每个方法中获取所有 note 属性(注意, Id、Title、Body、NoteType、...、EmployeeId)?或者我应该让 WCF 服务知道 Note(以及 DDD 对象 Note 的行为)?

以上是关于如何使用 DDD 和 SRP 实现可维护且松耦合的应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

DDD专栏6:建模演练:如何使用DDD来设计支付风控系统?

设计模式原则之:单一职责原则SRP

软件构造第六章第一节 可维护性的度量与构造原则

基于ABP实现DDD

软件设计原则

设计模式