「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合(下)

简介: 「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构和CQRS的整合

解耦的组件

就像细粒度的代码单元(类、接口、特征、混合等)一样,粗粒度的代码单元(组件)也受益于低耦合和高内聚。

为了解耦类,我们使用依赖注入,将依赖注入到类中而不是在类中实例化,依赖倒置,使类依赖于抽象(接口和/或抽象类)而不是具体类。这意味着子类不知道它将要使用的具体类,它没有引用它所依赖的类的完全限定类名。

同样,完全解耦的组件意味着一个组件不直接知道任何其他组件。换句话说,它没有引用来自另一个组件的任何细粒度代码单元,甚至没有接口!这意味着依赖注入和依赖倒置不足以解耦组件,我们需要某种架构结构。我们可能需要事件、共享内核、最终一致性,甚至发现服务!




在其他组件触发逻辑

当我们的一个组件(组件B)需要在另一个组件(组件A)中发生其他事情时执行某个操作时,我们不能简单地从组件A直接调用组件B中的类/方法,因为这样A就会被耦合到B。

然而,我们可以使用事件分派器来分派一个应用程序事件,该应用程序事件将被交付给监听它的任何组件,包括B,而B中的事件侦听器将触发所需的操作。这意味着组件A将依赖于事件分配器,但它将与B解耦。

然而,如果事件本身“存在”于A中,这意味着B知道A的存在,它与A是耦合的。这意味着组件都依赖于共享内核,但是它们之间是解耦的。共享内核将包含应用程序和域事件之类的功能,但它也可以包含规范对象,以及任何需要共享的内容,请记住,共享内核的任何更改都将影响到应用程序的所有组件,因此共享内核应该尽可能少。此外,如果我们有一个多语言系统,假设是一个微服务生态系统,其中它们是用不同的语言编写的,那么共享内核需要是语言无关的,以便所有组件都可以理解它,无论它们是用什么语言编写的。例如,它将包含事件描述,而不是包含事件类的共享内核。名称、属性、甚至方法(尽管这些在JSON之类的不可知语言中可能更有用),这样所有组件/微服务都可以解释它,甚至自动生成它们自己的具体实现。请在我的后续文章中阅读更多相关内容:不仅仅是同心圆层。



这种方法既适用于单片应用程序,也适用于像微服务生态系统这样的分布式应用程序。然而,当事件只能异步交付时,对于需要立即在其他组件中执行触发逻辑的上下文,这种方法是不够的!组件将需要一个直接的HTTP调用组件b。在这种情况下,解耦的组件,我们需要发现服务,将要求它应该发送请求来启动所需的行动,或者使请求发现服务代理的相关服务,最终将响应返回给请求者。此方法将把组件耦合到发现服务,但将使它们彼此解耦。

从其他组件获取数据

在我看来,一个组件不允许改变它不“拥有”的数据,但是它可以查询和使用任何数据。

组件之间共享的数据存储

当一个组件需要使用属于另一个组件的数据时,假设一个账单组件需要使用属于accounts组件的客户端名称,账单组件将包含一个查询对象,该对象将查询该数据的数据存储。这仅仅意味着账单组件可以知道任何数据集,但是它必须通过查询的方式将不“拥有”的数据作为只读数据使用。

每个组件隔离数据存储

在本例中,应用了相同的模式,但是我们在数据存储级别上更加复杂。组件拥有自己的数据存储意味着每个数据存储包含:

  • 它拥有的一组数据,并且是唯一允许更改的数据,使其成为唯一的真理来源;
  • 一组数据是其他组件数据的副本,它不能自己更改这些数据,但是组件功能需要它,并且需要在所有者组件中发生更改时对其进行更新。

每个组件将从其他组件创建所需数据的本地副本,以便在需要时使用。当拥有该组件的组件中的数据发生更改时,该所有者组件将触发承载数据更改的域事件。持有该数据副本的组件将侦听该域事件,并相应地更新其本地副本。

控制流

正如我上面所说的,控制流当然是从用户到应用程序核心,再到基础设施工具,最后回到应用程序核心,最后回到用户。但是类到底是如何组合在一起的呢?哪些取决于哪些?我们如何组合它们?

在Bob叔叔关于干净架构的文章中,我将尝试用UMLish图来解释控制流……

没有命令/查询总线

在我们不使用命令总线的情况下,控制器将依赖于应用程序服务或查询对象。




在上面的图中我们使用应用程序的接口服务,尽管我们可能认为这不是真正需要从应用程序服务是我们应用程序代码的一部分,我们不会想交换另一个实现,尽管我们可能完全重构它。

查询对象将包含一个优化的查询,该查询将简单地返回一些原始数据以显示给用户。该数据将以DTO的形式返回,并注入到ViewModel中。这个视图模型可能有一些视图逻辑,它将被用来填充一个视图。

另一方面,应用程序服务将包含用例逻辑,当我们希望在系统中执行某些操作时,而不是简单地查看某些数据时,将触发该逻辑。应用程序服务依赖于存储库,存储库将返回包含需要触发的逻辑的实体。它还可能依赖于域服务来协调多个实体中的域流程,但情况并非如此。

在展开用例之后,应用程序服务可能希望通知整个系统该用例已经发生,在这种情况下,它还将依赖于事件分派器来触发事件。

值得注意的是,我们在持久性引擎和存储库上都放置了接口。虽然看起来有些多余,但它们有不同的用途:

  • 持久性接口是ORM上的一个抽象层,因此我们可以交换正在使用的ORM,而不需要更改应用程序的核心。
  • repository接口是对持久性引擎本身的抽象。假设我们想从MySQL切换到MongoDB。持久性接口可以是相同的,如果我们想继续使用相同的ORM,那么即使是持久性适配器也可以保持不变。但是,查询语言是完全不同的,所以我们可以创建使用相同持久性机制的新存储库,实现相同的存储库接口,但是使用MongoDB查询语言而不是SQL构建查询。

使用命令/查询总线

在我们的应用程序使用命令/查询总线的情况下,除了控制器现在依赖于总线和命令或查询外,关系图几乎保持不变。它将实例化命令或查询,并将其传递给总线,总线将找到适当的处理程序来接收和处理命令。


在下面的关系图中,命令处理程序然后使用应用程序服务。然而,这并不总是需要的,事实上在大多数情况下,处理程序将包含用例的所有逻辑。如果需要在另一个处理程序中重用相同的逻辑,则只需要将逻辑从处理程序提取到单独的应用程序服务中。



您可能已经注意到,总线与命令、查询和处理程序之间没有依赖关系。这是因为,为了提供良好的解耦,它们实际上应该彼此不了解。总线知道什么处理程序应该处理什么命令或查询的方式应该通过简单的配置来设置。

如您所见,在这两种情况下,跨越应用程序核心边界的所有箭头和依赖项都指向内部。如前所述,这是端口和适配器体系结构、Onion体系结构和Clean体系结构的基本规则。


结论

一如既往,我们的目标是拥有一个松散耦合和高内聚的代码库,这样修改起来就容易、快速和安全。

    计划是没有价值的,但计划就是一切。-------------------------------艾森豪威尔

    这个信息图是一个概念图。了解和理解所有这些概念将帮助我们规划一个健康的架构,一个健康的应用程序。

    然而:

      地图不是领土。-----------------------阿尔弗雷德Korzybski

      这意味着这些只是指导方针!应用程序是我们需要应用知识的领域、现实和具体用例,这就是定义实际体系结构的内容!

      我们需要理解所有这些模式,但是为了解耦和内聚,我们还需要思考并准确地理解我们的应用程序需要什么,我们应该走多远。这个决策可以依赖于许多因素,从项目功能需求开始,但是也可以包括诸如构建应用程序的时间框架、应用程序的生命周期、开发团队的经验等因素。

      就是这样,这就是我理解这一切的方式。这就是我在脑海里给它找的合理解释。

      我在后续的文章中进一步扩展了这些想法:不仅仅是同心圆层。

      但是,我们如何在代码库中显式地实现这一切呢?这是我下一篇文章的主题:如何在代码中反映体系结构和域。

      相关文章
      |
      18天前
      |
      存储 边缘计算 Cloud Native
      “论模型驱动架构设计方法及其应用”写作框架,软考高级,系统架构设计师
      模型驱动架构设计是一种用于应用系统开发的软件设计方法,以模型构造、模型转换和精化为核心,提供了一套软件设计的指导规范。在模型驱动架构环境下,通过创建出机器可读和高度抽象的模型实现对不同问题域的描述,这些模型独立于实现技术,以标准化的方式储存,利用模型转换策略来驱动包括分析、设计和实现等在内的整个软件开发过程。
      |
      10天前
      |
      消息中间件 设计模式 Java
      Java中的消息驱动架构设计
      Java中的消息驱动架构设计
      |
      7天前
      |
      消息中间件 监控 Java
      使用Kafka实现分布式事件驱动架构
      使用Kafka实现分布式事件驱动架构
      |
      7天前
      |
      敏捷开发 Java 测试技术
      「架构」模型驱动架构设计方法及其运用
      本文探讨了MDA在软件开发中的应用,从需求分析到测试,使用UML建模功能需求,通过PIM设计架构,自动生成代码以减少错误。MDA提升了可维护性、可扩展性和可移植性,通过工具如Enterprise Architect和Eclipse MDT支持自动化转换。虽然有挑战,如模型创建和平台转换,但结合敏捷方法和适当工具能有效解决,从而提高开发效率和软件质量。
      「架构」模型驱动架构设计方法及其运用
      |
      11天前
      |
      中间件 BI 测试技术
      【实践篇】领域驱动设计:DDD工程参考架构
      领域驱动设计(DDD)参考架构旨在为团队提供DDD实践的起点,强调业务与技术的分离,考虑多种架构风格如分层、六边形等。它包括多限界上下文结构,每个上下文内有应用层(不含领域逻辑)、领域层(含领域模型和事件)和网关层。接入层负责外部请求的处理,业务层协调不同上下文。组件包括Start(启动)、Common(通用)、API、Facade、Application Service、External API、Query、Domain和Gateway,各组件有明确的职责和依赖关系,如Gateway处理技术细节并作为系统与外部的接口。架构设计是多因素权衡,适应实际工程需求。
      |
      11天前
      |
      缓存 前端开发 安全
      DDD中的分层架构
      领域驱动设计(DDD)的分层架构演进为依赖倒置的四层模型,强调关注点分离。表现层(UI)展示信息并处理用户指令,应用程序层负责用例编排,与领域层交互但不含业务逻辑。领域层承载核心业务逻辑,包含领域模型和服务,确保业务正确性。基础设施层提供技术支撑,如数据库和缓存,服务于其他层。各层解耦,实现灵活的系统架构。
      |
      28天前
      |
      存储 传感器 编解码
      【Camera基础(二)】摄像头驱动原理和开发&&V4L2子系统驱动架构
      【Camera基础(二)】摄像头驱动原理和开发&&V4L2子系统驱动架构
      |
      2月前
      |
      数据库
      DDD架构浅谈
      DDD架构浅谈
      50 4
      |
      2月前
      |
      人工智能 算法 测试技术
      探索软件自动化测试的未来:AI驱动的测试策略构建高效可靠的微服务架构:后端开发的新范式
      【5月更文挑战第28天】 在软件开发的世界中,测试是确保产品质量的关键步骤。随着技术的进步和项目复杂性的增加,传统的手动测试方法逐渐显得力不从心。本文旨在探讨自动化测试的最新趋势——人工智能(AI)驱动的测试策略。我们将分析AI如何通过智能化的测试用例生成、测试执行优化以及结果分析来提高测试效率和精确性。文章还将讨论实施AI测试策略的挑战与机遇,为软件测试工程师提供未来技术转型的视角。 【5月更文挑战第28天】 在当今软件开发的快速迭代和复杂多变的环境中,传统的单体应用架构已经难以满足业务敏捷性和可扩展性的需求。微服务架构作为一种新的解决方案,以其服务的细粒度、独立部署和弹性伸缩等特性,正逐
      |
      2月前
      |
      消息中间件 Java RocketMQ
      Spring Cloud RocketMQ:构建可靠消息驱动的微服务架构
      【4月更文挑战第28天】消息队列在微服务架构中扮演着至关重要的角色,能够实现服务之间的解耦、异步通信以及数据分发。Spring Cloud RocketMQ作为Apache RocketMQ的Spring Cloud集成,为微服务架构提供了可靠的消息传输机制。
      71 1