《微服务架构设计模式》第二章读书笔记:服务的拆分策略
微服务的架构
应用程序的架构是将软件分解为元素和这些元素之间的关系。
软件架构的4+1视图模型

软件架构的 4+1 视图,是从 4 个完全不同的角度描述一套系统,再加 1 套场景把全部内容串起来:
- 逻辑视图(写给开发看) 就是我们写代码时的类、包,只关心业务代码怎么划分、类之间怎么关联(继承、引用、依赖),不考虑打包、运行、服务器。
- 包:
com.ftgo.order.domain - 类:Order(订单实体)、CreateOrderCommand、OrderService 关系:OrderService 依赖 Order 类,CreateOrderCommand 和 Order 是关联关系。
- 实现视图(打包编译视角) 代码写完打包后的产物,Java 里就是 JAR、WAR 包;一个可运行程序(组件)由多个代码包(模块)拼出来,这里只讲包和包之间的依赖、组合关系。
- 模块 1:
order-domain.jar(包含上面 Order 实体类) - 模块 2:
order-service.jar(包含 OrderService) - 组件:
ftgo-order.war(可部署组件,把上面两个 jar 包打包在一起) 关系:order-service.jar依赖order-domain.jar;war 组件由两个 jar 模块组合而成。
- 进程视图(程序跑起来的样子) 系统上线运行后,每一个后台程序都是独立进程;这个视图只描述各个进程之间怎么互相调用、通信。
- 部署视图(服务器硬件视角) 进程要装在哪台电脑 / 云服务器上,机器之间靠网络连通;记录进程和服务器、服务器和服务器的对应关系。
- +1:场景(串联所有视图的主线) 前面 4 个视图都是孤立的切片,场景就是真实业务流程(比如用户下单),演示一套业务走下来,代码类、程序包、运行进程、服务器是怎么配合协作完成需求的。
简单总结:4 个视图分别看「代码设计、打包产物、运行程序、服务器部署」,场景用真实业务流程把这四者打通,完整讲清整套架构。
架构风格
特定的架构风格提供了有限的元素(组件) 和关系(连接器), 可以从中定义应用程序架构的视图。
分层架构
流行的三层架构是应用于逻 辑视图的分层架构构。 它将应用程序的类组织到以下层中:
- 表现层: 包含实现用户界面或外部API 的代码。
- 业务逻辑层: 包含业务逻辑。
- 数据持久化层: 实现与数据库交互的逻辑。
分层默认依赖规则:上层依赖下层(表现层调用业务层,业务层调用持久层 DAO 操作数据库)
// 持久层:MySQL 专属实现类(绑死数据库)
public class MysqlOrderDao {
public void insert(Order o) {
// 直接写JDBC操作MySQL
}
}
// 业务层 OrderService 直接 new 数据库实现类
public class OrderService {
// 直接依赖 MySQL 具体实现
private MysqlOrderDao dao = new MysqlOrderDao();
public void createOrder() {
dao.insert(new Order());
}
}
OrderService 的代码里写死了 MysqlOrderDao,代码强绑定 MySQL 数据库。 你想跑单元测试,创建 OrderService 对象时,它内部自动创建 MySQL DAO; 一旦执行 dao.insert(),代码就会去连接真实 MySQL,没有数据库环境直接报错。
痛点:Service 绑死 MySQL,单元测试必须启动数据库。
老式分层图画成「业务依赖数据库实现」,但规范设计里,是数据库实现反过来依赖业务写的接口,依赖流向完全反过来了。
扩展:正确依赖流
流向 1:前端 / 接口调用流程(用户下单走这条)
Controller(表现层) →【依赖】 Service(应用服务) → 业务接口 OrderRepository
- Controller 接收用户下单请求;
- Service 编排下单流程,只调用业务接口规定的 save 方法;
- Service 全程不知道底层是 MySQL、内存假数据还是 Oracle。
流向 2:数据库实现依赖流程(技术实现依赖业务)
MysqlDAO(持久层实现) → 业务接口 OrderRepository
- MySQL 代码只是去实现业务写好的接口;
- 数据库代码必须遵守业务定好的 save、findById 方法;
- 是数据库代码依赖业务接口,业务代码不依赖 MySQL。
克服这些弊端的替代架构:六边形架构风格
此架构的一个关键特 性和优点是业务逻辑不依赖于适配器。 相反, 各种适配器都依赖业务逻辑。

- 入站端口为业务逻辑对外提供的调用入口,供外部系统发起请求,例如服务对外暴露的公共 API 接口;【外人来找我】
- 出站端口是业务逻辑访问外部资源的抽象规范,用于对接外部依赖系统,仓储 Repository 接口便是典型示例,用于定义数据读写相关操作。【我要去找外部工具 / 系统。】
六边形架构风格的一个重要好处是它将业务逻辑与适配器中包含的表示层和数据访问层 的逻辑分离开来。 业务逻辑不依赖于表示层逻辑或数据访问层逻辑。
六边形架构和分层架构(三层架构)构成了软件的逻辑视图。 现在让我们将微服务架构定义为构成软件的实现视图的架构风格。
微服务架构是一种架构风格
它的实现视图由多个组件构成: 一组可执行文件或 WAR 文件。 它的组件是服务, 连接器是使这些服务能够协作的通信协议。 每个服务都有自己的逻辑视图架构, 通常也是六边形架构。

服务是一个单一的、可独立部署的软件组件, 它实现了一些有用的功能。服务具有API, 为其客户端提供对功能的访问。 有两种类型的操作:命令和查询。 API由命令、 查询和事件组成。
微服务架构中的每项服务,其API由与服务的业务逻辑交互的适配器实现。

为了使用微服务架构开发软件, 你首先需要识别服务, 并确定它们之间如何协作。
为应用程序定义微服务架构
一开始我们需要拿到领域专家或者现有应用的需求文档

- 应用程序是用来处理客户端请求的, 因此定义其架构的第一步是将应用程序的需求提炼 为各种关键请求。系统操作 (systemoperation) 是应用程序必须处理的请求的一种抽象描述。
- 第二步是确定如何分解服务。一种是定义与业务能力相对应的服务。另一种策略是围绕领域驱动设计的子域来分解和设计服务。 但这些策略的最终结果都是围绕业务概念而非技术概念分解和设计的服务。
业务分解障碍
- 网络延迟
- 服务之间的同步通信降低了可用性(可用自包含服务的概念)
- 需要维护跨服务的数据一致性(可用Saga)
- 上帝类(可用领域驱动设计中的概念)
识别系统操作
两步式流程

领域模型主要源自用户故事中提及的名词, 系统操作主要来自用户故事中提及的动词。每个系统操作的行为都是根据它对一个或多个领域对象的影响以及它们之间的关系来描述的。系统操作可以创建、更新或删除领域对象, 以及创建或破坏它们之间的关系。
有以下两种类型的系统操作:
- 命令型: 创建、更新或删除数据的系统操作。
- 查询型: 查询和读取数据的系统操作。
识别系统指令的切入点是分析用户故事和场景中的动词。

命令规范定义了命令对应的参数、返回值和领域模型类的行为。 行为规范中包括前置条件(即当这个操作被调用时必须满足的条件)和后置条件(即这个操作被调用后必须满足的 条件)。例如, 以下就是createOrder( ) 系统操作的规范。

根据业务能力进行服务拆分
业务能力是指一些能够为公司 (或组织)产生价值的商业活动。业务能力通常集中在特定的业务对象上。能力通常可以分解为子能力。
FTGO的业务能力如下图:

一旦确定了业务能力, 就可以为每个能力或相关能力组定义服务。图2-8 显示了FTGO 应用程序从能力到服务的映射。(非常主观的判断)

围绕能力组织服务的一个关键好处是,因为它们是稳定的,所以最终的架构也将相对稳 定。 架构的各个组件可能会随着业务的具体实现方式的变化而发展,但架构仍保持不变。此外,注意在架构定义流程中的一个重要步骤是调查服务如何在每个关键架构服务中协作。
根据子域进行服务拆分
通常都以面向对象和领域模型为核心,DDD(Domain-Driven Design,领域驱动设计)有两个特别重要的概念:
- 子域
- 限界上下文
领域驱动为每一个子域定义单独的领域模型。子域是领域的一部分,并把子域对应为每一个服务,FTGO的子域包括:订单获取、 订单管理、 餐馆管理、 送餐和会计。
DDD把领域模型的边界称为限界上下文(boundedcontext)。 限界上下文包括实现这个模型的代码集合。

拆分的指导原则
- 在定义类的职责时, 应该遵循单一职责原则【SRP】
- 把类组成包时, 应该遵循闭包原则【CCP】
按业务能力和子域以及单一职责原则和闭包原则进行分解是将应用程序分解为服务的好方法。
拆分单体应用为服务的难点
- 网络延迟。
- 同步进程间通信导致可用性降低。
- 在服务之间维持数据一致性。
- 获取一致的数据视图。
- 上帝类阻碍了拆分。
上帝类阻碍了拆分
图 2- 10显示了使用传统建模技术创建的Order类的结构。

应用DDD并将每个服务视为具有自己的领域模型的单独子域。



扩展:
应用程序必须在用 户体验 (即其自己的领域模型) 与每个服务的领域模型之间进行转换。在FTGO应用 程序中,向消费者显示的Order状态来自存储在多个服务中的Order信息。 这种转换通常 由 API Gateway处理。
定义服务API
到目前为止,我们有一个系统操作列表和一个潜在服务列表。 下一步是定义每个服务的 API : 也就是服务的操作和事件。
定义服务API ,先把业务操作分给对应服务,再判断是否要调用别的服务;有调用需求就定义对方配套接口,下文先讲操作怎么分给服务。
把系统操作分配给服务
考虑使用noteUpdatedLocation( )操作来更新送餐员的位 置。 一方面, 因为它与送餐员有关, 所以应该将此操作分配给Courier Service。另一方 面, 它是需要送餐地点的Delivery Service。
分配系统操作时,分不清归属就看两点:优先分给要使用该数据的服务,或是本身持有对应业务数据的服务。

确定支持服务协作所需要的API
你需要分析每个系统操作并确定所需的协作。

到目前为止, 我们已经确定了每项服务实现的服务和操作。 但重要的是要记住, 我们勾勒出的架构非常抽象。 我们没有选择任何特定的进程间通信技术。
小结
软件架构能够决定系统的可维护性、可测试性、可部署性、可扩展性等非功能特性,直接影响软件开发与迭代效率。微服务架构作为一种优秀的架构风格,能够显著提升系统的各项非功能能力。微服务基于业务需求拆分服务,以业务能力或DDD子域为依据,而非按照技术层级划分,主要包含业务能力分解和子域分解两种拆分模式。同时,通过落地领域驱动设计,为每个微服务独立构建领域模型,可以规避单体系统中上帝类造成的代码耦合、依赖交织问题,扫清服务拆分的障碍。
更多推荐

所有评论(0)