最近参与了一些电商业务中台等复杂业务系统的设计和开发,结合DDD和中台等,程序员
有一些架构方面的思考和体会,在这里记录一下。数据库
作技术方案,核心是下面几个问题:编程
作什么?- 产品需求缓存
业务上怎么作?- 业务文档数据结构
技术上怎么作?- 技术方案架构
代码怎么实现?- 落地实现框架
明确了这几个问题,能够处理大部分平常需求开发,若是是比较复杂的业务系统,就须要拆解的更精细。异步
好比电商的商品管理、订单交易、促销活动营销中心等系统的开发和重构,业务相对复杂,开发人天在几个月以上,直接开发可能会老虎啃天,无从下手。数据库设计
这时候能够经过一个流程化的模板来指导,若是抽象一个通用的流程,能够参考下面的套路:模块化
业务拆解 > 复杂度来源 > 核心挑战点
领域驱动设计 > 业务过程分析 > 领域模型抽象 > 模型分解
分层组织 > 工程架构 > 模块化 > 组件化
考虑功能复用 > 可选路径 —( 业务身份,能力,扩展点,工做流程,编排)
方案产出 > 总体-模块-流程-细节 > 方案评审 > 最终方案
其中的功能复用环节,是包括阿里在内的大部分业务中台的解决思路,仅供参考。
为何要关注复杂度?
我比较认同系统设计中「软件复杂度」的观点,架构设计的目的是为了解决软件系统的复杂度带来的问题,因此在设计架构时,首先就要分析系统的复杂度。
只有正确分析出了系统的复杂性,后续的架构设计方案才不会偏离方向;
不然,若是对系统的复杂性判断错误,即便后续的架构设计方案再完美再先进,都是南辕北辙,作的越好,错的越多、越离谱。
举个例子,医院管理应用的医疗管理系统(HIS),复杂度在于业务逻辑复杂,系统之间调用不清晰,
若是你设计一个QPS几万的高性能架构,就是没有解决系统的核心问题。
正确的作法是将主要的复杂度问题列出来,而后根据业务、技术、团队等综合状况进行排序,优先解决当前面临的最主要的复杂度问题。
射人先射马,擒贼先擒王。
肯定了复杂度,也就肯定了系统设计的难点,在进行系统设计时,能够把难点列出来,各个击破。
以优惠券为例,促销活动最大的复杂度来自营销形态的变化,营销最大的不变就是变,乱花渐欲迷人眼,各种促销方式变幻无穷。
每次促销活动都有不一样的玩法和定义,促销系统的设计必须对促销模式有所抽象,任何活动或优惠手段都是基于最基本的促销模式而创建的。
电商促销活动和优惠券建设难点包括:
底层模型抽象:底层模型抽象能够经过DDD的方式,对领域模型和进行抽象。
促销引擎性能:性能问题如何解决?已是老生常谈,工程领域有不少经典的解决方案,好比缓存,异步,最终一致性。
关联系统交互:理清和关联系统的交互
软件系统的目的反映在业务上,都是来解决一系列问题,例如考试系统完成考试,电商就是卖货,
同一个领域的系统都具备相同的核心业务,由于他们要解决的问题的本质是相似的,一个领域本质上能够理解为一个问题域 。
只要肯定了系统所属的领域,那么这个系统的核心业务,即要解决的关键问题就基本肯定了。
任何一个系统都会属于某个特定的领域,例如论坛系统,核心功能是肯定的,好比用户发帖,回帖等基本功能。
广义的电商系统也是一个领域,作电商业务,必需要支持的商品,订单,交易,物流等功能。
DDD里有领域专家的概念,领域专家要在这个领域深刻研究,只有这样才会遇到很是多的该领域的问题,积累比较更加丰富的经验。
一般来讲,一个领域有且只有一个核心问题,也就是核心子域。在核心子域、通用子域、支撑子域梳理的同时,会定义出子域中的限界上下文及其关系,用它来阐述子域之间的关系 。
以电商营销为例,优惠券、抽奖、套餐等,都是围绕这个促销这个业务范围来进行的,在促销域以外,还有相关的用户、商品、订单、风控、商家等。
下图是领域驱动设计中经典的分层架构:
用户界面/展现层:
请求应用层获取用户所需的展现数据;
发送命令给应用层执行用户的命令
应用层:
薄薄的一层,定义软件要完成的任务。
对外为展现层提供各类应用功能,对内调用领域层(领域对象或领域服务)完成各类业务逻辑。应用层不包含业务逻辑
领域层:
表达业务概念、业务状态信息及业务规则,是业务软件的核心
基础设施层:
为其余层提供通用的技术能力,提供了层间通讯;为领域层提供持久化机制。
这是一个相对简单的分层架构,其实已经老生常谈,那么问题来了,咱们在上面拆解的领域模型,如何映射到更加复杂的工程架构中?
DDD的核心诉求就是将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。
微服务追求业务层面的复用,设计出来的系统架构和业务一致,不过领域模型并不直接反映数据结构,须要明确这一点。
领域驱动设计最后落地到数据存储上,不须要直接参考领域模型,在最后的技术架构上能够自由选择合适的技术架构。
Java项目通常是典型的Maven多模块项目,可使用不一样的Module,区分各个层次,进一步,经过Package来控制DDD中的限界上下文。
对具体的领域对象封装时,典型的有充血和贫血模型,因为大部分程序员习惯在Service里封装业务逻辑的贫血模型,彻底的充血模型开发效率相对较低,
我本身的体会是,技术服务业务第一,在开发时能够灵活的选择实现策略,模型对象封装一些简单的静态方法,大部分业务逻辑仍是放在领域服务中实现。
你们都知道,编写整洁代码,有一个很是重要的原则就是DRY,
Don't Repeat Yourself,避免产生重复代码,有经验的程序员都可以意识到这一条约束。
若是你使用Idea开发,Idea也会识别而且提示你重复的代码,建议你进行抽象。
DRY的好处更少的代码是好的,它节省了时间和精力,易于维护,而且减小了bug的概率。
除了在软件开发领域,在业务系统层面,也存在如何避免重复能力建设,考虑业务复用的问题。
业务系统层面的DRY原则,其实能够总结为一个问题,就是复杂业务系统,如何实现具备共性的业务能力的复用,这个也是不少业务中台关注的问题。
通常的,大部分业务中台(特指业务中台,不包括数据中台等其余形态)对业务复用的方式,
均可以经过定义业务身份 ——> 梳理扩展点 ——> 枚举业务能力 ——> 根据不一样业务身份编排工做流 ——> 实现业务能力复用,这样的流程来实现。
能够对比编程中的Pipeline模式,或者责任链模式,只不过每一个链条上负责处理输入和输出的,是不一样的业务功能。
业务中台是另外一个话题,这部分是发散思考,具体能够参考阿里巴巴 TMF (Trade Mudule Framework)框架的介绍:
如何实现32.5万笔/秒的交易峰值?阿里交易系统TMF2.0技术揭秘
好的架构设计必定是扩展简单的。
在设计时,要尽可能封装可能的变化,在业务流程发生一些调整时,可以比较方便地修改系统程序模块或组件间的调用关系而实现新的需求,也就是咱们常说的可扩展性。
可是可扩展性自己也是系统设计的复杂度来源之一,这就涉及到一个问题,如何平衡可扩展和过分设计。
好的架构必定是扩展简单,运行平稳的。
系统架构最开始能够从一个通用的流程开始,case-by-case,
而后将「变化少」的部分沉淀下来为架构,将「变化多」的沉淀为扩展或者配置,梳理清楚,将这二者结合起来,最后完成系统架构设计。
可使用容量规划的思想,来处理可扩展性设计。
在作技术方案时,容量规划是一个特别重要的环节,要预估将来几年的增加量,进行数据库和缓存的容量规划。
我以为这个方式也能够应用在扩展性设计上,对业务变化进行预期,考虑技术方案可以支持的业务发展时间。
好的技术方案很难一蹴而就,大部分时候要通过反复的调整,就是须要关联的各方参与方案的评审和修改,最终肯定最终技术方案。
个人建议是,团队最好输出一份技术方案的规范,能够供每一个成员参考,从设计阶段,就统一团队成员的认识。
最后再总结一下,关于复杂业务系统开发的一些体会:
熟悉业务,抽象产品需求,分析相关测试用例,了解各类用户角色和其使用的场景
自顶向下进行方案设计,对于比较复杂的业务系统,比较好的方式是先关注顶层模型,避免在一开始就陷入技术和业务细节中去
从总体设计,到模块局部规划,设计好部署架构、分层和分模块、API设计、数据库设计等
能够参考成熟的解决方案,好比将开源软件,改造,变成适合本身业务需求的架构
验证和优化架构设计方案,完整的架构设计方案,须要有屡次的评审,充分收集各方面的反馈,反复修改后肯定
合理进行扩展,考虑架构预期能知足多长时间的业务增加,好比将来一年的业务变化