域模型之三, 设计方法简介

  1. 云栖社区>
  2. aliexpress-技术团队博客>
  3. 博客>
  4. 正文

域模型之三, 设计方法简介

郭东白 2016-02-22 12:03:03 浏览7746

这篇文章是一系列文章的第三篇。 如果还没有阅读过第一篇请从这里开始。 第二篇文章介绍了域模型主要概念的定义。 这是第三篇, 中间因为过年隔的时间较长,见谅。 这里Persona和scenario部分的例子用英文给出,主要是为了AliExpress的域模型虚拟小组同学写典型用户和场景的时候有个模板。 

典型用户和核心场景

前面讲了, 域模型是问题域模型, 所以的域模型设计首先是对问题的描述过程。 问题描述比较实用的方法就是微软创建的Scenario Driven Design的方法, 用自然语言把一个问题描述清楚。这个描述有两个部分, 一个是典型用户(Persona), 另一个是场景(Scenario) 先从Persona开始:

典型用户的方法是用一个人来描述一群人。 就像用中间值(median)描述一组数一样。 不同点是典型用户在现实世界上不存在。 只是为了理解和交流的方便故意把描述写得很真实。下面这个例子是我为一个假想的比价购物的电商初创公司Compara写的一个典型用户的描述:

Emily, 17 year old Caucasian, is a junior high student at Lincoln High School in Providence, Rhode Island, USA.  She can read and speak Spanish and Italian, the native tone of her parents. Emily is not religious, though her family attends the local Catholic Church on holidays. She is an avid painter and is the captain of the soccer team.  Her athletics and academics might get her into a top tier university.  Even though her family couldn’t afford oversea travels, she loves to read and learn about other cultures. Her volunteering experience at local ESL center brought her many foreign friends already, whose homelands she would like to visit one day. In her spare time, she read and post stories on Facebook using mobile phone.

下面我来分解一下这段描述:

Emily(是1998~2001出生女孩最受欢迎的名字), 17 year old(年龄很重要,决定如何投放线上和线下广告)Caucasian(人种说明我们要做一个国家主流人群, 而不是低收入的少数民族人群), is a junior high student at Lincoln High School (描述我们平台的调性和主流人群,这个例子是年轻女性,而不是一个家庭妇女)in Providence, Rhode Island, USA(美国东部人口聚集的地方, 我们主打城市而不是乡村).  She can read and speak Spanish and Italian(描述教育程度, 我们要找主流人群), the native tone of her parents. Emily is not religious, though her family attends the local Catholic Church on holidays(描述人群的宗教信仰, 我们平台必须服务有宗教和无宗教信仰的人群). She is an avid painter and is the captain of the soccer team(描述兴趣爱好, 开始给这个用户一些独立的特征,平台要提供完整的一站式购物体验).  Her athletics and academics might get her into a top tier university(这一句和下一句相呼应,说明我们要找的不是社会底层, 而是仅仅是当前收入偏低的人群).  Even though her family couldn’t afford oversea travels, she loves to read and learn about other cultures(说明一下这个典型用户的强烈好奇心). Her volunteering experience at local ESL center brought her many foreign friends already(说明她的开放的心态和社会影响力), whose homelands she would like to visit one day(一个渴望成功的人士才能正面影响到更多的人群). In her spare time, she read and post stories on Facebook (她的主要影响输出渠道)using her mobile phone(主要的端).

看了解释,大家应该明白短短一段话的意义。 每个词句都有很强的目的性, 并且能够描述出一个健康,开放,正面,乐观的女孩。这是对我们平台最想追求的早期用户。 我们期望通过他们来影响到更多的用户, 有可能是同一个学校的同学,或者是远隔千里的网友。这段描述也给了其他团队同学一个指导方向。 比如说一个用户界面设计的同学会使用很活泼而不是很严肃的色彩。一个运营的同学会首先去推广一个活泼亮丽的少女短裙而不是一个老气厚实的防寒大衣。 一个做技术的同学会首先把资源投在手机社交网络传播而不是联盟流量自动投放上。 

这里强调一下我个人的能力和时间有限, 写出的样本不一定能够达到大家学习的要求。 这里我讲一下我见到过的高水平的典型用户描述的具有的几个特点:

  1. 强针对性, 和商业目标强绑定。 用户画像清晰明确,有取舍,有差异化。所有的人都照顾到其实就是谁都没有照顾到。 3. 描述要有价值,对将来的业务和技术架构决策有帮助。 4. 要精略,减少不必要的修饰,避免误解。5.  随时调整。 业务目标变化了, 用户画像也要变化。  

有了persona, 我们就可以描述一个场景了。 下面是一个比价场景的例子:

【背景】Emily and her friends love hanging out in local mall, although she finds it increasingly harder to do so because of the upcoming SAT.

【痛点】Today, Emily and her friends have been circling the mall for two hours already. With the 30 dollars she earned from baby-sitting the booger licking boy next door, her choices are limited. Besides, she needs to set aside 10 dollars for her share of gas and a Taco Bell meal. After sifting through the entire clearance pile of Express, she found a pair of shorts she likes. Well, to be exact, she does not like the pair. Priced at USD24.99, it is what she is willing to entertain price-wise.  The shorts has probably been tried on for fifty times, thinking of that, she finally gave up. 

【行动】While waiting for her friends, Emily decided to try her luck with Compara. She took a picture of a pair of shorts from the regularly priced rack, frowning at the  USD59.99 price; then she opens up Compara.   

【结果】Wow, what a collection of shorts!  Compara must have tens of choices roughly matching the picture just taken!  Compara even conveniently displays only those matching Emily’s size. Most of these listings priced from USD14.99 to USD24.99, which she think is a fair price for such shorts and of course the 7-day wait.  Carefully going through the minute differences among all the choices, she locked down onto a pair, one that she favors far more than the Express one in front of her. Now she worries a bit about the quality, the promotional price of USD14.99 sounds almost too good to be true. However, this particular pair has a five star review and free domestic return, what a find!  She happily placed order. Better yet, with the $5 left, she found a Dashiki shirt for her hippy boyfriend. While, it is not really a Dashiki Shirt for USD5, but a T-Shirt with a Dashiki print, but how clever!

鉴于篇幅,这次我就不再逐句分析了。

用户场景是我们排优先级的基础。 并非所有的用户故事都会被赋予同样的优先级。核心场景要不断讨论锤炼, 仔细推敲。 从这个角度来看,我写的这个场景案例只是个人修改几次,没有真的经过不同的合作方仔细推敲, 不能作为好的范例。 同前面Persona的一样, 我见过的好的场景描述往往能够抓住下面几个要点:

  1. 讲清楚痛点(第二段)。 这是重中之重, 是域模型的基础, 应该给予足够的篇幅。
  2. 讲清楚行动点(第三段)。 是什么触发用户从痛苦的现状中寻找新的体验, 这个临界点和触发条件是什么样的, 用户的行动的障碍有多大(也就是Barrier of Entry)。
  3. 不讲实现方案, 只讲结果(第四段)。 用户故事是从用户角度出发的, 具体如何实现由设计来完成。所以场景只需要讲结果,当然也要合情合理,有足够细节。特别强调出这个产品和其他竞争对手之间地位的不同点(比如说示例中的等待期,价差,本地免费退货等)。
  4. 不能太精略,要有铺垫,要有冲击力。 一个用户故事如果干巴巴的, 没人愿意看, 就拿不到投资。而公司内的项目也一样, 一个真正能够服务用户的电商系统至少要有上百个用户故事,也要通过好的故事打动资源的持有人。
  5. 要从初到细, 以上的例子试讲一个公司的基本场景。但是分到了一个项目或者团队上, 粒度就太粗了。 假如说Compara公司要做一个快捷支付项目, 那么这个子场景就会对交易和收银台环节描述的很细致。
  6. 要随时调整,理由同前。
  7. 最后强调最重要的一点,描述好用户的心态。让大家能够清楚的体会到这个场景是用来满足用户什么样的心态的。 比如说在这个例子里, Emily的心态是追逐Affordable Quality, 是有理智有品位的消费。 这是核心。

实体类型抽取

有了核心场景,就可以先抽取实体,然后再抽象出实体类型。

抽取实体应该是个寻找需要独立识别个体的过程。 简单来说就是找名词。 最频繁出现的名词往往是核心实体。 比如说上文中的Emily。 当然出现在痛点描述中出现的实体我们一般不太需要关心, 比如说rack, clearance pile等等。 有些名词出现在文章中时从语法上来说不是一个实体,比如说a pair of shorts,但是在这个场景中代表某个特定短裤,也是实体。  

自然语言描述中不区分实体和实体类型, 比如说choices或者是listings就是对同一个实体类型的多种描述,而不是实体。

我们做领域建模,目的是抽取不同的实体类型。以这个场景为例, 我们可以抽象出这些实体类型(括号中是场景中提到的实体或者实体类型): 买家(Emily, Emily’s boyfriend),产品照片(picture),产品(a pair of shorts, the express one,a Dashiki shirt),商品 (listing, choice),订单(order) 和 评价(review)。

这些实体类型是互相联系的。 比如说一个产品可能有多个照片, 买家上传一个或者是多个产品照片, 上传的照片可能和零到多个商品匹配, 买家最终会选择一个商品提交订单, 每个商品上会有一个或者多个评价。 看到这里某些同学脑海里可能会有一个大概的模型了。

做过电商的同学这时候可能会提问,评价到底和订单的商品的关系是什么? 大家可能听说过某些平台允许任何注册用户提交评价,而某些平台仅仅允许下单并且完成交易的用户提交评价。其实这个问题在这个场景中不存在确定答案的。所以往往一个实体模型的最终确立需要涉及不只一个场景。 换句话说一个实体模型可以关联不只一个场景。 这是第一个需要注意的地方。

到这里大家可以看出来,从这个场景抽象出来的实体类型的粒度非常粗。 这是正常的。 因为这个示例场景是基本的核心场景,如果用一个图结构表示场景之间的联系的话, 这应该是一个树状结构的根节点。 它会有多个子结点提供更细分的场景,子结点也可以有更多的子节点继续细分。 而且场景的演化也是跟业务的扩展和复杂度提升密切相关。

同样的演化也存在于实体类型本身。 一个产品在形成的初期可能是一个非常具体的品牌和设计, 但是随着产品流行,一个产品可能演化为一个类目。 在一个电商网站的一件产品,在另一个网站可能是一个大的类目。这在非标产品中比较常见,比如说下图中牛仔短裤(Denim Short, 左), 齐B牛仔短裤( Daisy Dukes, Denim Shorts 的一种, 中),磨破齐B牛仔短裤(Distressed Daisy Dukes,右),就是一个从单品流行到类目的过程,并且这个流行仍然在深化, 而且跟国家和人群很有关系(穆斯林国家对比欧美)。 也就是说,从实体类型从产品到类目的过渡有一个相对含糊的边界并且随着设计的演化会逐渐改变。 标类和虚拟产品相对稳定一些,但是最近十多年智能手机的演化就是个极端的例子。  

实体,以产品为例,的演化  

模型表述

实体类型的表述有几个传统的方式,就是通过1. 自然语言描述, 2. 关系模型,和3.状态图。 其中文字描述除了前面的典型用户描述和场景描述之外,还有对实体模型的定义。

在整理文档是大家是按1,2,3的顺序。 但是建模的过程是2,3,1 这样的顺序。 这篇文章是对建模的表述, 所以用后者。

关系模型的表达我们借用数据库的Crow Foot表达方式, 如下图。 这里需要强调一下, 我们只是借用表达方式, 这里我们不是在描述一个数据库的逻辑模型或者是物理模型。 下图中有两个实体类型, 每个实体类型有一个阿拉伯数字标识。 每个实体类型会有1个或多个属性, 其中必然有一个实体的唯一标识,以“《实体类型》ID”的方式表达, 并且借用数据库的“PK”标识来标记。 如果一个实体类型对另一个实体类型形成依赖,我们就借用数据库的“FK”标识来标记。 如果这个依赖是强依赖就用粗体字标识这个属性, 也就是说这个属性是必须的。 这里强调一下,“PK” 和“FK”标识和数据库无关,而是为了简化域模型依赖关系的表达, 一个复杂领域的域模型有成百甚至上千实体类型, 如果每个依赖关系用拉线的方式表达,那么整个域模型的图被被密密麻麻的关系线占满,无法修订和阅读 。实体间的关系用Crow Foot 脚标表示Cardinality, “II” 表示有且仅有唯一,“IO”表示零或一,“I<”表示一个或多个“O<”表示一个或多个。 同时关系线上用“《实体ID1》关系《实体ID2》 ”来描述具体关系, 举个例子来说,图中“负责”关系可以读作“每个项目由一个并且仅有一个同学来负责, 一个同学可以负责一个或者是多个项目”。
域模型表达规范

有了标准,我们可以试图把前面的描述表示成域模型图如下。 每个产品照片都对应零个或一个产品(如何形成对应和问题域模型无关, 是否一定要形成对应在这个场景没有描述),每个产品照片对应一个并且只有上传买家ID。每个产品照片可以匹配多个商品,而每个商品可以被多个产品照片所匹配。 每个商品有自己的价格和图片。 一个买家浏览或着搜索多个商品, 一个商品也可以被多个买家浏览搜索。 每个订单对应一个商品和一个买家。 每个评价对应一个商品和一个买家, 而且有星级和内容。  

 域模型1

模型验证之一, 场景回放

从场景描述到模型的过程是一个从自然语言到形式语言的过渡过程。 之前比较模糊的概念必须在这个过程中澄清。 在这个过程中,有些尚未发掘的实体类型就会被发掘出来, 而模型中的缺陷也会被显示出来。  验证的过程就像做一个头脑实验, 用模型来返回去印证场景。 一个场景中隐性的存在着一些实体类型只有通过模型验证才能找到

举个例子,在前面的模型里, 订单对应到一个并且是仅有一个商品。 也就是说这个模型里不允许买家一次下单买多个商品, 但是这个与场景描述不符。 这个买家Emily下单其实买了两件商品,一件是给自己的短裤,另一个是给男朋友的T-shirt。而模型里很明显一个订单是无法对应两件商品的。 类似这样的问题我们需要和业务方仔细讨论, 确定到底是否场景描述有问题,还是模型设计有缺陷, 假设业务方确认场景正确或者在另一个关于订单管理和修改的场景中明确描述一个订单中可以提交两件商品, 那么这个模型就需要被修改。  我们假设实际情形就是这样, 那么我们就会得出一个结论,这个场景的建模需要引入子订单这个实体类型:一个订单中含有多个子订单,子订单和商品唯一关联。

继续看根据场景验证,细心的同学可能看到场景中有些描述还不能被已有的模型覆盖。 比如说价格(price), 等待时间(7-day wait),退货(free domestic return),促销(promotion)等等。买家并非简单地选择一个了图片就提交了订单吗, 而且在一些非常明确的特定条件下(比如说促销价,7天等待,免费不包邮在国内退货)达成了交易。 如果这些条件不存在, 买卖双方是无法达成交易的。而这些条件在已有的模型中无法表达。 这种所谓的不同条件其实就是买卖双方达成一笔交易的时候隐性的进入的合同条款。 也就是说在这个场景描述包含了一个隐性实体类型:达成交易的合同条件。 做过电商的同学可能知道, 这些条件其实就是一个卖家的offer, 中文被译为出价,是一个实体类型, 不同于商品。  在这里插一句,事实上有些平台在同一个标品下可能有不只一个卖家, 甚至是同一个卖家也可能以不同的条件来达成交易。 有的条件可能是零售包邮包退货, 有的条件可能是批发不包邮不退货, 有的可能是个人卖家把旧货兜售出去。 在虚拟商品情况下,成交条件就更丰富了, offer会更多种多样。

有了以上的讨论, 我们就能达成新的一个模型如下:
商品域模型2

跟前一个版本相比, 这里有几个大的改动: 第一,新增子订单和出价两个实体类型。第二, 关联关系调整, 子订单唯一映射到订单和出价,出价唯一映射到商品。    第三,价格和出价绑定, 而不是和商品绑定。

 

这里还有一个细节就是促销, 做过电商的同学其实知道促销事实上并非Offer的一部分, 但是在现有的场景下无法进一步细分, 所以我们这里处理方式还是和前面一样, 等到有了新场景在验证这些场景时再演化模型。另外模型还有一些细节比如说returnTerms, DeliveryPromise也做同样处理。

 

上面介绍的过程就是域模型的演化过程的最重要部分, 如何通过场景来不断验证已有模型的正确性和有效性,从而在没有任何研发投入的情况下迅速提升对问题理解的准确性, 统一各团队对问题的理解,统一域语言, 最大程度在研发初期的防止方向性错误。场景的引入和演化可以不断的反复进行并且相辅相成,比如说一旦引入卖家入驻和商品发布场景, 前面许多讨论,包括隐性的实体类型就变得非常自然了, 有的错误也就一目了然。 整个过程就是一个逐步演化和升级的过程,如下图,目标是让建模过程的产出,也就是让下图中黑色虚线框部分,典型用户,典型场景和域模型达到自恰
域模型演化

模型验证之二, 实体类型生命周期

通过场景验证之后, 我们基本上可以确定一个域模型中所有定义的实体类型是充分的。 也就是说这些实体类型合在一起,可以精确的表述我们的问题域。  那么下一个问题来了? 这些实体类型是必要的吗? 举个例子来说, 我们到底需不需要定义产品这个实体类型? 它和商品的区别在哪里? 它和出价的区别又在哪里?

我们先研究一下产品,在这个场景里产品是独立于电商公司Compara存在的一个实体类型。 它是线下就有的一个实体。 现实世界里的iPhone6s, 星球前传之魅影危机DVD, Nike的Kobe X运动篮球鞋, 就是产品的例子。  产品的生命周期从创意开始,到设计,生产,推广环节都是发生在零售业之外,而在这之后的生命周期状态,比如说翻新,召回,置换等等,也和零售业弱相关。 产品的生命周期的主要管控者是生产商。

商品的生命周期存在在电商平台之内, 商品和产品并不需要有一一对应关系, 举例来说, Nike的Kobe X系列鞋有(regular, elite, high tops)三种型号。是否把它们当成一种商品还是三种商品还是更多种商品(颜色+尺码)完全是一个电商平台的商业判断。 商品的生命周期开始于发布, 之后有审核,上架,成交,下架, 售后等周期。 一个商品的生命周期流转的主要管控着是电商平台。

出价的生命周期也存在在电商平台之内,依附于商品但是小于商品。 如果一个电商是纯自营,那么出价的存在就表示商品可以被售卖。 但是如果是一个平台电商,那么同一件商品可能有多个来自不同卖家的出价, 甚至是同一个卖家也可以有多个不同条件的出价(批发,零售,翻新,清仓甩卖,和出租等等)。这些出价有各自的成交条件和生命周期流转的管控者,也就是卖家,他们会控制出价的发布, 生效和失效过程。 

这三个概念,产品,商品,和出价的生命周期可以相互独立存在。 举个例子:一个电商可以在没有任何出价的情况下发布商品, 比如说当前尚未生产的产品,某个电商平台就可以提前发布商品,收集信息,判断商品的合理出价和销售量。 而一个有很多出价的产品, 比如说平衡车,平台处于诉讼风险的考虑也可以将商品下架。 而一个生命周期已经结束的产品(厂家结束生产和售后支持,并且无任何出价),一个平台依然可以维护一个商品页面服务之前成交的老客户。 这在某些电子产品比如说Wifi Router上很常见。   

下图用实体状态图表达这三个实体类型的状态流转和它们实体状态间的关系。 从这个图可以明显的看出这三个实体之间的状态区别。 这个信息, 再和前面的实体模型图上已经表达出的信息(产品和商品是一对多的关系, 商品和出价又是一对多的关系)我们就可以比较清楚的证实这个模型里产品,商品和出价各自存在的必要性。 

  实体类型状态图

事实上我们上面得出的结论也可能从卖家商品和平台的商品自动化匹配的场景中得出。 但是通过实体生命周期分析而得出的必要性结论往往更有指导意义, 而且有助于我们更加透彻的理解实体类型。 只有在域模型建立的初期对实体类型的深刻了解才能避免后期昂贵的设计失误。  

实体类型的定义

有了前面的透彻理解, 我们就可以为实体类型下定义了。 下面就有产品,商品和出价为例给出定义。 

产品是生产商从原材料加工设计而成,并且可以最终投放于市场来满足购买者某种需求的物品。产品可以包括有形的产品比如说日用品,电器,零配件等, 也包括虚拟产品比如说旅行,数字产品,保险, 餐饮,上门服务等。 

商品是电子商务中由平台管理者投放到市场里并且展示或推荐给购买者的物品, 购买者在平台上达成交易后可以获得物品本身或者是使用该物品的权限。 商品是购买者在电商平台上能够找到的任何有交易可能的实体或虚拟物品或者服务。

出价是某个卖家在一个平台上投放的为某个商品设定的达成交易所需要的条件, 其中包括但不限于成交价格,币种,数量,包装,品质保障,货物送达时间,服务履行期限,和退换货条件等等。 买卖双方在平台上以某个价格上达成的交易是以出价的具体条件为限制的。 

这里有几个要点需要解释一下:

  • 简洁明了准确。 定义就像一个词典里的解释一样, 不能太长。用词要反复推敲务必准确。 
  • 以问题域为界。 我们不是为大百科全书写词条, 而是为了统一团队内对问题的认识。 
  • 使用示例来解释定义。 很多定义写准确很难, 但是用例子来解释相对容易很多。 定义中使用图表也可以。 
  • 定义要完整,不能循环定义。 如果一个定义又提到另一个需要定义的实体类型,那么另外一个定义也有解释清楚。 这里的定义提到了“市场”, “购买者”,“卖家”等实体类型, 如果是一个完整的域模型定义那么必须也要包括这些类型的定义。 这里因为篇幅略去。

域模型文档组织

上面是从推导的角度给大家介绍域模型的生成过程。 而一个文档组织其实不是这样, 先要讲背景知识,商业目标,然后将核心用户,核心场景, 之后对问题域的第一层的实体类型定义并且给出域模型,实体状态图,和描述, 最后由浅入深再解释下个层次的域模型, 文章附录应该提供必要的reference, 尚未解决的问题,没有覆盖的部分, 争议点等等。 这样的域模型定义就比较完整了。

 

下一篇,我们就具体给出一些域模型的实例, 进一步讲述设计中的一些要点。