如何解决复杂架构问题?
天下难事必作于易。
软件架构中很重要的一个思想就是:“分治”。
当一个问题或是架构异常复杂时,想到的最简单的方式就是把复杂问题拆解成一个个可解的简单问题。
比如日常工作中处理一个较为复杂的业务系统,可以通过拆解到更精细的粒度一个个环节解决。
通过流程化方式梳理套路:
业务拆解 》复杂度来源 》 核心挑战点 领域驱动设计 》 业务过程分析 》 领域模型抽象 》 模型分解 分层组织 》 工程架构 》 模块化 》 组件化 考虑功能复用 》 可选路径:业务身份、能力定义、扩展点定义、工程流程、编排 方案产出 》 整体 -》模块 -》流程 -》 细节 》 方案评审 》 最终方案
针对于功能复用,看下大部分业务中台的解决思路。
复杂度来源:
架构设计的目的是为了解决软件系统的复杂度带来的问题
所以在架构设计时,首先要分析的就是系统的复杂度。只有正确的分析了系统的复杂性,后续的架构设计方案才不会偏离方向。否则,如果对于复杂性有了错误的判断,即使后续的设计方案再先进,都是做得多、错的多。
比如一个偏运营的系统,其复杂度在于复杂业务带来的业务复杂度。如果架构师将复杂度定义为高QPS,那就没有解决核心问题,反而引入新的问题。
解决核心问题:
射人先射马,擒贼先擒王
确定了系统的复杂度,就确定了系统设计的难点,在进行系统设计时,可以把难点列出来,各个击破。
比如营销系统的复杂度来源于营销形态的变化,营销形态千变万化,每次促销都有不同的玩法和定义。所以营销系统需要对营销形态有所抽象,任何活动或优惠手段都是基于这个营销模型建立起来的。
领域驱动,复杂架构治理的第一个方法论。
底层模型的抽象,可以通过DDD方式对领域模型进行抽象。促销引擎,编排之外考虑性能问题,可以采用缓存、异步、最终一致性等。系统之间的交互,要理清关系和依赖。
每个系统都有其需要解决的核心问题域,一个领域下的多个解决类似问题的系统同属于一个核心域。
只要确定了系统所属的域,那么这个系统的核心业务和关键问题基本就确定了。
DDD里面有领域专家的概念,领域专家要深入领域研究,只有这样才会遇到非常多该领域下的问题,积累出比较丰富的经验,所以相关行业经验非常重要,不要光看着技术实现,要积累业务问题。
通常一个领域下只有一个核心问题,也就是核心域。当然领域下还有其他的域,比如通用域、支撑域,在梳理域的过程中,就是定义子域边界的过程。
复杂架构落地过程中可以参考一些架构模型。
常见的有三层架构:
领域驱动设计时的分层模型:
各层定义说明:
整洁架构模式:
Robert Martin 的整洁架构将领域模型放在整个系统的核心,这一方面体现了领域模型的重要性,另外一方面也说明了领域模型应该与具体的技术实现无关。
领域模型就是业务逻辑的模型,它应该是完全纯粹的,无论你选择什么框架,什么数据库,或者什么通信技术,按照整洁架构的思想都不应该去污染领域模型。
如果以 Java 语言来实现,遵循整洁架构的设计思想,则所有领域模型对象都应该是 POJO(Plain Ordinary Java Object)(具有领域逻辑的 POJO 对象)。
插件化领域分层架构:
DDD的核心诉求就是将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。
微服务追求业务层面的复用,设计出来的系统架构和业务一致,不过领域模型并不直接反映数据结构,需要明确这一点。
领域驱动设计最后落地到数据存储上,不需要直接参考领域模型,在最后的技术架构上可以自由选择合适的技术架构。
Java项目一般是典型的Maven多模块项目,可以使用不同的Module,区分各个层次,进一步,通过Package来控制DDD中的限界上下文。
如何实现功能复用?
编程DRY原则
编写整洁代码,有一个非常重要的原则就是DRY,Don't Repeat Yourself,避免产生重复代码,有经验的程序员都能够意识到这一条约束。
如果你使用Idea开发,Idea也会识别并且提示你重复的代码,建议你进行抽象。
DRY它相信更少的代码是好的,它节省了时间和精力,易于维护,并且减少了bug的几率。
除了在软件开发领域,在业务系统层面,也存在如何避免重复能力建设,考虑业务复用的问题。
业务层面的DRY
业务系统层面的DRY原则,其实可以总结为一个问题,就是复杂业务系统,如何实现具有共性的业务能力的复用,这个也是很多业务中台关注的问题。
一般的,大部分业务中台(特指业务中台,不包括数据中台等其他形态)对业务复用的方式,都可以通过定义业务身份 ——> 梳理扩展点 ——> 枚举业务能力 ——> 根据不同业务身份编排工作流 ——> 实现业务能力复用,这样的流程来实现。
可以对比编程中的Pipeline模式,或者责任链模式,只不过每个链条上负责处理输入和输出的,是不同的业务功能。
可扩展性和过度设计如何平衡?好的架构设计一定是扩展简单的。
在设计时,要尽量封装可能的变化,在业务流程发生一些调整时,能够比较方便地修改系统程序模块或组件间的调用关系而实现新的需求,也就是我们常说的可扩展性。
但是可扩展性本身也是系统设计的复杂度来源之一,这就涉及到一个问题,如何平衡可扩展和过度设计。
区分确定性和变化。
好的架构一定是扩展简单,运行平稳的。
系统架构最开始可以从一个通用的流程开始,case-by-case,然后将「变化少」的部分沉淀下来为架构,将「变化多」的沉淀为扩展或者配置,梳理清楚,将这两者结合起来,最后完成系统架构设计。
用容量规划的方式来处理扩展程度。
可以使用容量规划的思想,来处理可扩展性设计。
在做技术方案时,容量规划是一个特别重要的环节,要预估未来几年的增长量,进行数据库和缓存的容量规划。
我觉得这个方式也可以应用在扩展性设计上,对业务变化进行预期,考虑技术方案能够支持的业务发展时间。
方案评审 好的技术方案很难一蹴而就,大部分时候要经过反复的调整,就是需要关联的各方参与方案的评审和修改,最终确定最终技术方案。
复杂业务系统开发的一些体会:
- 熟悉业务,抽象产品需求,分析相关测试用例,了解各种用户角色和其使用的场景自顶向下进行方案设计,对于比较复杂的业务系统,比较好的方式是先关注顶层模型,避免在一开始就陷入技术和业务细节中去
- 从整体设计,到模块局部规划,设计好部署架构、分层和分模块、API设计、数据库设计等
- 可以参考成熟的解决方案,比如将开源软件,改造,变成适合自己业务需求的架构
- 验证和优化架构设计方案,完整的架构设计方案,需要有多次的评审,充分收集各方面的反馈,反复修改后确定
- 合理进行扩展,考虑架构预期能满足多长时间的业务增长,比如未来一年的业务变化。