专栏名称: 互联网后端架构
主要介绍Java后端架构。其中也会掺杂一些前端、GO、Python、Linux,目标:全栈工程师!---好像很牛叉的样子 ^-^
目录
相关文章推荐
51好读  ›  专栏  ›  互联网后端架构

一文教会你如何写复杂业务代码

互联网后端架构  · 公众号  · 架构  · 2019-08-11 20:14

正文

一个复杂业务的处理过程

业务背景

简单的介绍下业务背景,零售通是给线下小店供货的B2B模式,我们希望通过数字化重构传统供应链渠道,提升供应链效率,为新零售助力。阿里在中间是一个平台角色,提供的是Bsbc中的service的功能。

在商品域,运营会操作一个“上架”动作,上架之后,商品就能在零售通上面对小店进行销售了。 是零售通业务非常关键的业务操作之一,因此涉及很多的数据校验和关联操作

针对上架,一个简化的业务流程如下所示:

过程分解

像这么复杂的业务,我想应该没有人会写在一个service方法中吧。一个类解决不了,那就分治吧。

说实话,能想到分而治之的工程师,已经做的不错了,至少比没有分治思维要好很多。我也见过复杂程度相当的业务,连分解都没有,就是一堆方法和类的堆砌。

不过,这里存在一个问题:即很多同学过度的依赖工具或是辅助手段来实现分解。比如在我们的商品域中,类似的分解手段至少有3套以上,有自制的流程引擎,有依赖于数据库配置的流程处理:

本质上来讲,这些辅助手段做的都是一个pipeline的处理流程,没有其它。因此,我建议此处最好保持KISS(Keep It Simple and Stupid),即 最好是什么工具都不要用,次之是用一个极简的Pipeline模式,最差是使用像流程引擎这样的重方法

除非你的应用有极强的流程可视化和编排的诉求,否则我非常不推荐使用流程引擎等工具。第一,它会引入额外的复杂度,特别是那些需要持久化状态的流程引擎;第二,它会割裂代码,导致阅读代码的不顺畅。 大胆断言一下,全天下估计80%对流程引擎的使用都是得不偿失的

回到商品上架的问题,这里问题核心是工具吗?是设计模式带来的代码灵活性吗?显然不是, 问题的核心应该是如何分解问题和抽象问题 ,知道金字塔原理的应该知道,此处,我们可以使用结构化分解将问题解构成一个有层级的金字塔结构:

按照这种分解写的代码,就像一本书,目录和内容清晰明了。

以商品上架为例,程序的入口是一个上架命令(OnSaleCommand), 它由三个阶段(Phase)组成。

@Commandpublic class OnSaleNormalItemCmdExe {
@Resource private OnSaleContextInitPhase onSaleContextInitPhase; @Resource private OnSaleDataCheckPhase onSaleDataCheckPhase; @Resource private OnSaleProcessPhase onSaleProcessPhase;
@Override public Response execute(OnSaleNormalItemCmd cmd) { OnSaleContext onSaleContext = init(cmd); checkData(onSaleContext);
process(onSaleContext);
return Response.buildSuccess(); }
private OnSaleContext init(OnSaleNormalItemCmd cmd) { return onSaleContextInitPhase.init(cmd); }
private void checkData(OnSaleContext onSaleContext) { onSaleDataCheckPhase.check(onSaleContext); }
private void process(OnSaleContext onSaleContext) { onSaleProcessPhase.process(onSaleContext); }}

每个Phase又可以拆解成多个步骤(Step),以 OnSaleProcessPhase 为例,它是由一系列Step组成的:

@Phasepublic class OnSaleProcessPhase {
@Resource private PublishOfferStep publishOfferStep; @Resource private BackOfferBindStep backOfferBindStep; //省略其它step
public void process(OnSaleContext onSaleContext){ SupplierItem supplierItem = onSaleContext.getSupplierItem();
// 生成OfferGroupNo generateOfferGroupNo(supplierItem); // 发布商品 publishOffer(supplierItem);
// 前后端库存绑定 backoffer域 bindBackOfferStock(supplierItem);
// 同步库存路由 backoffer域 syncStockRoute(supplierItem);
// 设置虚拟商品拓展字段 setVirtualProductExtension(supplierItem);
// 发货保障打标 offer域 markSendProtection(supplierItem);
// 记录变更内容ChangeDetail recordChangeDetail(supplierItem);
// 同步供货价到BackOffer syncSupplyPriceToBackOffer(supplierItem);
// 如果是组合商品打标,写扩展信息 setCombineProductExtension(supplierItem);
// 去售罄标 removeSellOutTag(offerId);
// 发送领域事件 fireDomainEvent(supplierItem); // 关闭关联的待办事项 closeIssues(supplierItem); }}

看到了吗,这就是商品上架这个复杂业务的业务流程。需要流程引擎吗?不需要,需要设计模式支撑吗?也不需要。对于这种业务流程的表达,简单朴素的组合方法模式(Composed Method)是再合适不过的了。

因此,在做过程分解的时候,我建议工程师不要把太多精力放在工具上,放在设计模式带来的灵活性上。而是应该多花时间在对问题分析,结构化分解,最后通过合理的抽象,形成合适的阶段(Phase)和步骤(Step)上。

过程分解后的两个问题

的确,使用过程分解之后的代码,已经比以前的代码更清晰、更容易维护了。不过,还有两个问题值得我们去关注一下:

1、领域知识被割裂肢解

什么叫被肢解?因为我们到目前为止做的都是过程化拆解,导致没有一个聚合领域知识的地方。每个Use Case的代码只关心自己的处理流程,知识没有沉淀。

相同的业务逻辑会在多个Use Case中被重复实现,导致代码重复度高,即使有复用,最多也就是抽取一个util,代码对业务语义的表达能力很弱,从而影响代码的可读性和可理解性。

2、代码的业务表达能力缺失

试想下,在过程式的代码中,所做的事情无外乎就是取数据--做计算--存数据,在这种情况下,要如何通过代码显性化的表达我们的业务呢?说实话,很难做到,因为我们缺失了模型,以及模型之间的关系。脱离模型的业务表达,是缺少韵律和灵魂的。

举个例子,在上架过程中,有一个校验是检查库存的,其中对于组合品(CombineBackOffer)其库存的处理会和普通品不一样。原来的代码是这么写的:

boolean isCombineProduct = supplierItem.getSign().isCombProductQuote();
// supplier.usc warehouse needn't checkif (WarehouseTypeEnum.isAliWarehouse(supplierItem.getWarehouseType())) {// quote warehosue checkif (CollectionUtil.isEmpty(supplierItem.getWarehouseIdList()) && !isCombineProduct) { throw ExceptionFactory.makeFault(ServiceExceptionCode.SYSTEM_ERROR, "亲,不能发布Offer,请联系仓配运营人员,建立品仓关系!");}// inventory amount checkLong sellableAmount = 0L;if (!isCombineProduct) {






请到「今天看啥」查看全文


推荐文章
小学生作文  ·  加盟思维导图作文,写作轻松有趣
7 年前
一起神回复  ·  大学生活好吗?
7 年前
程序员之家  ·  Hello World 程序的起源与历史
7 年前