业务单系统架构设计心得(一)

阅读说明:

  1. 如果有排版格式问题,请移步 《业务单系统架构设计心得(一)》,选择宽屏模式效果更佳。
  2. 本文为原创文章,转发请注明出处。

作为一个业务开发工程师,工作中最多的是需求开发,把需求从ppt到落地实现。随着工作的深入,工作面也变得更广和更深,需要面对更多的系统,更加复杂的场景。这时完成功能实现是基本要求了,想要开发能力上一个台阶,需要做好架构设计。常见的架构有:洋葱架构、六边形架构、整洁架构、SOA架构、CQRS架构等等。

洋葱架构
六边形架构

这些架构都有一个共同点,分层。针对业务开发系统,本文总结如下业务架构,总共分为5部分:api层、流程层、服务层、数据访问层、指令输出层。

业务单系统架构分层图

1.api层 功能:隔离本系统与外部其他系统的交互,交互方式有rpc接口,http,mq, 定时任务等方式。 api层接收和处理入口参数以及凭借和转换参数,映射为内部服务,并处理内部服务出参。api层不只是做转换,还需要理解内部服务的模型定义和领域能力,对参数建立合理的模型。

api层在设计上需要尽量满足以下三个原则:

  1. api层尽可能薄。api层不要业务逻辑,业务逻辑下沉到流程层和服务层里面。
  2. 满足弹性设计,减少后续对api定义的改动。api层是跟外部交互,接口定义上尽量减少变动。弹性设计方法有:
  • 对入参和出参使用一个大对象包装,后续变更参数时,变动收缩在对象内部;
  • 使用Map来传递一些可变参数;
  • 通过List实现参数规模的弹性,不断丰富Condition模型来支持更多的参数结构和含义,比如范围搜索、模糊搜索、并联或者互斥条件含义。
  1. 参数归一化。所有外部的请求,业务上一般需要记录上游方的调用信息,方便后面追溯请求和做业务监控。因而,需要抽离出公共模型,专门记录调用方相关信息。所有返回结果,根据是否成功有可能性:成功,失败。 在定义成功和失败上,为了便于上游,需要统一成功和失败的code,在设计返回结果时需要抽离出一个标准返回模型,统一定义成功和失败的code。比如http,只有200才表示成功,其他错误码都是不同失败原因。
  2. 上游参数映射:尽力隔离上游系统的领域内容,防止上游定义变更,带来的大批量修改灾难。

当然,api层可以做一些非业务功能,比如用户鉴权,接口限流。

2.流程层(biz层) 负责流程编排。将服务层的逻辑串联起来完成业务逻辑。流程层主要负责组织串联服务,可以将组织串联功能沉淀出基础组件功能便于复用。 流程层可以留有简单的业务逻辑,如果本身不复杂,业务逻辑可以直接放到流程层,不必下沉到服务层再被流程层调用,业务架构设计中可根据情况具体分析。

3.服务层(service) 系统核心模型和能力弹性的承载层,是系统功能和扩展性张力来源。服务层内部要进行划分成不同的功能区,划分方法可参考DDD,原则上各个功能区是独立的,不应存在跨功能区调用的情况。

4.数据访问层 对接数据存储,对外提供统一的接口,屏蔽存储实现,数据存储有mySQL, Redis, ES等。

5.输出指令层 输出指令数据,方式有RPC, MQ, 以及http等服务。在层级上跟数据访问层处于同一层级。输出指令层也是跟外部服务进行交互,如有必要进行参数映射,防止下游定义变更,带来的大批量修改灾难。

业务复用

随着系统壮大,越来越多新的功能加入,如果新功能跟现有功能在流程上具备相似之处,从原则上应该复用现有逻辑。复用现有逻辑,一方面能够减少开发量,只需要专注差异点的开发,能够快速交付上线;另一方面,从长远来看,便于维护,无需多次改动。

不同业务具备公共业务点和差异业务点,业务复用可以在两个地方实现:流程层重新编排和服务层子服务开启差异点扩展。

流程层重新编排是指各个业务线在流程层各自写一套服务编排逻辑,被编排的子服务可以分为公共子服务和差异子服务, 公共业务点放到公共子服务中,差异业务点放到差异子服务。

流程重新编排示意图

服务层子服务开启差异点扩展是指流程编排是相同的,在子服务中根据不同的业务线实现差异点,为了在子服务中实现差异点,需要在流程上下文中带上业务标识。

流程层重新编排适合差异点比较大的情况下,差异子服务并非一个服务的不同实现,这种情况下将差异点直接编排成子服务更合适。服务层子服务开启差异点适合差异比较小的场景,对于某个功能点各个服务都有自己不同的实现,这种有个好处是便于定义标准扩展接口,做成SPI还能够支持动态扩展。实际业务线采用哪种方式要根据具体业务来定,或者两种都采用。

扩展点

一个系统能够成为优秀系统,扩展能力是一个必备的能力。良好的扩展能力不仅能够帮助系统提高扩展,增强适应性,还能够简化系统结构,代码更加优雅。java中提供了多种机制对程序功能进行扩展,如继承,组合,多态,接口,内部类等,框架类比如Netty, Spring同样提供了大量自定义扩展点。同理,在业务系统架构设计中,剥离出变动部分,并设计为扩展点的形式,方便后续迭代。

链式扩展

用更形象的表述,就如数据结构中的链表一样,链表中的节点为扩展节点,链表将多个扩展节点串联起来执行。链式又可以分为单链和双链。单链中每个节点只有一个处理器,双链每个节点分为前置和后置处理器,双链在执行上,节点前置处理器先正序执行,执行完后,后置处理器再逆序执行。

单链扩展点执行图
双链扩展点执行图

链式扩展由扩展节点(扩展点的具体实现), 扩展节点链组成。链式扩展的扩展节点实现具备同等地位,图中ExtensionOne, ExtensionTwo, ExtensionThree都是对同一扩展点的不同实现。注册Extension时,将扩展节点编排成链,如果扩展节点无前后依赖关系,那么节点执行顺序可以任意编排。当然,有些扩展点是有前后的依赖关系,这种情况下就必须指定节点执行顺序。在Spring中,注册Extension变得更加简单。直接自动从Spring中上下文中找到所有扩展点的实现节点,在修改节点时,无需改动注册逻辑。

链式扩展应用广泛,在SpringMVC中,实现拦截器,提供日志打印,权限校验等工作。在订单业务系统中,也有很多应用。比如订单拆单流程,业务上有很多拆单规则比如大小件拆开,按照发货仓库拆开等等,一个订单经过拆单规则后,将拆分成多个子单。拆单规则通常是动态变化的,这里就非常适合采用链式扩展点。

SpringMvc拦截器示意图

星型扩展

星型扩展组成上有一个分发器和多个扩展节点。分发器根据分发条件分发到响应的扩展点进行处理。

星型扩展组成

星型扩展逻辑简化如下:

ini 复制代码
if (命中业务A) {
	执行ExtensionOne;
} else if (命中业务B) {
	执行ExtensionTwo;
} else {
	执行ExtensionThree;
}

星型扩展的应用也很多,上一节中的业务复用,在服务层子服务开启业务复用,需要开启星型扩展来处理不同业务的差异点。 SPI虽然没有明显分发器,但也属于星型扩展的方式。

参考: [1].zhuanlan.zhihu.com/p/479800537 [2].medium.com/expedia-gro... [3].mp.weixin.qq.com/s/pNfC7klCZ... [4].mp.weixin.qq.com/s/F42LqQncM... [5].mp.weixin.qq.com/s/y-RBStzS0...

相关推荐
Lee川9 小时前
深度拆解:基于面向对象思维的“就地编辑”组件全模块解析
javascript·架构
勤劳打代码9 小时前
Flutter 架构日记 — 状态管理
flutter·架构·前端框架
子兮曰15 小时前
后端字段又改了?我撸了一个 BFF 数据适配器,从此再也不怕接口“屎山”!
前端·javascript·架构
卓卓不是桌桌17 小时前
如何优雅地处理 iframe 跨域通信?这是我的开源方案
javascript·架构
Qlly17 小时前
DDD 架构为什么适合 MCP Server 开发?
人工智能·后端·架构
用户881586910912 天前
AI Agent 协作系统架构设计与实践
架构
鹏北海2 天前
Qiankun 微前端实战踩坑历程
前端·架构
货拉拉技术2 天前
货拉拉海豚平台-大模型推理加速工程化实践
人工智能·后端·架构
RoyLin2 天前
libkrun 深度解析:架构设计、模块实现与 Windows WHPX 后端
架构
CoovallyAIHub3 天前
实时视觉AI智能体框架来了!Vision Agents 狂揽7K Star,延迟低至30ms,YOLO+Gemini实时联动!
算法·架构·github