关于复杂业务治理的方法论

从if-else说起 

我经常说,我们不要做一个if-else coder。这里的if-else,不是说我们在coding的时候不能使用if-else,而是说我们不应该简陋地用if-else去实现业务的分支流程,因为这样随意的代码堆砌很容易堆出一座座“屎山”。
业务的差异性是if-else的根源。以零售通的商品业务为例。不同的处理场景,其业务逻辑实现是有差异性的。如下图所示,商品业务的差异性,主要体现在商品类型、销售方式和仓储方式的不同。
 
0
 
这三个维度上的差异组合起来,有 2 * 3 * 2 = 12 之多。这就是为什么在老代码中,到处可以看到 if(组合品) blabla,if(赠品) blabla,if(实仓) blabla 之类的代码。
那么,要如何消除这些讨厌的if-else呢?我们可以考虑以下两种方式:
  • 多态扩展:利用面向对象的多态特性,实现代码的复用和扩展。
  • 代码分离:对不同的场景,使用不同的流程代码实现。这样很清晰,但是可维护性不好。
 

多态扩展

多态扩展可以有继承和组合两种方式。继承勿用多言,组合有点像策略模式,也就是把需要扩展的部分封装、抽象成需要被组合的对象,然后对其进行扩展。
这里,我们举一个继承的例子,商品在上架的时候要检查商品的状态是否可售,普通商品(Item)检查自己就好了,而组合商品(CombineItem)需要检查每一个子商品。
用过程式编码的方式,很容易就能写出如下的代码:
public void checkSellable(Item item) {
        if (item.isNormal()) {
            item.isSellable(); //省略异常处理   
        } else {
            List childItems = getChildItems();
            childItems.forEach(childItem -> childItem.isSellable()); //省略异常处理    
        }
    }
 
然而,这个实现不优雅,不满足OCP,也缺少业务语义显性化的表达。更好的做法是,我们可以把CombineItem和Item的关系通过模型显性化的表达出来。
0
 
这样一来,一方面模型正确的反应了实体关系,更清晰了。另一方面,我们可以利用多态来处理CombineItem和Item的差异,扩展性更好。重构后,代码会变成:
public void checkSellable(Item item) {
        if (!item.isSellable()) {
            throw new BizException("商品的状态不可售,不能上架");
        }
    }

代码分离 

所谓的代码分离是指,对于不同的业务场景,我们用不同的编排代码将他们分开。以商品上架为例,我们可以这样写:
 /*** 1. 普通商品上架*/
    public void itemOnSale() {
        checkItemStock();//检查库存
        checkItemSellable();//检查可售状态   
        checkItemPurchaseLimit();//检查限购    
        checkItemFreight();//检查运费    
        checkItemCommission();//检查佣金    
        checkItemActivityConflict();//检查活动冲突
​
        generateCspuGroupNo();//生成单品组号   
        publishItem();//发布商品
    }
​
    /*** 2. 组合商品上架*/
    public void combineItemOnSale() {
        checkCombineItemStock();//检查库存   
        checkCombineItemSellable();//检查可售状态   
        checkCombineItemPurchaseLimit();//检查限购    
        checkCombineItemFreight();//检查运费    
        checkCombineItemCommission();//检查佣金    
        checkCombineItemActivityConflict();//检查活动冲突
​
        generateCspuGroupNo();//生成单品组号   
        publishCombineItem();//发布商品
    }
​
    /*** 3. 赠品上架*/
    public void giftItemOnSale() {
        checkGiftItemSellable();//检查可售状态   
        publishGiftItem();//发布商品
    }
这种方式,当然也可以消除if-else,彼此独立,也还清晰。但复用性是个问题。
 

多维分析 

细心的你可能已经发现了,在上面的案例中,普通商品和组合商品的业务流程基本是一样的。如果采用两套编排代码,有点冗余,这种重复将不利于后期代码的维护,会出现散弹式修改(一个业务逻辑要修改多处)的问题。 
一个极端情况是,假如普通商品和组合商品,只有 checkSellable() 不一样,其它都一样。那毫无疑问,我们使用有多态(继承关系)的CombineItem和Item来处理差异,会更加合适。
而赠品上架的情况恰恰相反,它和其他商品的上架流程差异很大。反而不适合和他们合用一套流程代码,因为这样反而会增加他人的理解成本。还不如单独起一个流程来的清晰。
那么,问题来了,我们什么时候要用多态来处理差异,什么时候要用代码分离来处理差异呢?
接下来,是我今天要给你着重介绍的多维度分析问题的方法论之一:矩阵分析法。
我们可以弄一个矩阵,纵列代表业务场景,横列代表业务动作,里面的内容代表在这个业务场景下的业务动作的详细业务流程。对于我们的商品业务,我们可以得到如下的矩阵:
0
 通过上面的矩阵分析,我们不难看出普通品和组合品可以复用同一套流程编排代码,而赠品和出清品的业务相对简单,更适合有一套独立的编排代码,这样的代码结构会更容易理解。
 

维度思维

多维度的重要性

结构化思维有用、很有用、非常有用,只是它更多关注的是单向维度的事情。比如我要拆解业务流程,我要分解老板给我的工作安排,我要梳理测试用例,都是单向维度的。
而复杂性,通常不仅仅是一个维度上的复杂,而是在多个维度上的交叉复杂性。当问题涉及的要素比较多,彼此关联关系很复杂的时候,两个维度肯定会比一个维度要来的清晰,这也是为什么说矩阵思维是比结构化思维更高层次的思维方式。
实际上,我们从汉语的词汇上,也不难看出一个人的思维层级,是和他的思考维度正相关的。当我们说这个人很“轴”、“一根筋”的时候,实际上是在说他只有一维的线性思维。所以,观察事物的视角越多,维度越丰富,其思维层级也会越高。
0
 

复杂业务治理总结

完整的方法论应该是“业务理解-->领域建模-->流程分解-->多维分析”。
为了方便大家理解,下面我把这些方法论做一个简单的串联和解释。
 

业务理解

理解业务是所有工作的起点。首先,我们要找到业务的核心要素,理解核心概念,梳理业务流程。
比如,在零售通的商品域,我们要知道什么是商品(Item),什么是单品(CSPU),什么是组合品(CombineItem)。在下单域,我们要知道订单(order)的构成要素是商品、优惠、支付。在CRM领域,我们要理解客户、机会、联系人、Leads等等。
你不应该放过任何一个模糊的业务概念,一定要透彻的理解它,并给与合理的命名(Ubiquitous Language)。唯有如此,我们才能更加清晰的理解业务,才能更好的开展后续的工作。
 

领域建模

在软件设计中,模型是指实体,以及实体之间的联系,这里需要我们具备良好的抽象能力。能够透过庞杂的表象,找到事务的本质核心。
再复杂的业务领域,其核心概念都不应该太复杂,抓住了核心,我们就抓住了主线,业务往往都是围绕着这些核心实体展开的。
比如,商品域虽然很复杂,但其核心的领域模型,无外乎就如下图所示:
 
0
 

流程分解

简单来说,流程分解就是对业务过程进行详细的分解,使用结构化的方法论(先演绎、后归纳),最后形成一个金字塔结构。
比如,在商品领域,有创建商品、商品上架、上架审核、商品下架、下架审核、修改商品、删除商品等一些列动作(流程),每个动作的背后都有非常复杂的业务逻辑。我们需要对这些流程进行详细的梳理,然后按步骤进行分解。最后形成一个如下的金字塔结构:
 
0
 

多维分析

业务的复杂性主要体现在流程的复杂性和多维度要素相互关联、依赖关系上,结构化思维可以帮我们梳理流程,而矩阵思维可以帮忙我们梳理、呈现多维度关联、依赖关系。二者结合,可以更加全面的展现复杂业务的全貌。从而让我们的治理可以有的放矢、有章可循。
既然是方法论,在这里,我会尝试给出一个矩阵分析的框架。试想下,如果我们的业务很简单,只有一个业务场景,没有分支流程,我们的系统不会太复杂。之所以复杂,是因为各种业务场景互相叠加、依赖、影响。
因此,我们在做矩阵分析的时候,纵轴可以选择使用业务场景,横轴是备选维度,可以是受场景影响的业务流程(如文章中的商品流程矩阵图),也可以是受场景影响的业务属性,或者任何其它不同性质的“东西”。
 
0
 
通过矩阵图,可以清晰的展现不同场景下,业务的差异性。基于此,我们可以定制满足差异性的最佳实现策略,可能是多态扩展,可能是分离的代码,也可能是其它。
这就是矩阵分析的要义,其本质是一种多维度思考的方法论。

总结

有了维度思维,我们便可以更加方面的去看清业务的全貌,更加全面的掌握业务信息,从而帮助我们更加体系化的去治理复杂性。

posted on 2020-11-04 23:34  Louis军  阅读(450)  评论(0编辑  收藏  举报

导航