COLA-项目

COLA-项目

COLA 4.0:应用架构的最佳实践_cola4.0_张建飞(Frank)的博客-CSDN博客

主要是项目搭建的架构 :alibaba/COLA: 🥤 COLA: Clean Object-oriented & Layered Architecture (github.com)

《架构》

架构图

cola arch

1)适配层(Adapter Layer):负责对前端展示(web,wireless,wap)的路由和适配,对于传统B/S系统而言,adapter就相当于MVC中的controller;

2)应用层(Application Layer):主要负责获取输入,组装上下文,参数校验,调用领域层做业务处理,如果需要的话,发送消息通知等。层次是开放的,应用层也可以绕过领域层,直接访问基础实施层;

3)领域层(Domain Layer):主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Domain Entity)的方法对App层提供业务实体和业务逻辑计算。领域是应用的核心,不依赖任何其他层次;

4)基础实施层(Infrastructure Layer):主要负责技术细节问题的处理,比如数据库的CRUD、搜索引擎、文件系统、分布式服务的RPC等。此外,领域防腐的重任也落在这里,外部依赖需要通过gateway的转义处理,才能被上面的App层和Domain层使用。

image.png

模型

image.png

img

Client 和 防腐层 - RPC

RPC的interface 应该和 service interface差不多,都在client 层定义,然后在 防腐层调用,在 应用层实现

限界上下文 Context A 通过控制器(Controller)为其他上下文暴露 REST 服务,而在 Context A内部呢?则调用的自己的应用层的应用服务(Application Service),然后再调用领域层的领域模型(Domain Model)。

倘若限界上下文 B 需要访问限界上下文A 的rest 服务,由基础设施层的客户端( 如 Feign Client)完成,这个客户端就是上下文映射模式的防腐层ACL。而在 Context B内部呢?实际不会直接调用Feign,则通过放置在领域层的接口(Interface)去完成,接口(Interface)去 调用由基础设施层的客户端( 如 Feign Client),完成真正的访问逻辑实现。

img

层次 包名 功能 必选 DDD
Adapter层 web 处理页面请求的Controller
Adapter层 wireless 处理无线端的适配
Adapter层 wap 处理wap端的适配
App层 executor 处理request,包括command和query 应用服务
App层 consumer 处理外部message 应用服务
App层 scheduler 处理定时任务 应用服务
Domain层 model 领域模型 Domain
Domain层 ability 领域能力,包括DomainService DomainService
Domain层 gateway 领域网关,解耦利器 Gateway
Infra层 gatewayimpl 网关实现 领域层实现
Infra层 mapper ibatis数据库映射
Infra层 config 配置信息
Client SDK api 服务对外透出的API
Client SDK dto 服务对外的DTO

备注:WAP(Wireless Application Protocol)无线应用协议是一个开放式标准协议,利用它可以把网络上的信息传送到移动电话或其他无线通讯终端上

image-20230911083054682

Adapter

适配器层

提供对外访问的 Controller,操作Client

Client

用户接口层

  • DTO —- 每一个 command 操作,对应一个DTO (Data Transfer Object)
    • Client Object
    • xxxCmd – command 对应的DTO
    • Domain Event 领域事件对应的DTO
  • domain event
  • service interface – 应用服务接口(APP 应用层实现)

APP

应用层

  • 命令查询职责分离 - CQRS(Command Query Responsibility Segregation)
  • service implement – 应用服务
  • even handler – 事件处理

Domain

领域层

  • 领域模型 (定义、操作 领域模型)
    • 实体 - entity
    • 聚合
    • 领域服务
  • Gateway interface 提供访问领域模型的接口 (基础设施实现)
    • 领域层 与 应用层 的媒介
    • 细致化管理

Gateway interface 到作用- (细致化解耦,减少不合理的依赖)

1
2
3
4
5
6
那么在订单域,该如何获取商品和库存信息呢?最直接的方式,无外乎就是RPC调用商品和库存服务,拿到DTO直接使用就完了。
原因
然而,商品域吐出的是一个大而全的DTO(可能包含几十个字段),而在下单这个阶段,订单所需要的可能只是其中几个字段而已。更合适的做法,应该是在订单域中,使用gateway对商品域和库存域的依赖进行解耦。

好处:
这样做有两个好处,一个是降低了对外域信息依赖的耦合;另一个是通过上下文映射(Context mapping),确保本领域边界上下文(Bounded context)下领域知识的完整性,实现了统一语言(Ubiquitous language)。

image.png

infrastructure

基础设施层

  • Data Obejct – 操作数据库
  • Gateway implement – 实现领域层 提供接口,
  • common
    • 事件发布
    • 异常处理

单个模块(简单)版本:

image-20230917070322897

10、代码骨架

用户接口层

用户接口层的核心职能:协议转换和适配、鉴权、参数校验和异常处理。

1
2
3
4
5
6
7
8
9
10
11
12
├── controller                        //面向视图模型&资源
│ ├── ResultController.java
│ ├── assembler // 装配器,将VO转换为DTO
│ │ └── ResultAssembler.java
│ └── vo // VO(View Object)对象
│ ├── EnterResultRequest.java
│ └── ResponseVO.java
├── provider // 面向服务行为,比如dubbo RPC
├── subscriber // 面向事件 ,比如 rocketmq的客户端程序
└── task // 面向策略 ,比如 xxl-job的调度任务
└── TotalResultTask.java
1234567891011

应用层

应用层的核心职能:编排领域服务、事务管理、发布应用事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
├── assembler                         // 装配器,将DTO转换为DO
│ ├── ResultAssembler.java
│ └── TotalResultAssembler.java
├── dto // DTO(Data Transfer Object)对象
│ ├── cmd // 命令相关的DTO对象
│ │ ├── ComputeTotalResultCmd.java
│ │ ├── EnterResultCmd.java
│ │ └── ModifyResultCmd.java
│ ├── event // 应用事件相关的DTO对象, subscriber负责接收
│ └── qry // 查询相关的DTO对象
└── service // 应用服务
├── ResultApplicationService.java
├── event // 应用事件,用于发布
└── adapter // 防腐层适配器接口
1234567891011121314

领域层

代码组织以聚合为基本单元。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
├── result                            // 成绩聚合
│ ├── entity // 成绩聚合内的实体
│ │ └── Result.java
│ ├── service // 领域服务
│ │ ├── ResultDomainService.java
│ │ ├── event // 领域事件
│ │ ├── adapter // 防腐层适配器接口
│ │ ├── factory // 工厂
│ │ └── repository // 资源库
│ │ └── ResultRepository.java
│ └── valueobject // 成绩聚合的值对象
│ ├── GPA.java
│ ├── ResultUK.java
│ ├── SchoolYear.java
│ └── Semester.java
└── totalresult // 总成绩聚合
├── ... 这段有点长,其代码结构与成绩聚合一致,因此省略 ...

1234567891011121314151617

基础设施实现层

该层主要提供领域层接口(资源库、防腐层接口)和应用层接口(防腐层接口)的实现。

代码组织基本以聚合为基本单元。对于应用层的防腐层接口,则直接以 application 作为包名组织。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── application                       // 应用层相关实现
│ └── adapter // 防腐层适配器接口实现
│ ├── facade // 外观接口
│ └── translator // 转换器,DO -> DTO
├── result // 成绩聚合相关实现
│ ├── adapter
│ │ ├── facade
│ │ └── translator
│ └── repository // 成绩聚合资源库接口实现
│ └── ResultRepositoryImpl.java
└── totalresult // 总成绩聚合相关实现
├── adapter
│ ├── CourseAdapterImpl.java
│ ├── facade
│ └── translator
└── repository
└── TotalResultRepositoryImpl.java

—————–《设计》—————–—

设计步骤

背景

限界上下文 - BC

领域服务

领域服务和聚合是领域驱动设计(Domain-Driven Design,简称DDD)中的重要概念。下面是一些设计领域服务和聚合的常见原则和方法:

  1. 领域服务(Domain Service):
    • 领域服务是一种无状态的操作,它封装了一些领域相关的业务逻辑,但不属于任何特定的实体或值对象。
    • 领域服务应该具有高内聚性,即它应该关注于一个明确的业务功能或操作。
    • 领域服务应该遵循单一职责原则,即每个服务应该只负责一个明确的任务。
    • 领域服务可以通过接口或抽象类定义,并由具体的实现类来实现。
  2. 聚合(Aggregate):
    • 聚合是一组相关的领域对象的集合,它们一起形成了一个有边界的整体。
    • 聚合应该有一个根实体(Aggregate Root),它是聚合的入口点,负责协调和管理聚合内的其他对象。
    • 聚合内的对象应该通过聚合根来访问和操作,外部对象只能通过聚合根来访问聚合内的对象。
    • 聚合应该保持一致性和完整性,即聚合内的对象应该满足一定的业务规则和约束。

在设计领域服务和聚合时,可以遵循以下步骤:

  1. 确定业务需求和业务边界:了解业务需求,确定领域中的业务边界和职责。
  2. 识别领域对象和关系:识别领域中的实体、值对象和聚合,并确定它们之间的关系和依赖。
  3. 设计领域服务:根据业务需求,设计领域服务来封装和处理领域相关的业务逻辑。
  4. 设计聚合:根据业务边界和职责,设计聚合来组织和管理领域对象,并确保聚合的一致性和完整性。
  5. 实现和测试:根据设计,实现领域服务和聚合,并进行测试和验证。

需要注意的是,设计领域服务和聚合是一个迭代的过程,需要不断地进行反馈和调整。在实践中,可以结合领域驱动设计的其他概念和模式,如实体、值对象、领域事件等,来更好地设计和实现领域服务和聚合。

如果您需要更详细的信息,可以使用以下查询词进行搜索:领域服务和聚合设计。

如果您有任何其他问题,请随时提问。

CSDN首页

搜索

img

会员中心 img

消息

历史

创作中心

AI发文

—–《COLA 4.0:应用架构的最佳实践》—–

应用架构的本质

什么是架构?十个人可能有十个回答,架构在技术的语境下,就和架构师一样魔幻。我曾经看过一本技术书,用了一章的篇幅讨论架构的定义,最终也没有说明白。

实际上,定义架构也没那么难,如下图所示,架构的本质,简单来说,就是要素结构。所谓的要素(Components)是指架构中的主要元素,结构是指要素之间的相互关系(Relationship)
image.png

例如组织架构,其要素是什么?组成组织的要素当然是人,结构呢?结构是人与人之间的关系。因此,组织架构就是关于定义人的职责划分,以及人与人之间协作关系的一种设计方法。

同样,对于应用架构而言,代码是其核心组成要素,结构就是这些代码该如何被组织,也就是要如何处理模块(Module)、组件(Component)、包(Package)和类(Class)之间的关系。简而言之,应用架构就是要解决代码要如何被组织的问题。
compo.png

一个没有架构的应用系统,就像一堆随意堆放、杂乱无章的玩具,只有熵值,没有熵减。而一个有良好架构的应用系统,有章法、有结构,一切都显得紧紧有条。
image.png

好的组织架构会遵循一定的架构模式,大部分的组织都会按职能和业务来设计自己的架构。如果你反其道而行之,硬要把销售、财务和技术人员放在一个部门,就会显得很奇怪。

同样,好的应用架构,也遵循一些共同模式,不管是六边形架构、洋葱圈架构、整洁架构、还是COLA架构,都提倡以业务为核心,解耦外部依赖,分离业务复杂度和技术复杂度

应用架构的本质,就是要从繁杂的业务系统中提炼出共性,找到解决业务问题的最佳共同模式,为开发人员提供统一的认知,治理混乱。帮助应用系统“从混乱到有序”,COLA架构就是为此而生,其核心职责就是定义良好的应用结构,提供最佳实践

COLA 架构

自从COLA诞生以来,已经被使用在很多的业务系统里面,有CRM的业务,有电商的业务,有物流的业务,有外卖业务,有排课系统… COLA作为应用架构,有一定的普适性,是因为业务问题都有一定的共性。例如,典型的业务系统都需要:

  • 接收request,响应response;
  • 做业务逻辑处理,像校验参数,状态流转,业务计算等等;
  • 和外部系统有联动,像数据库,微服务,搜索引擎等;

正是有这样的共性存在,才会有很多普适的架构思想出现,比如分层架构、六边形架构、洋葱圈架构、整洁架构(Clean Architecture)、DDD架构等等。

这些应用架构思想虽然很好,但我们很多同学还是“不讲Co德,明白了很多道理,可还是过不好这一生”。问题就在于缺乏实践和指导。COLA的意义就在于,他不仅是思想,还提供了可落地的实践。应该是为数不多的应用架构层面的开源软件。

分层结构

假如你是一个公司的CTO要管100号人,你怎么管?按照管理学的定义,一个人的管理幅度如果超过10个,管理就会变得很困难。因此,管100号人,你可以把他们分成10个小组,这样你管理10个小组长就好了。

所有的复杂系统都会呈现出层级结构,管理如此,软件设计也不例外,你能想象如果网络协议不是四层,而是一层,意味着,你要在应用层去处理链路层的bit数据流会是怎样的情景吗?同样,应用系统处理复杂业务逻辑也应该是分层的,下层对上层屏蔽处理细节,每一层各司其职,分离关注点,而不是一个ServiceImpl解决所有问题

对于一个典型的业务应用系统来说,COLA会做如下层次定义,每一层都有明确的职责定义:

image.png

1)适配层(Adapter Layer):负责对前端展示(web,wireless,wap)的路由和适配,对于传统B/S系统而言,adapter就相当于MVC中的controller;

2)应用层(Application Layer):主要负责获取输入,组装上下文,参数校验,调用领域层做业务处理,如果需要的话,发送消息通知等。层次是开放的,应用层也可以绕过领域层,直接访问基础实施层;

3)领域层(Domain Layer):主要是封装了核心业务逻辑,并通过领域服务(Domain Service)和领域对象(Domain Entity)的方法对App层提供业务实体和业务逻辑计算。领域是应用的核心,不依赖任何其他层次;

4)基础实施层(Infrastructure Layer):主要负责技术细节问题的处理,比如数据库的CRUD、搜索引擎、文件系统、分布式服务的RPC等。此外,领域防腐的重任也落在这里,外部依赖需要通过gateway的转义处理,才能被上面的App层和Domain层使用。

包结构

分层是属于大粒度的职责划分,太粗,我们有必要往下再down一层,细化到包结构的粒度,才能更好的指导我们的工作。

还是拿一堆玩具举例子,分层类似于拿来了一个架子,分包类似于在每一层架子上又放置了多个收纳盒。所谓的内聚,就是把功能类似的玩具放在一个盒子里,这样可以让应用结构清晰,极大的降低系统的认知成本和维护成本
image.png

那么,对于一个后端应用来说,应该需要哪些收纳盒呢?这一块的设计真可谓是费了老鼻子劲了,基本上每一次COLA的迭代都会涉及到包结构的调整,迭代到现在,才算基本稳定下来。
image.png

各个包结构的简要功能描述,如下表所示:

层次 包名 功能 必选
Adapter层 web 处理页面请求的Controller
Adapter层 wireless 处理无线端的适配
Adapter层 wap 处理wap端的适配
App层 executor 处理request,包括command和query
App层 consumer 处理外部message
App层 scheduler 处理定时任务
Domain层 model 领域模型
Domain层 ability 领域能力,包括DomainService
Domain层 gateway 领域网关,解耦利器
Infra层 gatewayimpl 网关实现
Infra层 mapper ibatis数据库映射
Infra层 config 配置信息
Client SDK api 服务对外透出的API
Client SDK dto 服务对外的DTO

你可能会有疑问,为什么Domain的model是可选的?因为COLA是应用架构,不是DDD架构。在工作中,很多同学问我领域模型要怎么设计,我的回答通常是:无有必要勿增实体。领域模型对设计能力要求很高,没把握用好,一个错误的抽象还不如不抽象,宁可不要用,也不要滥用,不要为了DDD而DDD。

问题的关键是要看,新增的模型没有给你带来收益。比如有没有帮助系统解耦,有没有提升业务语义表达能力的提升,有没有提升系统的可维护性和可测性等等。

模型虽然可选,但DDD的思想是一定要去学习和贯彻的,特别是统一语言、边界上下文、防腐层的思想,值得深入学习,仔细体会。实际上,COLA里面的很多设计思想都来自于DDD。其中就包括领域包的设计

前面的包定义,都是功能维度的定义。为了兼顾领域维度的内聚性,我们有必要对包结构进行一下微调,即顶层包结构应该是按照领域划分,让领域内聚。

也就是说,我们要综合考虑功能和领域两个维度包结构定义。按照领域和功能两个维度分包策略,最后呈现出来的,是如下图所示的顶层包节点是领域名称,领域之下,再按功能划分包结构。
image.png

例如,在我们刚刚上线的一个云店铺(cloudstore)项目中,按照COLA的分包策略,我们在每一个module下面首先按照领域做一个顶层划分,然后在领域内,再按照功能进行分包。
image.png

解耦

“高内聚,低耦合”这句话,你工作的越久,就越会觉得其有道理。

所谓耦合就是联系的紧密程度,只要有依赖就会有耦合,不管是进程内的依赖,还是跨进程的RPC依赖,都会产生耦合。依赖不可消除,同样,耦合也不可避免。我们所能做的不是消除耦合,而是把耦合降低到可以接受的程度。在软件设计中,有大量的设计模式,设计原则都是为了解耦这一目的。

在DDD中有一个很棒的解耦设计思想——防腐层(Anti-Corruption),简单说,就是应用不要直接依赖外域的信息,要把外域的信息转换成自己领域上下文(Context)的实体再去使用,从而实现本域和外部依赖的解耦。

在COLA中,我们把AC这个概念进行了泛化,将数据库、搜索引擎等数据存储都列为外部依赖的范畴。利用依赖倒置,统一使用gateway来实现业务领域和外部依赖的解耦

其实现方式如下图所示,主要是在Domain层定义Gateway接口,然后在Infrastructure提供Gateway接口的实现。
image.png

举个例子,假如有一个电商系统,对于下单这个操作,它需要联动订单服务、商品服务、库存服务、营销服务等多个系统才能完成。

那么在订单域,该如何获取商品和库存信息呢?最直接的方式,无外乎就是RPC调用商品和库存服务,拿到DTO直接使用就完了。

然而,商品域吐出的是一个大而全的DTO(可能包含几十个字段),而在下单这个阶段,订单所需要的可能只是其中几个字段而已。更合适的做法,应该是在订单域中,使用gateway对商品域和库存域的依赖进行解耦。
image.png

这样做有两个好处,一个是降低了对外域信息依赖的耦合;另一个是通过上下文映射(Context mapping),确保本领域边界上下文(Bounded context)下领域知识的完整性,实现了统一语言(Ubiquitous language)。

COLA Archetype

以上就是COLA架构的核心内容了。然而这么多module,这么多package,如果要手动去创建的话,是非常繁琐和费时的。为了能够快速创建满足COLA架构的应用,我创建了两个Maven Archetype。

  1. 一个是用来创建纯后端服务的archetype:cola-archetype-service。
  2. 一个是用来创建adapter和后端服务一体的web应用archetype:cola-archetype-web。

另外,你也可以使用阿里云的应用生成器去生成一个COLA应用,只是那边的版本没有同步更新,可能会老旧一点。

COLA组件

使用过老版本COLA的同学,应该知道,COLA除了架构之外,还提供了一些框架级别的功能,比如拦截器功能,扩展点功能等。

之前,这种框架功能和架构混淆在一起,会让人以为使用COLA,就必须要使用这些功能。实际上二者是可以分开使用的,也就是说,你可以单纯的使用COLA架构,而不使用任何COLA组件提供的功能也是完全没问题的

当然,我还是强烈推荐你可以有选择的使用这些COLA组件,毕竟这些组件都是我们在实际工作中的总结沉淀,其复用性和价值是被反复验证过的。

为了方便管理,以及更清晰的把架构和框架区分开来。在此次COLA 4.0的升级中,我把这些功能组件全部收拢到了cola-components下面。到目前为止,我们已经沉淀了以下组件:

组件名称 功能 版本 依赖
cola-component-dto 定义了DTO格式,包括分页 1.0.0
cola-component-exception 定义了异常格式, 主要有BizException和SysException 1.0.0
cola-component-statemachine 状态机组件 1.0.0
cola-component-domain-starter Spring托管的领域实体组件 1.0.0
cola-component-catchlog-starter 异常处理和日志组件 1.0.0 exception ,dto组件
cola-component-extension-starter 扩展点组件 1.0.0
cola-component-test-container 测试容器组件 1.0.0

这些组件是一个良好的开端,我相信,在未来会有更多有用的组件加入。当然,作为一个开源项目,如果你有好的组件idea,欢迎你随时为这个组件库添砖加瓦。

COLA 4.0

总结一下,在本次COLA升级中,我们进一步明确了架构和框架功能的定义。升级之后,如下图所示,COLA会被分成COLA架构和COLA组件两个部分:

  1. COLA架构:关注应用架构的定义和构建,提升应用质量。
  2. COLA组件:提供应用开发所需要的可复用组件,提升研发效率。

在这里插入图片描述

COLA 开源地址: https://github.com/alibaba/COLA

你可以按照以下步骤去使用COLA:

** 第一步:安装 cola archetype **
下载cola-archetypes下的源码到本地,然后本地运行mvn install安装。

** 第二步:安装 cola components **
下载cola-components下的源码到本地,然后本地运行mvn install安装。

** 第三步:创建应用 **
执行以下命令:

1
2
mvn archetype:generate  -DgroupId=com.alibaba.demo -DartifactId=demoWeb -Dversion=1.0.0-SNAPSHOT -Dpackage=com.alibaba.demo -DarchetypeArtifactId=cola-framework-archetype-web -DarchetypeGroupId=com.alibaba.cola -DarchetypeVersion=4.0.0
1

命令执行成功的话,会看到如下的应用代码结构:
demo

** 第四步:运行应用 **
首先在demoWeb目录下运行mvn install(如果不想运行测试,可以加上-DskipTests参数)。然后进入start目录,执行mvn spring-boot:run。
运行成功的话,可以看到SpringBoot启动成功的界面。

生成的应用中,已经实现了一个简单的Rest请求,可以在浏览器中输入 http://localhost:8080/helloworld 进行测试。


COLA-项目
http://example.com/2023/09/10/模式-思想/COLA-项目/
作者
where
发布于
2023年9月10日
许可协议