learn-tech/专栏/领域驱动设计实践(完)/057精炼领域分析模型.md
2024-10-16 11:38:31 +08:00

223 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
057 精炼领域分析模型
通过统一语言与“名词动词法”可以迫使团队研究问题域的词汇表,简单而快速地帮助我们获得初步的分析模型。但是这种方法获得的模型品质,受限于语言描述的写作技巧,统一语言的描述更多体现在是对现实世界的模型描述,缺乏深入精准的分析与统一的抽象,使得我们很难发现一些隐含在统一语言背后的重要概念。一言以蔽之,由此获得的领域分析模型还需要进一步精炼。
分析模式
对相同或相近的领域进行建模分析时,一定有章法和规律可循。例如同样都是电商系统,它们的领域模型定有相似之处;如果都为财务系统,自然也得遵循普适性的会计准则。这并非运用行业术语这么简单,而是结合领域专家的知识,将这些相同或相似的模型抽象出来,形成可以参考和重用的概念模型,这就是 Martin Fowler 提出的分析模式。Fowler 认为:“分析模式是一组概念,这些概念反映了业务建模中的通用结构。它可以只与某个特定的领域相关,也可以跨越多个领域。”由于分析模式是独立于软件技术的,就使得领域专家可以理解这些模式,这是分析建模过程中关键的一点。
在建立领域分析模型时我们可以参考别人已经总结好的分析模式。例如Martin Fowler《分析模式》中介绍的模式覆盖的领域就包括组织结构、单位数量、财务模型、库存与账务、计划以及合同期权、期货、产品以及交易等领域。Peter Coad 等人在《彩色 UML 建模》一书中也针对制造和采购、销售、人力资源管理、项目管理、会计管理等领域给出了领域模型,亦可以视为分析模式的一种体现,至少可以作为我们建立领域分析模型的参考。
我们也可以建立自己的分析模式。每个行业都可以定义自己的分析模式。要获得这样的分析模式需要专精的领域专家与软件设计师共同来完成。只可惜沟通与知识的壁垒让这样一个重要的分析工作变得举步维艰。软件业的普遍现象是我们重视了软件开发技术却忽视了领域专家给开发团队带来领域知识的重要性。在领域分析建模活动中扮演重要作用的不是开发团队而是领域专家。Martin Fowler 在《分析模式》一书中就这样写道:
我相信有效的模型只有那些真正在问题域中工作的人才能建造出来,而不是软件开发人员,不管他们曾经在这个问题域工作了多久。
如果能够认识到分析模式是企业软件系统中的一份重要资产或许我们能够说服领域专家将更多的时间用到寻找和总结分析模式的工作上来。总结出一种模式并不容易需要高度的抽象能力和总结能力。无论如何为系统的核心领域引入一些相对固化的模式总是值得的。Eric Evans 就认为利用这些分析模式,“可以避免一些代价高昂的尝试和失败过程,而直接从一个已经具有良好表达力和易实现的模型开始工作,并解决了一些可能难于学习的微妙的问题。我们可以从这样一个起点来重构和实验。”
分析模式可以作为领域分析建模的起点。Martin Fowler 在《分析模式》书中写到:
对于你自己的工作,看看是否有和模式相近的,如果有,用模式试试看。即使你相信自己的解决方案更好,也要使用模式并找出你的方案为什么更适合的原因。我发现这样可以更好地理解问题。对于其他人的工作也同样如此。如果你找到一个相近的模式,把它当作一个起点来向你正在回顾的工作发问:它和模式相比强在哪里?模式是否包含该工作中没有的东西?如果有,重要吗?
当然,分析模式并非万能的灵药。即使已经为该领域建立了成熟的分析模式,也需要随着需求的变化不断地维护这个核心模式。注意,模式并非模型,它的抽象层次要高于模型,故而具有一定通用性。正因为此,它无法真实传递完整的领域知识。分析模式是领域分析模式的参考,利用一些模式与建模原则,可以帮助我们进一步精炼领域分析模型,使得该模型能够变得稳定而又具有足够的扩展能力。
接下来,我将尝试运用分析模式中提到的建模原则与建模实践针对电商网站的促销领域进行分析建模。通过分析促销领域的业务背景,逐步地对促销领域分析模型进行精炼。这个精炼的过程运用了如下建模原则:
建模原则:将模型清晰地分解成操作级和知识级。
建模原则:如果某个类型拥有多种相似的关联,可以为这些关联对象定义一个新的类型,并建立一个知识级类型来区分它们。
建模原则:保证分析模型中的概念遵循单一抽象层次原则。
除最后一个建模原则来自我个人的定义之外,其余建模原则均来自 Martin Fowler 的《分析模式》。该书通过大量的案例总结了领域分析建模的原则与模式,值得每一位建模人员认真阅读。
促销领域的分析模式
促销Promotion是一种运营手段目的是通过这种手段去刺激消费的各种信息把信息传递到一个或更多的目标对象以影响其态度和行为提高转化率。为了拉动消费无论是线上还是线下商家总是会绞尽脑汁提供各种促销手段这就带来了促销策略的复杂性然而从消费心理角度考虑要刺激消费简单有效的方式就是让消费者认为花了更少的钱却买了更多的商品这就带来了促销策略的相似性。
促销领域的业务背景
在电商系统中,对促销的管理主要牵涉到对促销活动与促销规则的管理。同时,促销还会影响到订单、库存、物流以及支付。倘若我们将促销视为核心领域,则为它建立领域分析模型时,应以促销领域为主。
促销活动
促销活动实际上是针对促销进行基本属性管理,负责提供活动方式和商品内容,主要包括:
商品选择:参加促销的商品,分为活动商品和赠品两种;也可以选定商品的品种参与促销,例如针对图书类开展促销。
投放时间选择:即该促销的有效时段。
投放区域选择:针对全平台还是部分平台(自营或指定店铺),或者仅针对 App 平台。
用户类型针对新注册用户、VIP 用户等。
促销规则
促销规则是促销管理的核心。一个促销系统的好坏取决于促销规则设计是否合理。设计时既要考虑到商品的促销,又要考虑到店铺的盈利,还要考虑滞销品和畅销品的差别,因此促销规则的制订是非常灵活的,范围和促销力度也各有不同。大体来看,我们可以从平台、商品种数、促销方式这三个维度来分别理解促销规则的制订:
平台维度:促销规则可以分为自营促销和 POP 平台促销。
商品总数维度:站在商品角度看促销,则促销可以分为单品促销、集合促销和店铺促销:
单品促销:以单个商品为维度进行的促销叫单品促销,如限时抢。
集合促销:通过商品集合来满足促销规则进行的促销叫集合促销,如满额减。
店铺级促销:以商家店铺为维度进行的促销叫店铺级促销,如店铺级满额折。
促销方式维度:
直减类限时抢、直减、多买多折、VIP 专享价、手机专享价等
赠品:满赠
换购类:加价购,凑单
满额类:满额减、满额折等
返券类:满额返券
组合优惠类:套餐
预订类:团购
在配置促销规则时,还需要考虑规则的优先级,它会直接影响促销活动的共享与互斥。例如,我们可以按照一定的优先级完成用户的优惠享用,如享用了单品促销,就不能参加集合促销,满减优先级大于代金券;这是互斥的情况。促销活动也可以共享,例如满额减可以与满额包邮共同使用。
对促销领域的分析建模
在为促销领域进行分析建模时首先需要甄别出该领域的核心概念然后分析这些概念在该领域中蕴含的业务意义。基于前面介绍的业务背景我们知道促销领域的核心概念包括促销活动与促销规则。在管理促销活动时需要指定促销规则这就产生了二者之间的关联关系。表面看是通过促销活动去配置促销规则前者为主后者为辅但对于促销而言其实是活动与规则合二为一组合形成一种促销产品Promotion Product。这种促销产品可以是“券Coupon”形式也可以是“礼品卡Gift Card”形式又或者是提供“打折Discount”或者“包邮Free Shipping”。
模型概念“促销产品”的获得实际上是分析建模过程中对关系建模的一种体现。分析模式的建模原则提到:如果某个类型拥有多种相似的关联,可以为这些关联对象定义一个新的类型,并建立一个知识级类型来区分它们。在现实世界中,各种概念之间总会存在各种错综复杂的关系。例如在学校,有教师与学生之间的师生关系,有院长与教师之间的上下级关系,有教授与研究生之间的科研关系。一旦关系变得越来越多,越来越复杂,仅仅靠体现对象之间的委派关系来体现这种组合就会显得缺乏表现力,这个时候就可以将“关系”提炼为模型中一个显式的概念。
前面所述的促销活动与促销规则在业务上存在一定的重复。例如平台维度的促销规则其实对应的是促销活动中对投放平台或区域的选择。商品总数维度的促销规则又与促销活动中适用商品品种选择的配置重叠了。这是因为我们扩大了所谓“规则”的外延。规则Rule不是计划也不是策略而应该是一条条具体的可判断是否满足条件的约束规则。
例如在电商领域中,我们常常会这样来描述一个促销规则:
购指定图书满 100 元减 20 元,满 200 元减 40 元,在 2018 年 12 月 12 日当天有效。
或许市场人员在现实中就是这样来谈论促销规则,但领域分析模型并不一定就是现实世界模型的概念映射。在领域分析建模时,我们需要精确的概念。
事实上这一描述并非促销规则而是一次完整的促销分析描述中的字词“指定图书”属于促销活动中对适用商品品种的配置“2018 年 12 月 12 日当天有效”是该促销的有效时段属性。唯有描述“满 100 元减 20 元,满 200 元减 40 元”才是所谓的规则。该规则又包含了两条金额阈值的条件Criterion。描述中的促销活动与促销规则组成了促销产品类别为“券Coupon券的类型为现金券若描述中为满额折扣就是折扣券。诸多概念合起来最终形成了一次促销。这个促销模型如下所示
引入规格模式
促销规则包含了多个条件,只有商品满足了该条件才能确定它是否适用于该促销。这让我想起了 Martin Fowler 与 Eric Evans 共同提出的“规格模式Specification Pattern”。他们对规格模式的描述如下
问题
选择Selection需要基于某些条件Criterion选择对象的一个子集且需要多次刷新其选择
验证Validation需要根据确定的目标获得满足条件的合适对象
按需构造Construction-to-order需要描述对象应该做什么而无需解释对象执行的细节这样就可以构造一个候选对象来满足需求
解决方案
创建一个规格Specification对象它能够辨别候选对象是否满足某些条件。规格对象定义了方法 isSatisfiedBy(anObject),如果 anObject 的所有条件均满足,则返回值 true。
结果
解除需求设计、实现与验证之间的耦合
提供清晰的声明式的系统定义
规格对象可以是单一的也可以是合成的。于是通过引入规格模式我将原来定义的“条件Criterion”领域概念更名为“规格Specification”。一个促销规则可以包含多个规格而对于规格而言根据不同的促销场景又可以分为
金额Amount阈值的规格例如满 200 元减 40 元,或满 200 元 9 折
数量Count阈值的规格例如满 2 件 9 折,又或者限量购
于是,前面获得的促销模型就调整为:
避免领域概念的混淆
在分析促销模型时我发现模型中的促销产品概念并未处于同一个抽象层次多种促销产品之间甚至存在混合关联。例如作为“促销产品”的折扣Discount或现金抵用Reward可以单独针对一次促销提供也可以和同为“促销产品”的券Coupon进行捆绑同时作为“促销产品”的礼品卡Gift Card和券均可以提供同为“促销产品”的赠品或者包邮。
既然出现如此混乱的关系,就说明打折、现金抵用、赠品和包邮等概念并非一种促销产品,它们其实应该是促销产品的一种属性。我将这种属性称之为“促销产品类型”。例如,券的促销产品类型若为折扣,就是折扣券,若为现金抵用,就是现金券。它们都是券,差异在于促销产品的类型不同,而非促销产品不同。
在电商系统的促销策略中诸如折扣、现金抵用之类的促销手段未必需要通过券或者礼品卡的形式呈现它们其实可以直接作为促销产品而被单独使用。但在领域分析建模过程中我们不允许概念层次的混乱且必须避免领域概念的二义性。例如对于折扣Discount到底是促销产品还是促销产品类型必须分辨清楚。
在之前识别的促销产品概念中,到底哪些属于促销产品,哪些属于促销产品类型呢?既然后者是前者的一种属性,我们就可以将促销产品视为促销产品类型的载体。因此,只要分辨出这些概念的主次关系,就可以做出正确的划分。在前面给出的混合关系模型中,显然,券和礼品卡才是主要概念,折扣、现金抵用、包邮与赠品都是次要概念。
促销产品和促销产品类型不是随意搭配的例如“包邮”就既不属于券产品又不属于礼品卡产品。如此一来还需要针对这些概念建立一层抽象这个抽象概念与券、礼品卡处于同一个抽象层次。这个抽象的促销产品概念就是“优惠Special Offer”。由此得到的模型为
优惠概念的获得遵循了建模原则——保证分析模型中的概念遵循单一抽象层次原则Single Layer Abstraction Principle。单一抽象层次原则本是 Kent Beck 在 Smalltalk Best Practice Patterns 一书中提到的。他认为一个方法应该执行一个确定的任务方法由多个处于相同抽象层次的操作组成形成“组合方法Composed Method”模式。方法如此领域模型亦当如此因为它们都是针对任务进行逐级分解的过程。正如苹果、西瓜可以和土豆处于同一个抽象层次水果和蔬菜的抽象层次则在它们之上。如果将苹果与蔬菜放在同一层自然会造成概念的失衡状态正如让“包邮”和“折扣”与“券”放在同一层是失衡的。
知识级和操作级
当模型变得渐趋复杂时《分析模式》引入了操作级Operational Level和知识级Knowledge Level两个层次来组织模型中的概念。操作级模型记录该领域每天发生的事件知识级模型则定义了操作级对象的合法配置以及控制着结构的各种通用规则。知识级和操作级之间并没有非常清晰的鸿沟但 Martin Fowler 认为“将两者(知识级和操作级)分开有助于理清建模思路”。为此,我们需要明确这二者之间的差别。
在《分析模式》一书中Martin Fowler 引入了英国国民医疗服务制度的 Cosmos 项目作为分析模式的案例,这个模型的推导过程清晰地展现了引入这两个层级是怎么让模型变得更加清晰的。
Cosmos 作为一个医疗保健系统需要对医药行业的测量和观察需求建立模型。简单说来每个患者的测量结果可以建模为“测量Meassurement”。然而针对整家医院即使是一个患者也可能存在成千上万种可能的测量。如果为每种测量定义一个相应的属性就意味着一个患者存在着成千上万种可能的测量操作——测量的接口就会变得格外复杂。分析模型的解决方案是将所有可以被测量的不同事物身高、体重、血糖水平……都作为测量对象并将其抽象为“现象类型Phenomenon Type”。这里测量属于操作级现象类型属于知识级
在为测量引入现象类型后,患者可以有许多测量,但是针对某一种现象类型而言,患者就只有一个测量。例如 John Smith 身高 1 米 75在上述模型中该描述信息整个代表一个测量其中患者是 John Smith现象类型是身高数量是 1 米 75。
为什么现象类型属于知识级,测量属于操作级呢?
首先,测量是医药行业每天都会发生的事件,而现象类型则是测量的多种配置,这符合前面对操作级与知识级的定义。
其次,《分析模式》的建模原则提到:“操作级中的对象会经常发生变化,它们的配置由很少发生变化的知识级来约束。”这是从变化的角度来区分的。操作级中的“测量”可以被定义成多种多样的测量,但知识级的“现象类型”却是可以穷举的。因此,这里提到的“变化”表达的并非类型的变化,而是对象值的变化。
最后,领域概念中存在一些定性的描述,例如医疗观察模型中的血型 A 现象、汽车分类观察模型中的汽车油量不足现象,它们都是在系统中确确实实存在的客观事实,不会因为观察是否建立而消亡,像这样的定性描述放到知识级中,可以按照规则来使用它们。这是从领域概念的性质来区分的。
回到电商系统的促销策略模型。促销可以被定义为多种多样但促销产品与促销类型在促销领域中却是可以穷举的因此促销应该被定义在操作级而促销产品与促销类型则属于知识级。从领域概念的性质看促销产品为券类型是一种定性描述促销产品类型为折扣Discount也是一种定性描述这也可以得出它们同为知识级对象的结论。促销规则与规格与之相同它们还是对促销的配置因此也应该划归知识级对象的范围。
每个促销都有属于自己的类别Label这个类别是促销的一种定性描述属于知识级对象。在计算促销优惠时不同类别的商品会分别计算同一类别可以兼容这相当于分类汇总。对于促销而言如果我们将一个具体的促销实例视为一个实体在计算促销优惠时同一实体的促销是互斥的不同实体的促销可以叠加组合也可以按照优先级Priority。这个优先级属于促销的属性。优先级可以在配置促销策略时事先配置也可以由买家指定例如买家在购买商品时出现了多种促销叠加的情况买家就可以根据具体的购买情况选择最适合自己的促销这时用户指定的优先级要高于事先配置的优先级。
一个促销对应一个促销产品。除了促销产品具有不同的产品类型外促销自身也有自己的类型。这个类型确定了促销的适用范围准确地说是确定了“促销活动Promotion Activity”的适用范围。促销活动属于操作级因为它类似医疗案例中的测量概念。作为一个活动概念显然具有时间属性和状态这就引入了“有效时段”与“状态”概念。有效时段限制了促销活动的适用时间范围而促销活动的状态又与有效时段有关可标记为“有效”和“无效”代表了该促销活动是否在有效时段内。状态是一种可以穷举的定性描述放在知识级有效时段则不同它并非定性描述而是促销活动的固有属性因此应该和促销活动一起放在操作级。
一个促销并不会对应某一个具体的买家。促销面向商品和店铺,通过类别来说明它的使用范围。提供给买家的其实是促销产品。例如,一个买家获得了一张现金券或者礼品卡。为了避免买家无限次地享受促销福利,促销产品也需要标记其状态,包括未使用、已使用和过期状态。其中,已使用和过期状态都表现了该促销产品的无效状态,说明该促销产品对应的促销策略已经失效。为了区分促销活动与促销产品的状态,需要用限定修饰符说明,分别为“活动状态”和“产品状态”。
促销活动概念的引入对于促销而言具有重大意义,某种程度上,它根据变化频率的不同,将与促销相关的概念分成了完全独立的两部分。例如,一旦促销确定了优先级和类别,就不会轻易进行调整;而有效时段与促销状态则经常发生变化,如果作为促销的属性,就会受到时间和状态的限制,让促销无法被重用。促销活动与促销之间的分离,使得促销更加稳定,在保证重用的同时,还能避免促销被无限使用带来的潜在风险。
通过引入分析模式的建模原则与模式,我们对最初的模型进行了精炼,最终获得了如下的领域分析模型:
对分析模型的验证
我们可以结合实际的业务场景验证获得的促销分析模型。以京东商城为例,如下图所示:
上图给出了两种促销类别Label联合促销活动与玩具元旦特惠。以玩具元旦优惠类别为例促销Promotion为“跨店铺满减”。该促销的活动类型Activity Type包括适用店铺值为“跨店铺”、适用品种值为“玩具”。促销产品Promotion Product为优惠Special Offer促销产品类型Product Type为满减Reward规则Rule为金额阈值规则规格Specification为满99.00元减。促销活动Promotion Activity的有效时段Valid Period为 2019 年 1 月 1 日。图中的两种玩具都属于同一个促销类别,因此在计算满减时,这两个商品是可以叠加的。对应的分析模型为:
我们再来看另外一个促销场景:
上图展现的促销场景包含了多种促销它们的促销类别Lebel皆为京东自营因此在进行优惠计算时这些商品是可以叠加的。这里包含的促销实体有
促销实体促销产品为券Coupon促销产品类型为满减Reward优惠规则为金额阈值规格分别为满 49 减 6、满 158 减 20、满 258 减 30、满 388 减 50 等。促销活动的活动类型Activity Type为适用店铺值为京东自营活动状态为“有效”。一旦领取了券则产品状态为“未使用”。
促销实体促销产品为券Coupon促销产品类型为满减Reward优惠规则为金额阈值规格为满 98 减 10。促销活动的其中一种活动类型Activity Type为适用店铺值为京东自营另一种活动类型为适用商品值为自营晨光指定商品活动状态为“有效”。一旦领取了券则产品状态为“未使用”。
促销实体促销产品为优惠Special Offer促销产品类型为包邮Free Shipping优惠规则为金额阈值规格为满 99。促销活动的活动类型Activity Type为适用店铺值为京东自营活动状态为“有效”。
促销实体促销产品为优惠Special Offer促销产品类型为换购Trade-in优惠规则为金额阈值规格为满 30。促销活动的活动类型Activity Type为适用店铺值为京东自营活动状态为“有效”。
在促销模型中,这些促销实体就是一个个促销。实现时,体现为多个促销实例,这些促销实例可以通过促销活动的“适用商品”活动类型,作用到同一件商品,形成这种促销优惠的叠加。
目前给出的促销模型考虑还不全面。一方面这取决于它适用于哪种电商应用场景,例如淘宝与京东的促销策略就不相同。如果电商销售的仅为虚拟商品,促销领域逻辑更是有所不同。另一方面,该模型未考虑如何与计算订单金额、支付以及退换货这些业务相结合。总之业务越复杂,模型也会变得更复杂,这时就更需要利用抽象将模型化繁为简,又或者考虑在模型中使用不同的视图来表达不同的概念,尽量让模型变得精简而直观,同时又不会缺少关键的领域概念。