代码编排与领域设计

讲代码重构前,必须讲到软件架构设计,初期没有好的设计,再厉害的程序员,在堆积如山的屎山代码面前,都没有办法把原本一团乱麻的逻辑,层层剖析开,建立起新的代码金字塔

代码重构设计

代码编排

1、分解阶段和步骤: 垂直切分+水平扩展

想象一下,一个复杂的、具有类似功能的、具有个性功能的多表业务,如何去编排他的代码结构?

或者一个下单业务场景,要先检查商品数量、检查收货范围、检查用户状态,然后开始组装下单数据、订单数据、物流数据等,最后要进行调用支付渠道,这样一个复杂的流程,如何去编排他的业务代码?是全部写在一个service中吗?显然不可能!

1、把业务场景分割,按照操作性质,分成多个阶段

1、把阶段里的实现按照简单的操作步骤,抽象出多个表之间共性的操作

2、将这些共性操作划分成具体的步骤,封装成一个抽象类,在业务层面进行简单的业务步骤垂直切分

public abstract class AbstractBaseHandler extends AbstractHandler<ExcelModelContext, Void> {

    private Logger log = LoggerFactory.getLogger(AbstractBaseHandler.class);



    @Override
    public Void execute(ModelContext context) {
        commonStep(context);//共有步骤
        step1(context);//第1步
        step2(context);//第2步

    }

    public void commonStep(ModelContext context){}{

    }

    //抽象方法
    public abstract step1(ModelContext context);

    //抽象方法
    public abstract step2(ModelContext context);
}

3、基于这个抽象类,将多表的业务操作继承该类,形成软件结构上的独立业务处理模块,可以参考DDD领域驱动设计思想

4、但是一个业务处理模块里,写上全部的代码,仍然会非常庞大,往往业务处理模块需要再细分模块,使用设计模式的聚合等模式

上面这种简单形式,基本可以适用所有的应用场景,是一个通用的方法,但代码最多只能做到不难看,绝对算不上优美,只是把原先大坨大坨的、甚至上千行的业务代码,变成一块块的独立的业务代码,还是堆叠在一起

此种方法就是基于对过程的分解,将一个业务变为多个阶段,再在阶段中细化操作步骤,实际上还是过程化的编排方式,只是提高了代码可读性,但是没有提高抽象性和复用性

2、过程分解 + 对象模型

上述的过程分解的表达方式,还是一种简单朴素的模式,但这种代码编排,虽然可以实现快速的业务分析与代码编排,能比较好的进行代码维护,但缺少了从整个软件系统结构视角上的立体化设计,业务的可复用程度不高,只能针对某个场景进行单一的快速代码编排

这种过程化拆解,没有聚合领域知识,逻辑没有沉淀,只有单一的使用场景,我们缺失了模型和模型之间的关系,脱离模型的业务表达,是无法显性的表达我们的业务的

上述文中举例的下单业务,校验阶段中需要校验普通商品库存大于1,组合商品库存量大于2,按照只有过程化代码编排,需要先判断该商品属于单品还是组合商品,然后check不同类型的商品数量,这种具有很多if/else的流程化代码,显然不能完成商品领域内的业务沉淀,下一次碰到其他业务场景,比如秒杀等同样判定商品库存量的,则还要写一遍

商品的继承关系如下图

领域化改造的分析过程

写复杂业务的方法论

自上而下的过程分解 + 自下而上的对象建模

过程分析可以理清模型之间的关系,对象模型可以提升代码的复用度和业务语义表达能力

面向对象的DDD领域建模,就是将业务能力下沉到底层

但是并不是所有的业务都要下沉到domian层,这将会并不需要、复用度不高的业务场景把domain搞得很膨胀,并不利于domain的维护,那些不需要复用的,暂时放在Use Case中即可(Use Case即一个request的处理过程,在代码整洁之道中,指的就是独立的、单一的单个业务)

循序渐进的能力下沉策略,应该是更符合实际、更敏捷的方法,因为模型不是一次设计出来,是迭代演化而来的

 多个App层的能力下沉

参考分解过程

DDD业务结构设计

模型建立过程

STEP1 业务流程分析

首先需要对模型进行抽取,需要分析业务场景下的主过程式流程, 即需求分析

STEP2 用例设计,模拟现实世界

找出了业务流程中操作的对象实体,如上述业务流程,分为美团、厂家、商家等

STEP3 领域分裂,拆解出核心领域

1、分析出每个流程域涉及到的表单,如采购流程涉及到采购单,领用设备流程涉及到出库入库单

2、针对上述各流程域进行分解,根据业务复杂度,按照自底向上/自顶向下的方式进行拆解/合并,多个子域拆解进大的领域,一个复杂领域拆解成多个简单子领域

3、最终根据分析出的业务流程,回归领域设计,是否可以满足该业务流的发展,完善领域设计

领域建模是将一个系统划分成了多个子域,每个子域都是一个独立的业务场景,每个子域的实现就是“限界上下文",他们之间的关系是“ 上下文地图"

STEP3 数据模型分析与建立 

按照领域模型,设计各个微服务的数据库

STEP1建立流程与模型初始概念,STEP2将过程化业务流程变为面向业务领域的业务建模,

那么到了STEP3,则需要将模型真正放到业务里来,进行模型化的业务流转

DDD的设计与开发流程

需求分析 

使用统一语言建模进行业务沟通 : ddd是基于现实业务的真实建模思想,要参与真实业务的了解与分析,使用专业领域的语言与客户进行沟通,让开发人员与客户的业务专家进行头脑风暴

1、针对每一个领域进行业务分析,增加各种事件

2、识别模型中可能涉及的聚合以及聚合根

    

微服务拆分

拆分原则: 单一原则,小而专,微服务内高内聚,微服务间低耦合 

通过DDD进行业务建模,在基于领域模型进行限界上下文划分,能保证系统的设计,在限界上下文内高内聚,在限界上下文之间低耦合

总结

DDD最佳实践方式: 具体

聚合

整体与部分的关系, 整体封装部分的操作,要识别聚合关系

仓库&工厂

发生聚合后,订单与订单明细的增删改,通常会用一个仓库去实现,封装到一个仓库层中实现

增删改 : 通过订单仓库的封装,只需要在领域对象建模的时候,设定对象间的关系,即将其设定为“聚合”

查询 : 订单仓库查询到该订单后,将其封装在订单对象中,通过查询填补用户对象和订单明细对象,需要用工厂进行数据装配       

DDD里的工厂与设计模式的工厂是两回事 

带缓存层的仓库 :  即仓库层将dao、nosql等屏蔽起来,数据访问与业务层完全割裂

通过聚合实现了整体与部分的关系

对缓存、对数据库的操作都封装在了仓库与工厂中

数据的查询——通过工厂进行填补与装配

限界上下文

限界上下文: 通过上下文地图相互调用时,通过接口进行调用

比如电商常见的下单功能,可以划分为几个领域: 购物 - 下单 - 支付 - 物流 , 

下单模块A,依赖支付模块B,如果直接依赖,则其他使用A模块的领域,则也要使用BCD,成本太高

但若模块A依赖B‘接口,所有调用模块A的地方,不一定就需要B,B’接口有B和F两种实现,即支付功能可以增加微信支付、支付宝支付、银行卡支付等多种方式,形成低耦合,高内聚

可以通过微服务来实现"限界上下文"之间的低耦合

DDD往往应用于系统增删改的业务场景中,查询场景不用DDD

领域事件通知

如订单系统,将系统划分成用户下单、饭店接单、骑士接单等领域,当用户在下单domain中进行下单,形成一个订单,产生一个事件,此时饭店需要接单,另外一个微服务需要及时获得通知

——使用消息队列,解耦

使用消息队列实现领域事件在微服务间的通知

综合应用

下图是下单、接单、派送3个领域建模的增删改、查询和消息队列的场景应用

DDD代码分层架构

四层模型

五层模型

六边形模型

DDD分层架构的三种模式 - xixi_pan - 博客园

体系结构设计

架构师能力标准

1、将业务转换为技术,业务需求可以落地到技术方案

2、合理利用技术支撑业务

 系统技术架构图

系统业务架构图

微服务开发过程问题

当发生需求变更时,是局部服务进行修改,也会涉及到微服务之间的服务调用,当一个微服务团队向另一个微服务团队提出接口调用需求时,另一个微服务团队该如何设计呢?

当多个团队都在向你提出API接口时,如何提供接口?

对这些接口进行规划,通过复用,用尽可能少的接口满足他们的要求

当调用方需要接口变更时怎么办?

变更现有接口应当尽可能向前兼容,即接口的名称与参数都不变,只在内部增加新的功能

调用方如何调用接口?

异步调用/同步调用

防腐层 

比如用户下单领域中包含用户注册,当系统不断完善设计和扩展,用户注册需要单独拎出来到用户领域内,则原先依赖用户下单、取消订单等依赖注册的逻辑都会报错,维护成本太高

可以在下单领域内,放置一个feign,其他的service都不需要修改了,维护成本变低

防腐层 : 接口变更时,降低维护成本

数据管理

仓库层屏蔽了数据操作,在仓库层以下,要根据业务的数据特性,选用不同的数据库

1、对于数据量稳定但需要频繁查询的用户表、饭店表,使用redis作为缓存层

2、3、对于有并发场景数据量巨大的订单下单,使用关系型数据库

对于报表分析、订单查询,从生产上同步数据到es等平台

由于数据库的拆分,不能随意join了,要使用工厂进行数据填充装配,干掉join操作,使用分页后的数据填充,为了提升海量数据的查询性能,可以适当增加冗余字段

当系统要在某些查询模块进行订单查询时,可能对各个字段都需要进行过滤查询,利用NoSQL的特性,采用“宽表”的设计

整洁架构设计

DDD下沉 : 技术中台

传统的领域驱动设计,是每个模块驱动和设计各自的仓库与工厂,这样大大增加开发与工作量,但其实大致都是相同的,会产生大量的重复代码,

是否可以通过抽象,提取出共性,形成通用的仓库与工厂,下沉到底层技术中台中?

从而降低领域驱动的开发成本与技术门槛 —— 实现DDD领域驱动设计,还需要相应的平台架构支持

每个DDD模块都需要微服务间通过feign接口相互调用,数据要通过补填关联查询,还有聚合的实现、仓库和工厂的设计,如果每个模块都要反复的实现这些功能,DDD的设计将异常繁琐

——需要一个既支持DDD、又支持微服务的技术中台

技术中台应该能够封装那些繁琐的聚合操作、仓库与工厂的设计,以及相关的各种技术,将精力放在业务开发上面,降本增效,技术门槛降低,技术更迭方便,上手容易,不再受限于技术细节

底层技术的更迭

过去: 架构是软件系统中最稳定不变的部分

现在: 好的架构源于不停的演变

设计思考: 如何让底层的架构更易于技术更迭,易于架构调整, 来应对不断演变的新技术、新框架

老系统技术架构升级成本极高的原因,是业务代码与底层技术框架太过于耦合,解决思路就是对他们进行接耦 ——在上层业务代码与底层技术框架之间建立接口层

如上图,中间接口层编写实现类去调用底层框架,实现真正的持久化

整洁框架核心设计思路

如何轻松实现架构更迭演化,又能保证开发团队的快速交付?

业务代码与技术框架解耦,可以实现架构快速更迭&快速交付

整洁框架核心设计思路 : 

业务领域层设计的实质,就是将领域模型,通过贫血模型或者充血模型的设计,最终落实到对代码的设计,在此基础上,将业务领域层与其他各个层次进行解耦

各中台对比

系统分为前台与后台,提取出的公用组件,属于前台还是后台呢?

其实,公用的组件既包含前台的界面,也包含后台的逻辑,所以被称为中台

中台: 将以往业务系统中可以复用的前台与后台代码剥离个性、提取共性,形成的公用组件

因此,阿里提出小前台,大中台的战略

业务中台: 将抽象的业务组件,做成微服务,各个业务系统都可以使用,如会员管理、权限管理、仓储管理等

技术中台: 封装各个业务系统所要采用的技术框架,设计出统一的API,使上层的开发门槛降低,提升交付速度

数据中台: 整理各个业务系统的数据,建立数据存储与运算的平台

DDD模式下的快速交付团队

如果业务变得越来越复杂、参与的人越来越多,交付速度就会越来越慢,团队不能适应快速变化, 

软件规模化发展是软件的必然趋势,要解决规模化团队与软件快速交付的矛盾

大前端+技术中台,可以提高规模化团队的交付速度,但是组建这样一个团队的成本非常高,既要懂技术,也要懂业务,既要懂UI,也要懂前端,每个人必须是全栈工程师

这时就需要架构团队,这里的架构就不是一个人了,而是一个团队,

架构团队通过技术选型,构建技术中台,将软件开发中诸如UI、应用、数据库甚至大数据等诸多技术进行了封装,然后以API接口的形式开放给上层技术

架构团队从业务开发的角度进行提炼,提炼共性、保留个性,将这些共性沉淀到技术中台中,通过将DDD与微服务涉及的各个技术组件封装到技术中台中,封装各个技术细节

箴言

宁愿花更多的时间去分析设计,让软件设计精简到极致,从而花更少的时间去编码

技术中台实战

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值