DDD领域驱动设计实战-分层架构及代码目录结构(下)

简介: DDD领域驱动设计实战-分层架构及代码目录结构

2.4 基础层

为其它各层提供通用技术基础服务:

  • 三方工具
  • 驱动
  • MQ
  • API网关
  • 文件
  • 缓存
  • DB
    最常用的
  • 基础层包含基础服务,它采用依赖反转,封装基础资源服务,实现应用层、领域层与基础层解耦。


MVC架构由于上层应用对DB强耦合,很多公司在架构演进最怕换DB,一旦更换,可能需重写一堆代码。

但采用依赖反转,应用层即可通过解耦保持独立核心业务逻辑。当DB变更,只需更换DB基础服务。


4 Infrastructure(基础层)

主要存放基础资源服务相关的代码,为其它各层提供的通用技术能力、三方软件包、数据库服务、配置和基础资源服务的代码都会放在这一层目录里。

Infrastructure 的代码目录结构有:config 和 util 两个子目录。

image.png

  • Config
    主要存放配置相关代码。
  • Util
    存放平台、开发框架、消息、数据库、缓存、文件、总线、网关、第三方类库、通用算法等基础代码,你可以为不同的资源类别建立不同的子目录。

3 微服务架构演进

领域模型中对象的层次从内到外依次是:值对象、实体、聚合和限界上下文。

实体或值对象的简单变更,一般不会让领域模型和微服务发生大变。但聚合的重组或拆分却可以。因为聚合内业务功能内聚,能独立完成特定业务。那聚合的重组或拆分,势必引起业务模块和系统功能变化。


可以聚合为基础单元,完成领域模型和微服务架构的演进。

聚合可作为整体,在不同领域模型间重组或拆分,或直接将一个聚合独立为微服务。

微服务架构的演进案例

现有

微服务 1:包含聚合 a、b、c

微服务2:

微服务3:包含聚合 d、e、f

当发现微服务1中聚合a的功能经常被高频访问,以致拖累了整个微服务1的性能,可把聚合a,从微服务1中剥离,独立为微服务2以应对高性能场景

随业务发展,发现微服务3的领域模型变化,聚合d会更适合放到微服务1的领域模型。即可将聚合d整体迁移到微服务1。注意定义好聚合间的代码边界

架构演进后,微服务1从最初包含聚合a、b、c,演进为包含聚合b、c、d的新领域模型和微服务

可见,好的聚合和代码模型的边界设计,可让你快速应对业务变化,轻松实现领域模型和微服务架构演进。

微服务内服务的演进

在微服务内部,实体的方法被领域服务组合和封装,领域服务又被应用服务组合和封装。在服务逐层组合和封装的过程中,你会发现这样一个有趣的现象。

image.png

服务设计时,你并不一定能完整预测有哪些下层服务会被多少个上层服务组装,因此领域层通常只提供一些原子服务,比如领域服务a、b、c。但随系统功能增强和外部接入越来越多,应用服务不断丰富。终有一日,你会发现领域服务b和c同时多次被多个应用服务调用了,执行顺序也基本一致。这时你可以考虑将b和c合并,再将应用服务中b、c的功能下沉到领域层,演进为新的领域服务(b+c)。这样既减少了服务的数量,也减轻了上层服务组合和编排的复杂度。


这就是服务演进,领域模型会越来越能适应需求快速变化。

从MVC跨越到DDD

由于层间松耦合,可专注本层设计,而不必关心其它层,也不必担心自己的设计会影响其它层。即DDD成功降低层与层之间的依赖。

分层架构使得程序结构更清晰,升级和维护更容易。修改某层代码时,只要本层接口参数不变,其它层不必修改。即使本层接口发生变化,也只影响相邻的上层,修改工作量小且可控。


传统企业应用大多是单体架构,而单体架构则大多是三层架构。三层架构解决了程序内代码间调用复杂、代码职责不清的问题,但这种分层是逻辑概念,在物理上它是中心化的集中式架构,并不适合分布式微服务架构。


DDD分层要类似三层架构,只是在DDD中,这些要素被重新划分了层,确定了层与层之间的交互规则和职责边界。

image.png

DDD分层架构相比MVC(只有API)在用户接口层新增了DTO,给前端提供了更多的可使用数据和更高的展示灵活性。


DDD分层架构对三层架构的业务逻辑层进行了更清晰的划分,改善了三层架构核心业务逻辑混乱,代码改动相互影响大的情况。


MVC架构向DDD分层架构演进,主要发生在业务逻辑层和数据访问层。

DDD分层架构将业务逻辑层的服务拆分到了应用层和领域层:


应用层快速响应前端的变化

领域层实现领域模型的能力

数据访问层和基础层之间:


三层架构数据访问采用DAO方式

DDD分层架构的数据库等基础资源访问,采用了仓储(Repository)设计模式,通过依赖倒置实现各层对基础资源的解耦。

仓储本身属基础层,但考虑到一个聚合对应一个仓储,为了以后聚合代码整体迁移方便,在微服务代码目录设计时,在聚合目录下增加一个Repository的仓储目录,跟仓储相关的代码都在这个目录下。

这个目录下的代码与聚合的其它业务代码是分开的。如果未来换数据库,只需将Repository目录下的代码替换。而如果聚合需要整体迁移到其它微服务中去,仓储的代码也会一并迁移。


仓储又分为两部分:仓储接口和仓储实现。仓储接口放在领域层中,仓储实现放在基础层。原来三层架构通用的第三方工具包、驱动、Common、Utility、Config等通用的公共的资源类统一放到了基础层。


MVC 到 DDD 具体操作如下:

抽象数据存储层

一般将Data Access层做抽象,降低系统对DB的直接依赖。

举个例子:

  • 新建Account实体对象:一个实体(Entity)是拥有ID的域对象,除了拥有数据之外,同时拥有行为。Entity和数据库储存格式无关。
  • 对象储存接口类AccountRepository:Repository只负责Entity对象的存储和读取,而Repository的实现类完成数据库存储的细节。通过加入Repository接口,底层数据库连接可以通过不同的实现类而替换

总结

聚合之间的代码边界一定要清晰。聚合之间的服务调用和数据关联应该是尽可能的松耦合和低关联,聚合之间的服务调用应该通过上层的应用层组合实现调用,原则上不允许聚合之间直接调用领域服务。这种松耦合的代码关联,在以后业务发展和需求变更时,可以很方便地实现业务功能和聚合代码的重组,在微服务架构演进中将会起到非常重要的作用。


要有代码分层思想。

写代码时一定要搞清楚代码的职责,将它放在职责对应的代码目录内。


应用层代码主要完成服务组合和编排,以及聚合之间的协作,它是很薄的一层,不应该有核心领域逻辑代码

领域层是业务的核心,领域模型的核心逻辑代码一定要在领域层实现。如果将核心领域逻辑代码放到应用层,你的基于DDD分层架构模型的微服务慢慢就会演变成传统MVC架构。

在整个微服务架构里面一般微服务上层还有BFF层、聚合服务层,一般BFF层或聚合服务层用来协调多个微服务或者做数据转换。微服务内的应用层主要处理自己的逻辑编排,bff主要处理微服务之间的逻辑。


同一个微服务内,跨领域的方法调用,我们可以在应用层进行组合和编排,那微服务间的领域方法调用是怎样的呢?

从应用层发起。方法是逐层封装,一直到应用服务。微服务内应尽量避免领域服务在不同聚合之间的调用,这样聚合之间耦合度会比较高。


Controller, Service, Repository。Controller相当于用户接口层里的Facade。由于采用了充血模型,之前三层模型中的Service的业务逻辑被封装在了domain的各个聚合下的实体之中。如果需要使用到多个实体来完成某个操作,就要使用聚合中的service。

FAQ

应用服务只能调用领域服务和实体的方法,能调用仓储接口的方法么?按理应该隔离,即应用服务应该调用领域服务的方法,再让领域服务调用仓储接口的方法吧?

如果是应用服务直接调用文件或者缓存,应用服务可以之间调用仓储。但如果中间有领域实体和数据库,则需通过领域服务,然后通过聚合根来调用仓储。


实体的转换只有从用户接口层到应用服务层一次是么?即到应用服务层后,以及之后的仓储接口都是可以直接对领域实体进行操作的?

用户接口层大多是DTO,应用层和领域层大多是DO,基础层则是PO,在不同层之间是需要进行数据转换的。


需要在实体中配置一些和底层存储相关的注解,这样会不会不能把领域层可仓储实现进行隔离?如果这样,那Spring Data Jdbc是不是没有严格遵守DDD?而且它提供的领域事件的发布机制实现,是在对应的实体中产生,例如在某一实体中定义产生领域事件的源头,当对应的实体保存或更新时,就会发出这样一个领域事件。按照咱们文章中讲解的事件的发布是在应用层,那么如果要这样做的话,是不是就需要在应用层重新转发领域层实体内产生的领域事件呢?

如果是这样,确实领域层与数据库层会有耦合。领域事件其实放领域层也可,放应用层主要是为统一管理。如果领域事件放在实体内部,查找和运维起来就不是太方便,而且这个实体还需要对领域事件的实体进行操作。目录结构的设计主要是从边界、分层和便利性考虑的。


参考

《实现领域驱动设计》

DDD分层架构:有效降低层与层之间的依赖

https://zhuanlan.zhihu.com/p/343388831

https://zhuanlan.zhihu.com/p/342826364

https://zhuanlan.zhihu.com/p/353041636?utm_source=wechat_session&utm_medium=social&utm_oi=1122562755827355648


目录
相关文章
|
1月前
|
Cloud Native Serverless API
微服务架构实战指南:从单体应用到云原生的蜕变之路
🌟蒋星熠Jaxonic,代码为舟的星际旅人。深耕微服务架构,擅以DDD拆分服务、构建高可用通信与治理体系。分享从单体到云原生的实战经验,探索技术演进的无限可能。
微服务架构实战指南:从单体应用到云原生的蜕变之路
|
1月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
Spring Boot 3.x 微服务架构实战指南
|
4月前
|
缓存 Cloud Native Java
Java 面试微服务架构与云原生技术实操内容及核心考点梳理 Java 面试
本内容涵盖Java面试核心技术实操,包括微服务架构(Spring Cloud Alibaba)、响应式编程(WebFlux)、容器化(Docker+K8s)、函数式编程、多级缓存、分库分表、链路追踪(Skywalking)等大厂高频考点,助你系统提升面试能力。
245 0
|
11月前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
Cloud Native Devops 云计算
云计算的未来:云原生架构与微服务的革命####
【10月更文挑战第21天】 随着企业数字化转型的加速,云原生技术正迅速成为IT行业的新宠。本文深入探讨了云原生架构的核心理念、关键技术如容器化和微服务的优势,以及如何通过这些技术实现高效、灵活且可扩展的现代应用开发。我们将揭示云原生如何重塑软件开发流程,提升业务敏捷性,并探索其对企业IT架构的深远影响。 ####
338 3
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####
|
7月前
|
Cloud Native Serverless 流计算
云原生时代的应用架构演进:从微服务到 Serverless 的阿里云实践
云原生技术正重塑企业数字化转型路径。阿里云作为亚太领先云服务商,提供完整云原生产品矩阵:容器服务ACK优化启动速度与镜像分发效率;MSE微服务引擎保障高可用性;ASM服务网格降低资源消耗;函数计算FC突破冷启动瓶颈;SAE重新定义PaaS边界;PolarDB数据库实现存储计算分离;DataWorks简化数据湖构建;Flink实时计算助力风控系统。这些技术已在多行业落地,推动效率提升与商业模式创新,助力企业在数字化浪潮中占据先机。
442 12
|
11月前
|
Java 开发者 微服务
从单体到微服务:如何借助 Spring Cloud 实现架构转型
**Spring Cloud** 是一套基于 Spring 框架的**微服务架构解决方案**,它提供了一系列的工具和组件,帮助开发者快速构建分布式系统,尤其是微服务架构。
926 69
从单体到微服务:如何借助 Spring Cloud 实现架构转型

热门文章

最新文章

下一篇
oss云网关配置