写点什么

架构进阶之路:复杂业务开发与领域驱动设计

发布于: 2021 年 02 月 08 日
架构进阶之路:复杂业务开发与领域驱动设计

以下是在现公司,给成员做分享的资料。业务案例来自:一文教会你如何写复杂业务代码。作者:张建飞,进行了重新整理。

一 业务开发职责

1.1 开发=CVS?

1.2 为什么会这样?

1、业务所限。非供应链、交易、金融等复杂业务领域,简单业务需求偏多

2、个人原因。接受需求时,只考虑局部实现,不考虑整体业务。

设计时还没有达到要求,只局限于当前需求实现这一最低要求;无法从更高一层的角度看待问题。如果当前级别的需求都存在很大的疏漏,那么到了复杂业务场景,真的有能力能够让人放心交给你来做吗?


1.3 业务真的简单吗?

上图是当前业务系统中的信息流投放与 CRM 交互过程。这只是 CRM 中的很小一部分,目前做了多家供应商支持,不同的回调码、消息结构;而且目前支持了组合,各种分支逻辑会更复杂。加上各种风控限制,供应商的稳定性问题,整体看起来,我们的业务也没有那么简单。

1.4 复杂业务场景下的设计开发

如何定义“复杂”?

1.5 基于经验的一些解决方法


二 业务案例与分析实战

2.1 背景

这里选择阿里零售通业务作为示例。领域:典型的供应链场景,涉及品牌商入驻、库存/仓储、配送、销售多个系统和环节

2.2 商品的生命周期

2.3 关键业务节点——上架


提问:从这张图,能看到哪些信息?


上图中红框标识的是一个运营操作的“上架”动作,这是非常关键的业务操作。上架之后,商品就能在零售通上面对小店进行销售了。上架操作非常关键,所以也是商品域中最复杂的业务之一,涉及很多的数据校验和关联操作

带来的问题:分支逻辑多;且结构复杂。 商品(单品),商品组/组合商品;状态、库存、佣金、运费、规格、限购、供货价、….

2.4 实现思路?

拆分:一个大 service => check1(), check2(),…,createNo(), publish(),……

问题:流程如何管理?

是否需要引入流程引擎(工作流引擎):已有 or 自研的流程引擎,or 依赖于数据库配置的流程处理。 但过早考虑这些是否合理?

2.5 回归核心问题

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

2.6 总结

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

当问题被清楚地结构化分解之后,我们就可以很明确地通过简单的组合等手段实现业务流程。在保证实现的基础之上,再考虑工具或设计模式来提升灵活性和可扩展性。

2.7 过程分解后的两个问题

1、领域知识被割裂肢解

到目前为止做的都是过程化拆解,导致没有一个聚合领域知识(校验规则…)的地方。每个用例的代码只关心自己的处理流程,知识没有沉淀。

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


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

试想下,在过程式的代码中,所做的事情无外乎就是取数据 -- 做计算 -- 存数据,在这种情况下,要如何通过代码显性化的表达业务呢? 说实话,很难做到,因为缺失了模型,以及模型之间的关系。脱离模型的业务表达,是缺少韵律和灵魂的。【人话:赋予代码业务更强的表达能力,提升可读性、可维护性】

2.8 怎么做?

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


引入领域模型:


使用模型的表达要清晰易懂很多,而且也不需要做关于组合商品的判断了,因为我们在系统中引入了更加贴近现实的对象模型(CombineBackOffer 继承 BackOffer),通过对象的多态可以消除我们代码中的大部分的 if-else。

三 复杂业务开发方法总结

3.1 总结:过程分解+对象模型 上下结合

所谓上下结合,是指我们要结合自上而下的过程分解和自下而上的对象建模,螺旋式的构建我们的应用系统。这是一个动态的过程,两个步骤可以交替进行、也可以同时进行。

这两个步骤是相辅相成的,上面的分析可以帮助我们更好的理清模型之间的关系,而使用模型表达可以提升我们代码的复用度和业务语义表达能力



四 领域驱动设计初探

4.1 领域驱动设计的一些基础概念

领域:本质上是一种边界划分,领域词语中的“域”也是边界的意思。

领域设计= 边界 + 设计。要求:具备领域知识、总结和概括能力、达成共同认知。


电商系统,有商品模型,但是一个商品模型只有商品的基本信息,数据。如果需要获取一个商品的总价,那么我们需要调用 model 里面的一个方法来计算。这就是典型的贫血模型。

如果我们在商品模型里面提供一个计算总价的方法。把数据和业务逻辑放在一起,这就是充血模型。


领域模型的基础是对象模型,对象是由属性和方法组成的。识别贫血模型有一个重要的指标是:模型自身需要处理的业务逻辑是否外漏

如果模型本身没有需要处理的业务逻辑,只包含属性那么他不应该称为贫血模型。

如果模型自身需要处理业务逻辑但是没有处理,而是由调用者来处理本属于模型本身的业务逻辑时就应该称之为贫血模型。

在处理表单数据校验时,通常使用的是外部校验方式


领域设计不是一件简单事情,首先你得有这个领域的知识,然后还有一套你对这个领域知识总结和概括,甚至摸清了领域中的逻辑关系,一般人不一定掌握得好,大概只有多年业务专家才会有这个本事,或者多年来一直在开发某个业务系统,直至公司人员组织结构都按照业务领域划分成不同部门了,比如电商公司有订单组、仓库组、支付组和货运组,这种带有业务性质的组织部门其实已经代表了大家的共同领域认识。

4.2 领域驱动实践历程

1. 套概念阶段

了解了一些 概念,然后在设计中“使用”聚合根,有界上下文等概念。更进一步,也会使用一定的分层策略。然而这种做法一般对复杂度的治理并没有多大作用。


2. 融会贯通阶段

DDD 的本质是统一语言、边界划分和面向对象分析的方法。


哪些能力应该放在 Domain 层,是不是按照 DDD 传统的做法,将所有的业务都收拢到 Domain 上,这样做合理吗?这并不是一个可以很容易说清楚的问题。因为在现实业务中,很多的功能都是用例特有的,如果盲目的使用 Domain 收拢业务并不见得能带来多大的益处。相反,这种收拢会导致 Domain 层的膨胀过厚,反而会影响复用性和表达能力。


所以,我们考虑逐步地进行能力下沉的方式,即不强求一次就能设计出 Domain 的能力,也不强制要求把所有的业务功能都放到 Domain 层,而是采用实用主义的态度,即只对那些需要在多个场景中需要被复用的能力进行抽象下沉,而不需要复用的,就暂时放在 App 层的 Use Case 里就好了。


通过实践,发现这种循序渐进的能力下沉策略,应该是一种更符合实际、更敏捷的方法。因为我们承认模型不是一次性设计出来的,而是迭代演化出来的。

4.3 能力下沉过程

五 开发的选择:技术 or 业务

很多业务技术同学的困惑,也是我之前的困惑:即业务技术到底是在做业务,还是做技术?业务技术的技术性体现在哪里?

通过上面的案例,我们可以看到业务所面临的复杂性并不亚于底层技术,要想写好业务代码也不是一件容易的事情。业务技术和底层技术人员唯一的区别是他们所面临的问题域不一样。

业务技术面对的问题域变化更多、面对的人更加庞杂。而底层技术面对的问题域更加稳定、但对技术的要求更加深。比如,如果你需要去开发 Pandora,你就要对 Class Loader 有更加深入的了解才行。

但是不管是业务技术还是底层技术人员,有一些思维和能力都是共通的。比如,分解问题的能力,抽象思维,结构化思维等等。这些,都需要我们在日常的工作生活中不断的加深思考,沉淀能力。


发布于: 2021 年 02 月 08 日阅读数: 137
用户头像

磨炼中成长,痛苦中前行 2017.10.22 加入

微信公众号【程序员架构进阶】。多年项目实践,架构设计经验。曲折中向前,分享经验和教训

评论 (2 条评论)

发布
用户头像
受教了
2021 年 02 月 09 日 11:00
回复
感谢评论~以后欢迎一起探讨哈
2021 年 02 月 09 日 11:54
回复
没有更多了
架构进阶之路:复杂业务开发与领域驱动设计