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


一文教会你如何写复杂业务代码文章插图
作者 | 张建飞 阿里巴巴高级技术专家
了解我的人都知道 , 我一直在致力于应用架构和代码复杂度的治理 。
【一文教会你如何写复杂业务代码】这两天在看零售通商品域的代码 。 面对零售通如此复杂的业务场景 , 如何在架构和代码层面进行应对 , 是一个新课题 。 针对该命题 , 我进行了比较细致的思考和研究 。 结合实际的业务场景 , 我沉淀了一套“如何写复杂业务代码”的方法论 , 在此分享给大家 。
我相信 , 同样的方法论可以复制到大部分复杂业务场景 。
一个复杂业务的处理过程业务背景简单的介绍下业务背景 , 零售通是给线下小店供货的 B2B 模式 , 我们希望通过数字化重构传统供应链渠道 , 提升供应链效率 , 为新零售助力 。 阿里在中间是一个平台角色 , 提供的是 Bsbc 中的 service 的功能 。
一文教会你如何写复杂业务代码文章插图
商品力是零售通的核心所在 , 一个商品在零售通的生命周期如下图所示:
一文教会你如何写复杂业务代码文章插图
在上图中红框标识的是一个运营操作的“上架”动作 , 这是非常关键的业务操作 。 上架之后 , 商品就能在零售通上面对小店进行销售了 。 因为上架操作非常关键 , 所以也是商品域中最复杂的业务之一 , 涉及很多的数据校验和关联操作 。
针对上架 , 一个简化的业务流程如下所示:
一文教会你如何写复杂业务代码文章插图
过程分解像这么复杂的业务 , 我想应该没有人会写在一个 service 方法中吧 。 一个类解决不了 , 那就分治吧 。
说实话 , 能想到分而治之的工程师 , 已经做的不错了 , 至少比没有分治思维要好很多 。 我也见过复杂程度相当的业务 , 连分解都没有 , 就是一堆方法和类的堆砌 。
不过 , 这里存在一个问题:即很多同学过度的依赖工具或是辅助手段来实现分解 。 比如在我们的商品域中 , 类似的分解手段至少有 3 套以上 , 有自制的流程引擎 , 有依赖于数据库配置的流程处理:
一文教会你如何写复杂业务代码文章插图
本质上来讲 , 这些辅助手段做的都是一个 pipeline 的处理流程 , 没有其它 。 因此 , 我建议此处最好保持 KISS(Keep It Simple and Stupid) , 即最好是什么工具都不要用 , 次之是用一个极简的 Pipeline 模式 , 最差是使用像流程引擎这样的重方法 。
除非你的应用有极强的流程可视化和编排的诉求 , 否则我非常不推荐使用流程引擎等工具 。 第一 , 它会引入额外的复杂度 , 特别是那些需要持久化状态的流程引擎;第二 , 它会割裂代码 , 导致阅读代码的不顺畅 。 大胆断言一下 , 全天下估计 80% 对流程引擎的使用都是得不偿失的 。
回到商品上架的问题 , 这里问题核心是工具吗?是设计模式带来的代码灵活性吗?显然不是 , 问题的核心应该是如何分解问题和抽象问题 , 知道金字塔原理的应该知道 , 此处 , 我们可以使用结构化分解将问题解构成一个有层级的金字塔结构:
一文教会你如何写复杂业务代码文章插图
按照这种分解写的代码 , 就像一本书 , 目录和内容清晰明了 。
以商品上架为例 , 程序的入口是一个上架命令(OnSaleCommand), 它由三个阶段(Phase)组成 。
@Commandpublic class OnSaleNormalItemCmdExe {@Resourceprivate OnSaleContextInitPhase onSaleContextInitPhase;@Resourceprivate OnSaleDataCheckPhase onSaleDataCheckPhase;@Resourceprivate OnSaleProcessPhase onSaleProcessPhase;@Overridepublic 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 {@Resourceprivate PublishOfferStep publishOfferStep;@Resourceprivate BackOfferBindStep backOfferBindStep;//省略其它steppublic void process(OnSaleContext onSaleContext){SupplierItem supplierItem = onSaleContext.getSupplierItem();// 生成OfferGroupNogenerateOfferGroupNo(supplierItem);// 发布商品publishOffer(supplierItem);// 前后端库存绑定 backoffer域bindBackOfferStock(supplierItem);// 同步库存路由 backoffer域syncStockRoute(supplierItem);// 设置虚拟商品拓展字段setVirtualProductExtension(supplierItem);// 发货保障打标 offer域markSendProtection(supplierItem);// 记录变更内容ChangeDetailrecordChangeDetail(supplierItem);// 同步供货价到BackOffersyncSupplyPriceToBackOffer(supplierItem);// 如果是组合商品打标 , 写扩展信息setCombineProductExtension(supplierItem);// 去售罄标removeSellOutTag(offerId);// 发送领域事件fireDomainEvent(supplierItem);// 关闭关联的待办事项closeIssues(supplierItem);}}