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

阅读说明:

如果有排版格式问题,请移步 [《业务单系统架构设计心得(一)》](https://www.yuque.com/mrhuang-ire4d/oufb8x/ilud3u0w74sthfpo?singleDoc# 《业务单系统架构设计心得(一)》),选择宽屏模式效果更佳。
本文为原创文章,转发请注明出处。

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

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

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等服务。在层级上跟数据访问层处于同一层级。输出指令层也是跟外部服务进行交互,如有必要进行参数映射,防止下游定义变更,带来的大批量修改灾难。

业务复用

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

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

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

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

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

扩展点

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

链式扩展

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

image

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

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

星型扩展

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

星型扩展逻辑简化如下:

if (命中业务A) {
	执行ExtensionOne;
} else if (命中业务B) {
	执行ExtensionTwo;
} else {
	执行ExtensionThree;
}

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

参考:
[1]. https://zhuanlan.zhihu.com/p/479800537
[2].https://medium.com/expedia-group-tech/onion-architecture-deed8a554423
[3].https://mp.weixin.qq.com/s/pNfC7klCZTKhXwC4t5V7BA
[4].https://mp.weixin.qq.com/s/F42LqQncMDLQH-WWmZ28fA
[5].https://mp.weixin.qq.com/s/y-RBStzS0jNlbkljRP_MrA

posted @ 2024-01-15 12:01  扎Zn了老Fe  阅读(88)  评论(0编辑  收藏  举报