领域驱动设计中的架构要素

简介: 领域驱动设计中的架构要素

多数时候,领域驱动设计的分层架构并不能清晰表达各模块之间的依赖关系,以及这些模块在分层架构中所处的位置。因为我倾向于将Uncle Bob的Clean Architecture与DDD的分层架构整合起来,如下图所示:

image.png


在这个架构图中,基础设施层处于最外部,然后是应用层,最核心的是领域层。基础设施中的模块,我都称之为gateway。根据依赖方向,如果是被调用的方向,即由外至内的调用方向,就是北向,称之为北向网关。如果当前限界上下文是通过该网关调用外部资源或者别的限界上下文,即由内至外的调用方向,则是南向网关。例如图中的OrderController,会被别人调用,因而属于北向网关。注意,倘若OrderController通过RESTful方式暴露API,即为REST服务,也就是基于资源的服务。我们不能将它与DDD的应用服务混为一谈。


南向网关要特殊一些,它是打通应用层或领域层与外部资源(数据库、消息队列、第三方服务)的通道。根据整洁架构的设计原则,我们不能让内层依赖外层,以保证内层的纯粹性与稳定性。为了解除应用层或领域层与它的耦合,南向网关往往需要提供接口。这就说明,基础设施层的南向网关都是具体实现,内层对南向网关的调用则通过接口和依赖注入。至于它们的接口,就应该放在领域层或者应用层。例如,数据库的持久化属于南向网关,但它们的抽象Repository就属于领域层。


通过上图,可以帮助我们明确各个模块和各层之间的职责。下图则基于这样的内外层架构清晰地表达了限界上下文(Bounded Context,以下简称BC)之间的协作关系,即DDD中的Context Map:

image.png


Context Map中有两个常用的模式OHS(开放主机服务)与ACL(防腐层)。显然,OHS就对应前面提到的北向网关,ACL就对应着南向网关。


为了遵循整洁架构原则,就需要为ACL提供一个抽象。例如订单要调用商家BC的服务,就需要在订单BC中定义一个被调用服务的接口,然后在ACL中,通过具体框架提供的跨进程调用方式,去真正发起对商家BC服务的调用。所以,我通常将代表ACL的模块命名为Client。通过Client可以防止上游BC发生变化时对下游BC产生直接影响。一旦变化发生,我们仅需要修改南向网关中的client实现。如下图所示:

image.png

这张图体现了有ACL和无ACL的区别。


下图体现了BC对领域概念的控制,它是控制领域概念一致性的边界。在DDD中,最好的方式是不去跨BC重用一个相同的领域概念:

image.png

假设我们的BC都是微服务,就是零共享架构,数据库是独立的。那么,各自BC关心的Product属性应该放在各自数据库中,它们的ID要保持一致。


现在基于这些认识来讨论两个问题:

  • 一个BC如何发起对另一个BC的调用
  • 调用时,是否会产生所谓的“领域模型”耦合


例如在订单BC中,如果在获得订单信息的同时,还需要获得订单中商品的信息以及该商品所属商家的信息,那么该谁发起对商家BC和商品BC的调用?


首先,我们在订单BC中定义自己的模型,该模型除了Order之外,还包含了商家与商品的信息,但这些信息是Read Model,是不需要在订单BC中持久化的。这就遵循了“BC是控制领域概念一致性的边界”这一原则。由于商家与商品在订单BC中并没有持久化的需求,因此当修改发生时,并不会因此而产生数据的不一致,更不会产生领域模型的耦合。这些领域模型都各自被定义在自己的BC中,没有重用。


其次,该谁来发起商家和商品BC的调用呢?通过第一张图与第二张图的讨论,我们需要在订单BC中定义商家BC和商品BC对应服务的接口(即前面提到的Client的接口),然后在领域层的相关对象(通常是领域服务),发起对这些接口的调用。框架会通过IoC框架注入Client实现,以满足对外部服务的调用。调用后,会在订单BC将返回的结果转换为自己BC的模型对象。如果需要组装最后的DTO,则可以在领域服务之上再包装一个应用服务,完成整个完整用例的逻辑。这样,就可以让Controller只调用应用服务,减少Controller对领域层的理解,从而遵循“最小知识”法则。


基于这样的设计思想,DDD的代码模型就可以定义为:

image.png


以下是对代码结构的说明:

  • application:对应了领域驱动设计的应用层,主要内容为该限界上下文中所有的应用服务。
  • interfaces:对gateways中除persistence之外的抽象,包括访问除数据库之外其他外部资源的抽象接口,以及对第三方服务或其他限界上下文服务的抽象接口。从分层架构的角度讲,interfaces应该属于应用层,但在实践时,往往会遭遇领域层需要访问这些抽象接口的情形,单独分离出interfaces,非常有必要。
  • domain:对应了领域驱动设计的领域层,但是我将repositories单独分了出来,目的是为了更好地体现它在基础设施层扮演的与外部资源打交道的网关语义。
  • repositories:代表了领域驱动设计中战术设计阶段的资源库,皆为抽象类型。如果该限界上下文的资源库并不复杂,可以将repositories合并到domain中。
  • gateways:对应了领域驱动设计的基础设施层,命名为gateways,则是为了更好地体现网关的语义,其下可以视外部资源的集成需求划分不同的包。其中,controllers相对特殊,它属于对客户端提供接口的北向网关,等同于上下文映射中“开放主机服务(OHS)”的概念。如果为了凸显它的重要性,可以将controllers提升到与application、domain、gateways同等层次。我之所以将其放在gateways之下,还是想体现它的网关本质。persistence对应了repositories抽象,至于其余网关,对应的则是application/interfaces下的抽象,包括消息队列以及与其他限界上下文交互的客户端,例如通过http通信的客户端。其中,client包下的实现类与interfaces下的对应接口组合起来,等同于上下文映射中“防腐层(ACL)”的概念。


归根结底,在运用DDD进行架构设计,并通过BC映射到微服务设计时,要遵循两方面的设计原则。


一个是普适性的架构与设计原则,例如整洁架构、分而治之思想、关注点分离、最小知识法则等。理解了这些原则,你就清楚该如何分配职责,如何解耦。


另一个是DDD的设计原则,搞清楚每个层的职责,层之间的关系,BC之间的关系,领域模型是什么?


在明白了这些设计原则的真谛时,当我们碰到DDD设计落地的问题时,不知道该如何处理时,都可以基于这些设计原则来做出符合当前场景的决策,而不要做个“寻章摘句老雕虫”,照搬书上的方法,只要书上未曾涉及到此问题,就无从应对了。

相关文章
|
3月前
|
人工智能 运维 安全
配置驱动的动态 Agent 架构网络:实现高效编排、动态更新与智能治理
本文所阐述的配置驱动智能 Agent 架构,其核心价值在于为 Agent 开发领域提供了一套通用的、可落地的标准化范式。
773 58
|
3月前
|
人工智能 安全 数据可视化
配置驱动的动态Agent架构网络:实现高效编排、动态更新与智能治理
本文系统性地提出并阐述了一种配置驱动的独立运行时Agent架构,旨在解决当前低代码/平台化Agent方案在企业级落地时面临困难,为Agent开发领域提供了一套通用的、可落地的标准化范式。
411 18
配置驱动的动态Agent架构网络:实现高效编排、动态更新与智能治理
|
5月前
|
数据可视化 IDE Java
OneCode图生代码技术深度解析:从可视化设计到注解驱动实现的全链路架构
OneCode图生代码技术通过可视化设计与Java注解驱动,实现UI到代码的高效转换,支持设计即开发、组件复用与动态加载,提升企业应用开发效率与协作能力。
OneCode图生代码技术深度解析:从可视化设计到注解驱动实现的全链路架构
|
运维 监控 负载均衡
动态服务管理平台:驱动微服务架构的高效引擎
动态服务管理平台:驱动微服务架构的高效引擎
262 17
|
运维 负载均衡 监控
深入探索微服务架构的核心要素与实践策略
在当今软件开发领域,微服务架构已成为构建灵活、可扩展企业级应用的首选模式。本文旨在剖析微服务架构的设计理念,通过实例阐述其核心组件如服务注册与发现、配置管理、熔断机制等如何协同工作,以提升系统的敏捷性和维护性。同时,探讨了在实践中应对分布式系统复杂性的最佳策略,包括负载均衡、服务监控和日志聚合等关键技术,旨在为后端开发者提供一套完整的微服务实施指南。
269 33
|
9月前
|
调度 决策智能 知识图谱
腾讯云大模型知识引擎驱动 DeepSeek 满血版能源革命大模型:架构、优势与产业变革
腾讯云大模型知识引擎驱动的DeepSeek满血版能源革命大模型,融合了超大规模知识、极致计算效能和深度行业理解,具备智能预测、优化调度、设备健康管理和能源安全预警等七大功能模块。该模型通过分布式计算和多模态融合,提供精准的能源市场分析与决策支持,广泛应用于智慧风电场管理、油气田开发、能源市场交易等十大场景,助力能源行业的数字化转型与可持续发展。
|
敏捷开发 缓存 中间件
.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素
本文深入探讨了.NET技术的高效开发模式,涵盖面向对象编程、良好架构设计及高效代码编写与管理三大关键要素,并通过企业级应用和Web应用开发的实践案例,展示了如何在实际项目中应用这些模式,旨在为开发者提供有益的参考和指导。
138 3
|
消息中间件 监控 NoSQL
驱动系统架构
【10月更文挑战第29天】
201 2
|
存储 前端开发 API
DDD领域驱动设计实战-分层架构
DDD分层架构通过明确各层职责及交互规则,有效降低了层间依赖。其基本原则是每层仅与下方层耦合,分为严格和松散两种形式。架构演进包括传统四层架构与改良版四层架构,后者采用依赖反转设计原则优化基础设施层位置。各层职责分明:用户接口层处理显示与请求;应用层负责服务编排与组合;领域层实现业务逻辑;基础层提供技术基础服务。通过合理设计聚合与依赖关系,DDD支持微服务架构灵活演进,提升系统适应性和可维护性。