◆ 介绍
在这篇博文中,我将介绍整洁架构(Clean Architecture ),它是一种现代、可扩展的正式软件架构,适用于现代 Web 应用程序。接下来,我将讨论DDD(领域驱动设计)如何适应这幅图景,以及 DDD 概念如何与清洁架构完美契合,从而产生一种称为清洁 DDD 的方法。最后,我介绍了命令查询职责分离 (CQRS),并描述了它如何补充和增强 Clean DDD 解决方案,以创建优雅、健壮、可扩展和可测试的软件系统。
◆ 清洁架构
清洁建筑是一种相对“现代”的正式建筑,因为它不到十年的历史。它随着时间的推移从其他几种架构演变而来,包括六边形架构、端口和适配器以及洋葱架构。 在这篇文章中,Bob 大叔强调了所有前身架构和清洁架构都具备的五个品质:
Clean Architecture 可以被可视化为一系列同心圆,每个圆代表应用程序的不同层。使架构结合在一起的原则称为Dependency Rule:
“使这个架构工作的最重要的规则是依赖规则。这条规则说源代码依赖只能指向内部。内圈中的任何东西都无法知道外圈中的某物。特别是某物的名称内圈中的代码不得提及外圈中声明的内容。这包括函数、类、变量或任何其他命名的软件实体。
这是一个很好的起点,但我想进一步详细说明。清洁架构应用程序中的层:
整洁架构的显着特征是组成它的同心层围绕着一个包含抽象和业务逻辑的中央核心。这些抽象的实现,连同它们的外部依赖,被推到外层。在描述这种布局时,我喜欢使用“外围”的术语来指代外部实现层,而“核心”来指代内部层。请注意,这是我的命名约定,此时您不太可能在其他任何地方找到它。
同样,我想强调一下我们如何明确地使用依赖倒置原则来确保内部层(纯逻辑和抽象)永远不会有任何外部层(实现)的知识。内部层使用这些层中定义的抽象,而实际的实现逻辑存在于外部层中。重申一下:依赖倒置原则指出细节依赖于抽象;抽象不依赖于细节。因此,我们正在区分什么是本质(核心)和什么是细节(外围)。使用依赖注入,通常通过控制容器的反转,所有内容都在完成的解决方案中结合在一起。
我对 Clean DDD 的解读
整洁的领域驱动设计代表了软件架构开发的下一个合乎逻辑的步骤。这种方法源自Blob的原始架构,但在概念上略有不同。相同之处在于它在较高级别使用相同的同心层方法,但是使用域驱动设计来构建内核。此外,DDD 推动将域分离为不同的有界上下文也为这种设计提供了信息,因为这些有界上下文现在成为堆栈每一层水平分离的指南。这是一个真正的、现代的、以领域为中心的模型,用于构建和交付复杂的业务应用程序。
域层是核心中最中心的层。该层是使用 DDD 原则构建的,其中没有任何内容对它之外的任何内容有任何了解。在大多数情况下,这里不使用依赖注入,尽管事件调度程序实现可能会出现罕见的异常。领域层中的领域服务和其他业务逻辑甚至不需要真正位于接口后面,因为该逻辑不太可能随着时间而改变,并且不需要多态性。在使用接口确实有意义的领域领域,例如使用策略模式来封装不同的业务逻辑,继续使用它们;否则,只需将域服务直接注入需要它们的类中。
应用层非常重要,因为它基本上是将领域层与外层绑定的“粘合剂”。它几乎就像一个中间层。应用层声明了代表基础设施、持久性和表示组件的接口和其他抽象。这些组件的实际实现不在这一层中声明,而是通过依赖注入提供给应用程序组件。
该层还负责编排:它实现了操作域对象和启动域工作流的高级逻辑。这样,它本身不包含任何一流的业务逻辑,而是通过对领域层的调用来组织该逻辑。它可以协调任务并将工作委托给域,但它不包含业务规则或维护业务状态。
应用层同样使用注入的持久化接口执行持久化操作。这就是存储库模式或 CQRS 发挥作用的地方(解释如下)。由于不同的编排操作,它将数据传输对象(DTO) 传递到表示层。同样,它还使用注入的基础设施接口与操作系统和其他外部资源进行通信。
持久层包含 应用层中声明的持久性接口的实现。它还包含专门的持久性模型(数据访问)类,这些类可能是也可能不是数据库表的镜像(特别是如果您使用对象关系映射器,又名 ORM),或者可能代表数据库查询的投影。这是对数据库进行实际读/写的所有硬逻辑所在的位置。
基础设施层包含应用程序层中声明的基础设施接口的实现。这里没有太多要说的,因为它是你所期望的:它封装了与操作系统、外部 API 等通信的逻辑。与外部消息队列通信的实现细节以及通信的服务都在这里与任何其他外部机构。
表示层是一个 API 层,它汇集了所有应用程序层组件,并将它们注入适当的实现(通常使用 IOC 容器)。在我的解释中,这一层不是用户界面(UI),而是呈现 UI 与之通信的外观。在 Web 应用程序中,表示层是一个 MVC 应用程序,它使用 Web 协议(如 REST、GraphQL 或 Web 套接字)与 UI 通信。展望未来,当我谈到 MVC 控制器时,要知道我总是将它们称为表示层组件。
现在,您需要注意一些事情。我研究过的一些资料将 Web API 视为系统的应用层。换句话说,应用层和表示层似乎是一回事。我强烈不同意这一点。应用程序层是它自己的动物,如果需要,您应该始终能够将其与表示逻辑分离。
用户界面是该架构中绝对最高的概念层。这是用户直接与之交互的代码。一些示例可能是 Angular 或 React 等,它们在用户的 Web 浏览器中运行,或者使用 Windows Presentation Foundation (WPF) 构建的桌面应用程序。一些消息来源将其与表示层混为一谈,但我认为将其分开很重要,至少在 Web 应用程序中是这样。如果架构良好,您的系统应该能够毫不费力地移除 UI 并用不同的 UI 替换。
公共层是一个库或一组库,用于横切关注点,例如日志记录、文本操作、日期/时间算法、配置等,它们对整个系统都是全局的. 公共层中的组件和接口可以在堆栈的任何层中使用(UI 除外,它可能完全断开连接,在 Web 应用程序的情况下,完全在用户的浏览器中运行)。公共层包含组件和功能的实现细节,这些细节足够通用,可以在应用程序的任何地方使用。在这一点上,这里绝对不应该有任何业务逻辑或与域有关的任何事情。
努力防止这一层膨胀失控。当怀疑某个东西是否属于公共层时,想想自己,这个组件是否可以在完全不同的软件系统中重用,甚至可以放入可重用工具包中?如果答案是“否”,那么您真的需要考虑它是否是一个横切关注点,或者它是否属于系统的另一部分。
CQRS 代表 Command/Query Responsibility Segregation,这是一件很棒的事情。这是一种架构设计模式,它允许更高级别的层(例如表示层)与其他层(例如应用层)进行通信 - 例如,表示层内的控制器将调用由应用程序执行的命令和查询层组件。CQRS 与 Clean Domain-Driven Design 完美契合,因为它是一种行为模式:Clean DDD 是什么,CQRS 是如何。
在我深入挖掘之前,我想明确一点,你不需要使用 CQRS 来实现 Clean Architecture 或 Clean DDD 解决方案,但你为什么不使用它呢?为了争论,另一种方法可能是将您的编排逻辑封装在应用程序层服务中,这些服务直接注入您的控制器中。一切都很整洁,并尊重依赖倒置原则。但是,您已经丧失了 CQRS 提供的好处,因为它抽象了跨层边界的组件之间的通信过程本身。CQS 原则指出:
◆ CQS 自身
CQS 可以直接在 Clean DDD 解决方案中实施。为此,您通常会将控制器方法分解为命令/查询(即写入/读取)操作,并且永远不会违反两者之间的分隔。目的是创建一个基于任务的界面,它处理行为,而不仅仅是保存数据或执行其他 CRUD 操作。请注意:这是 CQS 和 CQRS 与 DDD 相交的地方——操作本身通常会使用您正在使用的有界上下文的普遍语言以业务流程命名. 请注意,这种简单、优雅的设计也促进了单一职责原则,因为每个操作都更具凝聚力,并且更好地对应 UI 操作。这促进了接近用户体验 (UX) 驱动开发的开发过程。
CQS 的反面是我经常看到的反模式:命令和查询之间的零分离。在这种非设计中,没有真正的方法来理解给定操作的副作用是什么,因为总是有一些 Rube Goldberg 逻辑在后台运行。这在已有 10 多年历史的遗留应用程序中很常见,但许多应该更了解的职业开发人员仍然以这种方式构建解决方案。另一种常见的反模式是在控制器(Web API)上公开 CRUD 操作,然后业务逻辑分散在整个应用程序中,例如在 UI 本身中或更糟的是在存储过程中的数据库中。这会产生迷宫般的、脆弱的解决方案,在极其荒谬的程度上违反了开放/封闭原则
◆ CQRS 是 CQS+
命令查询职责分离,是什么?CQRS 接受命令和查询并将它们转换为一流的对象。使用 CQS、基于任务的接口的解决方案可以很容易地重构为 CQRS,因为逻辑分离已经存在。两种模式的最大区别在于 CQS 中的命令/查询是方法;在 CQRS 中,模型. 这里的区别很重要。通过将操作视为模型,您将在应用程序中引入令人难以置信的可扩展性。这与 .NET 团队在过去引入任务并行库 (TPL) 时所做的没有什么不同:他们采用了在应用程序中开发异步控制流的繁重过程,并将所有这些抽象为一流的对象,可以独立于依赖它们的代码进行处理。
以下是使用 CQRS 的几个明显好处:
◆ 怎么运行的
在较高级别上,命令/查询在表示层(在控制器操作内部)中实例化并与应用层通信,然后应用层执行业务编排逻辑并执行您感兴趣的高级任务。
一种方法是将命令/查询参数和处理它们的逻辑都放在同一个对象中。该对象使用依赖注入注入每个控制器或使用某种工厂创建。在命令/查询对象上调用Execute()方法并检索结果。我不喜欢这个。
实现 CQRS 的更好方法是将命令/查询与其处理程序分开,并利用进程内消息传递服务将命令/查询对象分派给它们各自的处理程序。这样做有很多好处,但有两个明显的好处是:
◆ 命令
CQRS 命令总是以现在命令式命名——例如RegisterEmployeeCommand。命令会改变系统状态并返回简单的 ack/nack 或元数据响应,或者它们会抛出异常。它们与域事件的不同之处在于可以拒绝命令;事件不能。命令通常通过应用层与域层交互。这很重要,因为域层包含所有业务逻辑并负责使系统保持一致状态。构成命令的属性应尽可能接近第 3 范式 [Greg Young, CQRS Documents ]。如果你对 1NF/2NF/3NF 之间的区别感兴趣,这篇 Quora 帖子很好地解释了它. 最后,命令通常需要是幂等的。
◆ 查询
CQRS 查询也以现在时命名,通常以“Get”开头——例如 GetEmployeeListQuery。查询不会改变系统状态,它们只是返回数据,通常以数据传输对象的形式。返回的 DTO 上的属性的结构接近于第一范式,因为数据可能会从非规范化的数据库查询中返回,并且返回的 DTO 的结构通常会匹配用户的屏幕或某些可由用户使用的规范模型任何客户。
在他的原始规范中,我研究过的大多数专家都同意这一点,Greg Young 指出大多数时候查询应该绕过域层。让我们进一步解开它。为什么我们要直接从应用层传递到表示层?首先,数据模型比用户输入更可靠,并且假设始终是一致的,因此不需要进行验证的数据库。其次,查询不会改变状态,因此进行这种操作的业务域逻辑没有用处。第三,在系统中读取(查询)将比写入(命令)更频繁地发生,因此它们需要快速高效。
命令和查询的其他注意事项:
◆ 整洁 DDD + CQRS
一切都导致了这一点。展望未来,我将使用它作为 Web 应用程序开发的主要架构方法,这也是我在演示应用程序中使用的方法。重申一下,高级架构基于清洁架构原则,在系统的同心层之间具有明确的概念分离。
再一次,没有灵丹妙药,那么有哪些利弊呢?
优点
缺点
◆ 高级主题
首先,命令与查询的分离允许您将模块一直拆分到数据库。在极端架构中,可能有一个仅用于命令的数据库和一个或多个仅用于读取的单独数据库。此外,可以对读取的数据库进行非规范化,这可以极大地提高性能和可伸缩性。我目前无意在演示应用程序中实现这种架构。
CQRS 的极端逻辑结论导致了一种称为事件溯源的架构模式,这本质上意味着状态数据不存储在命令数据库中,而是一系列事件,这些事件使数据从一些基本的初始化状态发生了变异。通过“重放”事件,可以获得数据的快照,这使您可以从任何时间点获取数据的状态。此快照可以通过最终一致性随时间同步到读取数据库,或其他一些复制模式。这种方法可能适用于某些高级应用程序,例如金融应用程序。出于我们的目的,我可以拥有一个单一的数据库而不是尝试实现事件溯源。您为实现这种高级架构模式付出的代价是显着增加了复杂性。最后,我研究过的大多数专家都同意 CQRS 可以在不使用事件溯源的情况下提供巨大的好处。这是我建议您谨慎行事的另一个领域,因为这些高级模式不适合胆小的人。
◆ 结论
在这篇博客文章中,我介绍了 Clean Architecture,它是一种一流的架构,它随着时间的推移从其他几种架构方法发展而来,并首先由 Bob 正式化。然后我讨论了领域驱动设计如何与 Clean Architecture 结合以产生 Clean DDD,这是一种架构方法,它将 DDD 的方法论和以业务为中心与 Clean Architecture 的逻辑分离相结合,以产生优雅且更容易转换为微服务的应用程序. 最后,我介绍了 CQRS,这是一种行为架构模式,它增强了 Clean DDD,从提高性能到更轻松的测试和更好的可扩展性,一切都得到了改善。
来源:
https://www.toutiao.com/a7057052695844274727/?log_from=9f3c53df832bb_1645595473806
“IT大咖说”欢迎广大技术人员投稿,投稿邮箱:aliang@itdks.com
来都来了,走啥走,留个言呗~
IT大咖说 | 关于版权
由“IT大咖说(ID:itdakashuo)”原创的文章,转载时请注明作者、出处及微信公众号。投稿、约稿、转载请加微信:ITDKS10(备注:投稿),茉莉小姐姐会及时与您联系!
感谢您对IT大咖说的热心支持!
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有