【实践篇】领域驱动设计:DDD工程参考架构

简介: 领域驱动设计(DDD)参考架构旨在为团队提供DDD实践的起点,强调业务与技术的分离,考虑多种架构风格如分层、六边形等。它包括多限界上下文结构,每个上下文内有应用层(不含领域逻辑)、领域层(含领域模型和事件)和网关层。接入层负责外部请求的处理,业务层协调不同上下文。组件包括Start(启动)、Common(通用)、API、Facade、Application Service、External API、Query、Domain和Gateway,各组件有明确的职责和依赖关系,如Gateway处理技术细节并作为系统与外部的接口。架构设计是多因素权衡,适应实际工程需求。

背景

为什么要制定参考工程架构

不同团队落地DDD所采取的应用架构风格可能不同,并没有统一的、标准的DDD工程架构。有些团队可能遵循经典的DDD四层架构,或改进的DDD四层架构,有些团队可能综合考虑分层架构、整洁架构、六边形架构等多种架构风格,有些在实践中可能引入CQRS解决读模型与写模型的差异化等等。即使无法制定通用的、标准的工程应用架构,但为团队制定一个遵循领域驱动设计思想的参考架构依然有价值。基于以下原因:

  • 为团队实践DDD的战术设计提供可以快速开始的工程参考
  • 参考工程大量的命名和结构决策,显式的体现DDD的相关理念,有利于团队对DDD的战术实现达成一致认知
  • 同时,参考架构有助于沉淀团队对领域驱动设计的一些思考和最佳实践

参考架构的考量因素

虽然无法制定完全通用的DDD参考架构,但制定某个特定上下文下的参考架构却具有可行性和实践价值。针对于上下文的选择要尽量贴合实际的工程实践场景并考虑多维度的因素。

本文所述参考工程架构遵循以下原则:

  • 遵循领域驱动设计的本质思想
  • 充分考虑业务系统建设特点
  • 依赖最小化,保持轻量

希望工程参考架构能涵盖以下范围

  • 分离业务域与技术域

参考架构要遵循技术和业务隔离的特性,可以参考多种架构风格。业务与技术关注点的分离并不是DDD独有的特点,在六边形、整洁架构、洋葱架构中都遵循了这一重要原则。

  • 多限界上下文场景

大多数团队基于DDD进行微服务拆分的时候,特别是系统建设初期,对单个微服务应用内的限界上下文的粒度需要权衡。由于团队组织架构因素及微服务成本问题,单个应用容纳的限界上下文一般是多个(理想情况下是1:1)。这些限界上下文随着后续的逐步迭代有可能会迁移至独立应用。因此,参考架构将多上下文的应用场景作为重要考量因素。

  • 明确的组件、职责边界及依赖关系
  • 支持领域报表场景:报表场景在业务系统较为常见,DDD并没有体现该场景的处理方式。作为工程参考架构,还是希望能够从实际业务出发,体现对写模型和报表模型的显示支持
  • 外部依赖最小化:需要排除不必要的依赖,保持工程架构的轻量化

参考架构剖析

应用的多上下文结构

基于以上原则,参考工程考虑单个应用内多上下文的场景,以期在模块化和服务粒度及成本间进行权衡折衷。应用架构对多上下文的支持的逻辑示意图如下所示,在解决方案域对限界上下文进行识别和划分之后,基于其业务内聚性和关联性,把多个上下文实现单个工程应用中。单个应用内的多个限界上下文间可能存在交互,交互的形式可以是基于事件驱动,也可以是基于进程内调用。采用事件驱动的方式上下文间的耦合性对低一些,但一般需要引入事件总线的支持,额外组件的引入必然会导致复杂性的上升。进程内调用则耦合性会高一些,但从实现角度复杂度会低一些。具体选择哪种方式开发人员可以基于实际情况进行权衡。

需要再次说明的是,这种应用架构决策是一种多因素的权衡,并没有与子域与限界上下文1:1的理想化实践保持一致。

从上图的逻辑示意图我们再深入一层,从分层的维度去剖析一下详细的应用架构的展现形式,如下图所示:

分层关注点

客户端

客户端与应用处于不同的进程,是应用能力的消费端,在实际项目中可能是APP端、PC端、小程序端、公众号端或三方的业务调用端等等。

接入层

接入层是外部系统与应用内部业务能力的中间层,接入层是应用层对外的门面,是当前应用对外暴露业务能力的入口。该层的组成可能是对外提供的HTTP接口声明、分布式定时任务调度、消息监听器、RPC服务等等。其重要职责包括对外部系统的请求进行基础的参数校验、入参适配和服务路由(转发至系一层的应用服务)以及响应数据的适配

业务层:

该层是应用的业务逻辑所在层,整个架构风格采用模块化单体风格,在该层不同的限界上下文体现为不同的模块。在每个限界上下文内采用分层架构,独立划分为应用层、领域层和网关层

应用层:

协调领域对象、领域服务或外部依赖服务完成业务用例,该层只做能力协调,不处理任何领域逻辑

领域层:

领域层是整个分层的核心,与技术实现无关,主要负责领域模型、领域事件、领域服务定义,以及业务相关外部服务的接口抽象以及仓库的接口抽象等。

领域层与应用服务的本质区别是:应用层不包含领域逻辑,领域逻辑全部下沉到领域层实现。

网关层:

网关层定位是应用的出口网关,是应用与外部基础设施交互的防腐层,处理所有技术相关实现

该组件的命名有多种方式,比如有些团队将其命名为 “rpc”,也有些团队将其命名为 “infrastructure”,不同的命名体现了团队对其背后所表达的隐喻的决策选择。在本文的参考架构中选择了 网关-Gateway这一命名,决策原因是:限界上下文自身是高内聚的,其与外部的交互需要统一出口,Gateway所表达的网关的含义恰当的表现了这种统一出口的理念。如果Facade层是应用的北向网关,是外部系统请求进入内部的入口。则此时的Gateway则表达的是限界上下文的南向网关,是内部应用连接外部的出口。

组件及依赖

从宏观的分层我们再深入一层看下每层的组件划分。如下图所示:

Start组件:

整个应用的启动入口、加载应用配置信息等等。

Common组件

提供在不同的限界上下文间复用的领域模型元素的抽象,比如对Command、Query、Event、Entity、ValueObjec通用抽象等。当然,领域模型的通用抽象不是必须在Common组件内以提供复用,也可以作为一个独立的限界上下文,并以共享内核方式与其它上下文进行共享,或者也可以实现为独立的jar包组件。

API 组件

RPC类型服务的接口声明组件,以公司内部使用的JSF为例,该组件是应用对外部系统暴露的JSF API的组件。该组件可以是独立的工程,当然,有些团队会将其作为一个Module放入应用工程中。

统一门面组件:Facade

外部客户端触达应用系统的入口,也是内部应用服务的统一门面,类似于六边形架构风格下的适配器。参考架构中基于不同场景划分为 provider(RPC服务)、task(定时任务)、listener(MQ监听)、rest(http接口)等几个子包。外部请求进入系统后,由Facade组件完成入参基本校验、入参转换、服务路由以及出参转换等操作。另外,还可以承担处理登录态、鉴权以及日志等相关能力。

应用服务组件:Application Service

应用服务代表着用例以及系统行为,其通过委托到领域层和基础设施层(参考架构中的Gateway组件)完成用例的应用逻辑逻辑处理,可以理解为应用服务是领域层的客户端。该组件典型的职责:

从存储层加载领域对象、委托领域对象执行领域逻辑、保存领域对象

  • 重要事件通知到外部
  • 出入参转化适配
  • 事务处理外部
  • 非领域逻辑的服务调用

External API

应用服务的逻辑不仅仅需要协调领域层,有时还需要依赖于外部的三方服务。External API 组件负责对这些外部服务进行接口声明定义,不做具体实现。

应用服务组件不直接依赖这些外部服务实现,而是依赖其接口抽象。同时,此处的模型定义是基于该限界上下文的语义,是一种对外部模型的适配。

该组件不依赖其它组件,且仅被应用服务组件和Gateway组件依赖。网关组件依赖External API 组件的接口声明并提供底层技术实现,应用服务组件依赖其接口,并通过IOC方式将具体实现注入完成服务调用。

注意,该组件所依赖的服务不涉及领域逻辑,只是用于支撑应用服务的编排。如果是涉及了领域逻辑,则对外部服务依赖的接口定义需要下沉到Domain层。

Query

Query组件解决领域相关的报表查询场景,在限界上下文内作为与应用服务对等的组件存在,两个组件分别负责业务的查询和命令逻辑。

虽然引入了Query组件对报表场景提供支持,但没有完全的引入CQRS这一模式。在很多资料中CQRS与DDD同时提及的概率比较高,因为,在DDD下我们解决了复杂的面向领域的写侧模型,但在报表场景下,这种富领域模型有可能并不是最佳选择。如果读侧和写侧都基于统一的领域模型,一般会导致领域模型的折衷设计。为了满足查询侧诉求,领域模型不得不引入额外的、领域无关的属性,由此造成领域模型的污染。

Domain

Domain组件是领域逻辑核心,承担整个系统领域逻辑的实现,其定义了领域模型、领域服务、领域事件以及仓储层的抽象。该组件不依赖其它组件(除了通用的领域模型抽象组件Common之外。

上图所体现的参考架构使用了DDD的战术设计的经典建模元素,比如聚合、实体、值对象、仓储、工厂以及领域事件等。在实际落地过程中,这些设计元素的抽象具有一定的挑战,设计过程中需要经过不断分析、权衡和重构以完成建模,这正是核心设计所在。

Gateway

网关层承担整个技术相关性的实现,是内部应用的出口网关。

技术相关性是网关组件区别于其它组件的根本特性。在该组件内要处理技术实现的所有细节,比如与外部服务、中间件、DB的交互等。同时,其与Domain组件以及External API组件的接口抽象协作,共同承担了系统与外部依赖间(包括外部服务以及应用依赖的中间件、DB等基础设施)的防腐层职能,负责内部模型到外部模型转化、外部模型到内部模型转化以及具体交互。基于网关组件的特性,也非常适合在该层做统一的外部服务数据缓存及降级熔断处理。

网关层依赖于Domain、Application Service、External API和Query组件,负责上述四个组件定义的接口实现。在Gateway组件通过子包对实现进行隔离:

  • query:查询服务组件的实现
  • external:External API 组件中依赖外部服务的接口实现
  • repository:仓储接口的实现
相关文章
|
2天前
|
机器学习/深度学习 弹性计算 人工智能
阿里云服务器ECS架构区别及选择参考:X86计算、ARM计算等架构介绍
在我们选购阿里云服务器的时候,云服务器架构有X86计算、ARM计算、GPU/FPGA/ASIC、弹性裸金属服务器、高性能计算可选,有的用户并不清楚他们之间有何区别,本文主要简单介绍下这些架构各自的主要性能及适用场景,以便大家了解不同类型的架构有何不同,主要特点及适用场景有哪些。
|
7天前
|
负载均衡 Serverless 持续交付
云端问道9期实践教学-省心省钱的云上Serverless高可用架构
详细介绍了云上Serverless高可用架构的一键部署流程
32 10
|
21天前
|
弹性计算 Java 关系型数据库
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
Web应用上云经典架构实践教学
|
7天前
|
存储 人工智能 运维
面向AI的服务器计算软硬件架构实践和创新
阿里云在新一代通用计算服务器设计中,针对处理器核心数迅速增长(2024年超100核)、超多核心带来的业务和硬件挑战、网络IO与CPU性能增速不匹配、服务器物理机型复杂等问题,推出了磐久F系列通用计算服务器。该系列服务器采用单路设计减少爆炸半径,优化散热支持600瓦TDP,并实现CIPU节点比例灵活配比及部件模块化可插拔设计,提升运维效率和客户响应速度。此外,还介绍了面向AI的服务器架构挑战与软硬件结合创新,包括内存墙问题、板级工程能力挑战以及AI Infra 2.0服务器的开放架构特点。最后,探讨了大模型高效推理中的显存优化和量化压缩技术,旨在降低部署成本并提高系统效率。
|
9天前
|
运维 监控 安全
天财商龙:云上卓越架构治理实践
天财商龙成立于1998年,专注于为餐饮企业提供信息化解决方案,涵盖点餐、收银、供应链和会员系统等。自2013年起逐步实现业务上云,与阿里云合作至今已十年。通过采用阿里云的WA体系,公司在账号管理、安全保障、监控体系和成本管控等方面进行了全面优化,提升了业务稳定性与安全性,并实现了显著的成本节约。未来,公司将持续探索智能化和全球化发展,进一步提升餐饮行业的数字化水平。
|
9天前
|
运维 安全 架构师
架构师工具箱:Well-Architected云治理提效实践
本次分享基于阿里云Well-Architected Framework的最佳实践案例,涵盖企业从上云到优化的全过程。安畅作为国内领先的云管理服务提供商(Cloud MSP),拥有800多名员工,其中70%为技术工程师,为企业提供架构安全、数据智能等技术服务。内容包括Landing Zone与Well-Architected的关系、企业云治理现状及需求分析,重点探讨了安全合规、成本优化、资源稳定性和效率提升等方面的最佳实践,并通过具体客户案例展示了如何通过自动化工具和定制化解决方案帮助企业提升云上业务价值。
|
2月前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
1月前
|
运维 监控 Java
后端开发中的微服务架构实践与挑战####
在数字化转型加速的今天,微服务架构凭借其高度的灵活性、可扩展性和可维护性,成为众多企业后端系统构建的首选方案。本文深入探讨了微服务架构的核心概念、实施步骤、关键技术考量以及面临的主要挑战,旨在为开发者提供一份实用的实践指南。通过案例分析,揭示微服务在实际项目中的应用效果,并针对常见问题提出解决策略,帮助读者更好地理解和应对微服务架构带来的复杂性与机遇。 ####
|
2月前
|
NoSQL Java 数据处理
基于Redis海量数据场景分布式ID架构实践
【11月更文挑战第30天】在现代分布式系统中,生成全局唯一的ID是一个常见且重要的需求。在微服务架构中,各个服务可能需要生成唯一标识符,如用户ID、订单ID等。传统的自增ID已经无法满足在集群环境下保持唯一性的要求,而分布式ID解决方案能够确保即使在多个实例间也能生成全局唯一的标识符。本文将深入探讨如何利用Redis实现分布式ID生成,并通过Java语言展示多个示例,同时分析每个实践方案的优缺点。
76 8
|
1月前
|
算法 NoSQL Java
微服务架构下的接口限流策略与实践#### 一、
本文旨在探讨微服务架构下,面对高并发请求时如何有效实施接口限流策略,以保障系统稳定性和服务质量。不同于传统的摘要概述,本文将从实际应用场景出发,深入剖析几种主流的限流算法(如令牌桶、漏桶及固定窗口计数器等),通过对比分析它们的优缺点,并结合具体案例,展示如何在Spring Cloud Gateway中集成自定义限流方案,实现动态限流规则调整,为读者提供一套可落地的实践指南。 #### 二、
65 3

热门文章

最新文章

下一篇
开通oss服务