Layered Web Application In Action——Web应用分层架构实施总结

问题/现象

时间来到2018年,分层架构似乎已不是一个需要讨论的问题,因为大概已经不存在不是分层架构的项目了,

甚至在SpringFramework成为标配的影响下,很多项目的架构都是雷同和千篇一律的,只是在各个层的选型上会有差异。

 

虽然这样的局面相对于之前的年代是巨大进步,但是事情并没有这么简单,新的问题和挑战出现了,其中最突出的就是Service层开始失控

以我们的互金和小贷系统为例,典型症状是:3000多行代码的service类,100多个import,两位数的依赖注入,几乎每一个组件都在依赖其它所有组件。

Service层变得臃肿 API泛滥 组件互相依赖纠缠严重,成了整个项目开发和维护的瓶颈!

 

 

反思/总结

Service层失控是必然的,毕竟它是系统最核心最复杂的业务逻辑所在,

在项目的规模和业务复杂度不高的时候,MVC和三层分层架构是有效的,

对于长期迭代开发类项目,其规模和复杂度必然与日俱增,到了一定阶段就会发现某些层变得难以维护了——通常是Service层(或者叫中间层、业务逻辑层),

并不是说分层不再有效了,分层仍然是必要的,但是简单的分层不能解决问题,因为层内部也会变得复杂和难以维护。

简单地分层 就相当于把一个难题(例如把大象装进冰箱)分成了3个步骤并没有让事情简单,因为其中的第2步本身仍然是难题。

 

于是问题变成了——Servie层该如何设计和组织 才能避免其变得复杂和难以维护。

 

解决方案1——水平拆分保持合适的组件粒度

所谓合适的组件粒度就是既不要过大(比如1个比较复杂的模块只有一个service)也不要过小(比如极端情况一个功能一个service),

粒度过小的情况不太会出现也容易修正,粒度过大是最容易犯的错误,很多时候不知不觉就把一个service搞得很大了,

尤其是多人开发大家都不加节制的在service里面增加功能点 彼此间又缺少复用的话,情况很快就变糟了,于是得到一个几千行代码几十个API的难以维护的Service类。

以小贷系统的风控模块为例,最开始创建了一个CreditService主要功能是交单审批的业务,后来做到了押品管理和图表功能时 没有意识到需要拆分,于是都塞进去了,虽然业务上都是风控模块的业务,但是其实从技术实现上显然应该分成三个组件——CreditApproveService、CreditGuaService、CreditChartService。

 

解决方案2——垂直拆分Service层增加一个Business层

在方案一的基础上,对于系统中的一些业务逻辑比较复杂的功能可能会发现不适合放在任何一个Service中,

比如小贷系统的“合同展期”、“合同垫付”、“合同提前结清”等 明显与ContractService中的其它功能(如合同查询、合同生效等)不在一个级别,

比如互金系统的各种存管交易功能 明显与FundInvestService FundProductService中的CRUD功能不在一个级别,

在这两个例子中 前者要比后者复杂得多(需要串联本地DB事务操作和外部系统接口的调用和响应处理),并且前者要依赖后者;

 

这正是需要分层的典型特征和信号,虽然都是业务逻辑,但两者复杂性的明显差异、修改和维护频率的明显差异、可复用性的明显差异,决定了它们不应该被强行塞进同一个Service层来实现,进一步分层才是合理的。

为了方便表述和理解,我们将上面提到的两种业务逻辑分别称为“交易流程型业务逻辑”和“原子服务型业务逻辑”,分别对应了Business层和Service层,

这里将原来的Service层做了垂直拆分 出现了一个新的层——Business层,

 

由于这一层的业务逻辑较复杂、与前端业务场景高度相关,因此Business层不追求可复用性,同时非常的看重可维护性、易修改性

因此Business层应该是由多个细粒度组件构成——每个组件只封装一个交易流程,例如小贷系统的ContractExtendBusiness、ContractInstPayBusiness、ContractShiftClearBusiness等等,不难看出 这个风格完全不同于Service层。

 

这样做的好处很明显:

  • 过去没分Business层时,多个交易流程的实现方法(往往是1个public外加三四个private)都被塞进了一个Service,彼此的private方法都是可见的,很快的,Service中的API就变得数量众多、职责边界不清、纠缠在一起了,每次阅读和修改这些代码都成本高昂而且效率低下容易出错。
  • 有了细粒度风格的Business层以后,对任何一个交易流程的修改调整 只需找到对应的那个Business组件去修改即可。

 

基于互金和小贷系统Business层改造的经验,除了上述的细粒度组件风格,还发现Business层组件往往具有流程的共性部分,这种情况恰好是“Template Method”设计模式适合的场景——将共性的业务流程封装在一个抽象父类中并且为流程可变的部分留出扩展点,那么各个Business组件就可以省去很多重复代码 只需继承抽象父类 并为扩展点(abstract protected方法)提供实现,

这样Business层变得更加简单清晰和容易维护——具体流程逻辑变化时找到对应的Business子类修改、共性逻辑变化时找到Business父类修改即可。

 

结论

归纳起来就是:

  • 分层以后,Service层容易成为瓶颈;
  • 首先可以对Service层做水平拆分,避免单个Service组件过大难维护;
  • 对于复杂系统或模块,还需要在Service之上增加Business层 用于封装“交易流程型业务逻辑”,Service里面放“原子服务型业务逻辑”;
  • Business层(如果存在)组件粒度要小、功能要单一,避免重蹈Service层的覆辙,也避免成为Service层的影子和传声筒;
  • Business组件的共性逻辑和个性化逻辑需要进一步拆分,应用Template Method设计模式可以很好的完成这个工作——抽象父类封装共性逻辑,个性化逻辑作为扩展点在子类中实现;参考Wealth2.0的AbstractDmsTx设计。

 

存在争议的结论:

  • 如何区分Business和Service:
    • Service是简单的CRUD,通常只对一个实体对象做增删改查,
    • Business串联多个Service以及外部服务,实现一个多步骤的业务流程,通常涉及到多个实体的增删改查;
  • Service不应该依赖注入其它Service(基础功能Service如WebUserService、UaasService除外)
  • Business中如果存在调用外部服务的步骤,通常无法纳入本地数据库事务,因此需要特殊的设计来保证一致性,例如Wealth2.0的TxFlow框架;
  • Business中如果不存在调用外部服务的步骤,只是调用多个Service完成一个复杂的本地数据库事务,那么只需在Business层配置SpringAOP事务拦截即可;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值