标签
| DDD
字数
| 7441字
阅读
| 19分钟
最近几天,电商圈出了一件大事情,拼多多再次吸引了大家的眼球。2019年1月20日,拼多多出现了数额巨大的羊毛Bug,起因在于一张无门槛的优惠券,券面价值100元,可以全场通用(特殊商品除外),有效期一年。如果仅仅从业务角度分析,定义这样的优惠券自身并没有任何问题。当然,也有人说像这样的无门槛券本身就不该用于花费充值、Q币充值等几乎等于现金业务的商品,这是从促销层面去考虑的问题。还有人提到风控问题,为何等到损失达200亿(事后拼多多说明这些优惠券涉及到千万)才发现问题?更有人质疑这是一次别出心裁的炒作。
从薅羊毛的角度看这个Bug,似乎是因为该无门槛券可以被无限次使用导致的。本质上,这确实是一个Bug,我不明白这样的Bug是如何产生的,是测试不到位,还是说该优惠券本身是一个内部测试数据,被不小心放到生产环境?正好,最近正在考虑如何利用分析模式建立电商系统中的促销模型,算是应景之作吧。
分析模式对模型的精炼
在领域驱动设计中,通过统一语言与“名词动词法”的结合,可以快速获得初步的分析模型。但是这种方法获得的模型品质,受限于语言描述的写作技巧,统一语言的描述更多体现在是对现实世界的模型描述,缺乏深入精准的分析与统一的抽象,使得我们很难发现一些隐含在统一语言背后的重要概念。一言以蔽之,由此获得分析模型还需要进一步精炼。
对相同或相近的领域进行建模分析时,一定有章法和规律可循。例如同样都是电商系统,它们的领域模型定有相似之处;如果都为财务系统,自然也得遵循普适性的会计准则。这并非运用行业术语这么简单,而是结合领域专家的知识,将这些相同或相似的模型抽象出来,形成可以参考和重用的概念模型,这就是Martin Fowler提出的分析模式。Fowler认为:“分析模式是一组概念,这些概念反映了业务建模中的通用结构。它可以只与某个特定的领域相关,也可以跨越多个领域。”由于分析模式是独立于软件技术的,就使得领域专家可以理解这些模式,这是分析建模过程中关键的一点。
每个行业都可以定义自己的分析模式,当然我们也可以参考一些已经被别人总结好的分析模式。例如在Martin Fowler的著作《分析模式》中,模式所覆盖的领域就包括组织结构、单位数量、财务模型、库存与账务、计划以及合同(期权、期货、产品以及交易)等领域。Eric Evans认为利用这些分析模式,“可以避免一些代价高昂的尝试和失败过程,而直接从一个已经具有良好表达力和易实现的模型开始工作,并解决了一些可能难于学习的微妙的问题。我们可以从这样一个起点来重构和实验。”
要获得这样的分析模式,需要专精的领域专家与软件设计师共同来完成。只可惜沟通与知识的壁垒让这样一个重要的分析工作变得举步维艰。Martin Fowler撰写《分析模式》正是希望通过引入更多的模式来降低建模的难度,至少可以做到一定程度的模型复用。然而,分析模式与领域知识息息相关,这就限制了分析模式的通用性;但这并不能说明分析模式不重要。领域驱动设计尤其强调为领域建模,对于那些具有较长生命周期的产品而言,
针对产品的核心领域建立一套分析模式绝对值得,因为它是分析阶段的重要资产,它的稳定与扩展能力可能会直接影响到领域模型中的设计模型与实现模型
。
案例:促销策略的分析模式
促销(Promotion)是一种运营手段,目的是通过这种手段去刺激消费的各种信息,把信息传递到一个或更多的目标对象,以影响其态度和行为,提高转化率。为了拉动消费,无论是线上还是线下,商家总是会绞尽脑汁提供各种促销手段,这就带来了促销策略的复杂性;然而从消费心理角度考虑,要刺激消费,简单有效的方式就是让消费者认为花了更少的钱却买了更多的商品,这就带来了促销策略的相似性。从某种意义上讲,拼多多的这次促销极大地“刺激”了消费者的热情!
促销策略的业务背景
在电商系统中,对促销的管理主要牵涉到对促销活动与促销规则的管理。同时,促销还会影响到订单、库存、物流以及支付。如果我们将促销视为核心领域,那么在为它建立分析模型时,应以促销领域为主。
促销活动
促销活动实际上是针对促销进行基本属性管理,负责提供活动方式和商品内容,主要包括:
促销规则
促销规则是促销管理的核心。一个促销系统的好坏取决于促销规则设计是否合理,设计时既要考虑到商品的促销,又要考虑到店铺的盈利,还要考虑滞销品和畅销品的差别,因此促销规则的制订是非常灵活的,范围和促销力度也各有不同。大体来看,我们可以从平台、商品种数、促销方式这三个维度来分别理解促销规则的制订:
在配置促销规则时,还需要考虑规则的优先级,它会直接影响促销活动的共享与互斥。例如我们可以按照一定的优先级完成用户的优惠享用,如享用了单品促销,就不能参加集合促销,满减优先级大于代金券;这是互斥的情况。促销活动也可以共享,例如满额累促销可以与满额包邮共同使用。
对促销领域的分析建模
我们在为促销领域进行分析建模时,首先需要甄别出该领域的核心概念,然后分析这些概念在该领域中蕴含的业务意义。基于前面介绍的业务背景,我们知道促销领域的核心概念包括促销活动与促销规则。在管理促销活动时,需要指定促销规则,这就产生了二者之间的关联关系。表面看,是通过促销活动去配置促销规则,前者为主,后者为辅;但对于促销而言,其实是活动与规则合二为一组合形成一种
促销产品(Promotion Product)
。这种促销产品可以是“券(Coupon)”形式,也可以是“礼品卡(Gift Card)”形式,又或者是提供“打折(Discount)”或者“包邮(Free Shipping)”。
模型概念“促销产品”的获得实际上是分析建模过程中对
关系建模
的一种体现。在现实世界中,各种概念之间总是会存在各种错综复杂的关系,例如在学校中,有教师与学生之间的师生关系,有院长与教师之间的上下级关系,有教授与研究生之间的科研关系。一旦关系变得越来越多,越来越复杂,仅仅靠体现对象之间的委派关系来体现这种组合就会显得缺乏表现力,这个时候就可以将“关系”提炼为模型中一个显式的概念。
建模原则:
如果某个类型拥有多种相似的关联,可以为这些关联对象定义一个新的类型,并建立一个知识级类型来区分它们。
前面所述的促销活动与促销规则在业务上存在一定的重复。例如平台维度的促销规则,其实对应的是促销活动中对投放平台或区域的选择。商品总数维度的促销规则,又与促销活动中适用商品(品种)选择的配置重叠了。这是因为我们扩大了所谓“规则”的外延。规则(Rule)不是计划,更不是策略,而应该是一条条具体的可判断是否满足条件的约束规则,例如:
购指定图书满100元减20元,满200元减40元,在2018年12月12日当天有效。
以上描述并非
促销规则
,而是一次完整的
促销
,该促销的
促销产品
为“券(Coupon)”,券的类型为现金券(若描述中为满额折扣,就是折扣券)。描述“指定图书”属于促销活动中对适用商品(品种)的配置,描述“2018年12月12日当天有效”则是该促销的
有效时段
属性。唯有描述“满100元减20元,满200元减40元”,才是所谓的
规则
。该规则又包含了两条金额阈值的条件(Creteria)。面对这种场景,我们就可以在分析模型中引入“
规格模式(Specification Pattern)
”。
规格模式
由Martin Fowler与Eric Evans提出,他们对规格模式的描述如下所示:
问题:
选择(Selection):需要基于某些条件(Creteria)选择对象的一个子集,且需要多次刷新其选择
验证(Validation):需要根据确定的目标获得满足条件的合适对象
按需构造(Construction-to-order):需要描述对象应该做什么而无需解释对象执行的细节,这样就可以构造一个候选对象来满足需求
解决方案:
创建一个规格(Specification)对象,它能够辨别候选对象是否满足某些条件。规格对象定义了方法isSatisfiedBy(anObject),如果anObject的所有条件均满足,则返回值true。
结果:
解除需求设计、实现与验证之间的耦合
提供清晰的声明式的系统定义
规格对象可以是单一的,也可以是合成的。
一个促销规则可以包含多个规格,而对于规格而言,在促销场景又可以分为:
结合业务分析与模型分析,我们可以得出如下的分析模型:
分析促销产品时,我们发现模型中的概念并未处于同一个抽象层次,且相互间存在混合关联的关系。例如打折(Discount)或现金抵用(Reward)可以单独针对一次促销提供,也可以和券(Coupon)进行捆绑;礼品卡(Gift Card)和券均可以提供赠品或者包邮。显然,诸如打折、现金抵用、赠品和包邮等概念并非一种促销产品,而是一种促销产品类型,是促销产品的一种属性,例如券的促销产品类型若为打折,就是折扣券,若为现金抵用,就是现金券。这时,促销产品其实是这些产品类型的一种载体。
在电商系统的促销策略中,诸如打折、现金抵用之类的促销手段未必需要通过券或者礼品卡的形式呈现,它们其实是可以作为促销产品被单独使用的。但在建模过程中,我们却不允许概念层次的混乱,因为我们必须要避免领域概念的二义性。例如对于打折(Discount),到底是促销产品,还是促销类型,必须分辨清楚。既然概念层次不在同一个抽象层次上,我们就需要针对这些概念建立一层抽象,这个抽象概念是与券、礼品卡处于同一个抽象层次。这个抽象的促销产品概念就是“优惠(Special Offer)”。因此,前面建立的模型就可以改进为:
建模原则:
保证分析模型中的概念遵循单一抽象层次原则。
知识级和操作级
当模型变得渐趋复杂时,《分析模式》引入了操作级(operational level)和知识级(knowledge level)两个层次来组织模型中的概念。操作级模型记录该领域每天发生的事件;知识级模型则定义了操作级对象的合法配置,记录控制着结构的各种通用规则。知识级和操作级二者之间并没有明确的差异,但Martin Fowler认为“将两者(知识级和操作级)分开有助于理清建模思路”。为此,我们需要明确这二者之间的差别。
在《分析模式》一书中,Martin Fowler引入了英国国民医疗服务制度的Cosmos项目作为分析模式的案例,这个模型的推导过程清晰地展现了引入这两个层级是怎么让模型变得更加清晰的。
Cosmos作为一个医疗保健系统,需要对医药行业的测量和观察需求建立模型。简单说来,每个患者的测量结果可以建模为“测量(Meassurement)”。然而针对整家医院,即使是一个患者也可能存在成千上万种可能的测量。如果为每种测量定义一个相应的属性,就意味着一个患者存在着成千上万种可能的测量操作——测量的接口就会变得格外复杂。分析模型的解决方案是将所有可以被测量的不同事物(身高、体重、血糖水平……)都作为测量对象,并将其抽象为“现象类型(Phenomenon Type)”。这里,测量属于操作级,现象类型属于知识级:
在为测量引入现象类型后,患者可以有许多测量,但是针对某一种现象类型而言,患者就只有一个测量。例如John Smith身高1米75,在上述模型中,该描述信息整个代表一个测量,其中,患者是John Smith,现象类型是身高,数量是1米75。
那么为什么现象类型属于知识级,测量属于操作级呢?《分析模式》给出了一条建模原则:“操作级中的对象会经常发生变化,它们的配置由很少发生变化的知识级来约束。”这是
从变化的角度
来区分的。操作级中的“测量”可以被定义成多种多样的测量,但知识级的“现象类型”却是可以穷举的。因此这里提到的“变化”表达的并非类型的变化,而是对象值的变化。
领域概念中存在一些定性的描述,例如医疗观察模型中的血型A现象、汽车分类观察模型中的汽车油量不足现象,它们都是在系统中确确实实存在的客观事实,不会因为观察是否建立而消亡,像这样的定性描述放到知识级中,可以按照规则来使用它们。这是
从领域概念的性质
来区分的。
回到电商系统的促销策略模型,促销可以被定义为多种多样,但促销产品与促销类型在促销领域中却是可以穷举的,因此促销应该被定义在操作级,而促销产品与促销类型则属于知识级。当然,各自级别内部的属性自然也会归入到各自的级别中,正如数量之于测量,在促销策略模型中,有效时段就属于促销的属性,因此有效时段也属于操作级的概念。
促销活动属于操作级,因为它类似案例中的测量概念,针对某一种促销活动类型而言,一次促销只有一个促销活动。为了应对操作级对象(促销活动)的变化,驱动我们引入了知识级的促销活动类型(Activity Type)概念。
从领域概念的性质看,促销类型为折扣(Discount)是一种定性描述,券类型为现金券也是一种定性描述,因此促销类型和券类型属于知识级。那么,促销规格为“满200元减40元”是一种定性描述吗?——不是。虽然一旦定义了这样的规格,它确实是一种不变的事实,但它却是附着在促销规则上,一旦促销规则失效或者被删除,这样的规格就没有存在的意义了。因此促销规则与规格应属于操作级的概念。