DDD中的上下文映射

什么是上下文映射

上下文映射,对应的英文单词是Context Map,代表的是领域驱动设计中,多个限界上下文之前的关系。方便设计者和开发者能够一目了然地看到每个限界上下文和其它限界上下文之间的关系,最终的产出可能是一张映射图,或者映射卡片。实际上的限界上下文映射的设计,不只是跟设计决策和技术实现有关,还跟企业文化、组织架构有关。

有哪些上下文映射

分离方式

分离方式(separate way),分离方式指的是两个限界上下文没有任何关系,没有关系其实就是一种非常好的设计,因为它们可以独立变化,互相影响。
但在实际的开发过程中,可能两个限界上下文会有一些耦合。如果设计者认为这两个限界上下文解耦的价值远远大于复用的价值(比如分属于两个差异很大的团队),那可以通过引入少量的重复来彻底解耦开来。
比如:在电商场景中,支付上下文,就和库存上下文没有任何关系。

客户-供应

客户-供应(customer/supplier)是我们最中间的一种上下文映射方式。一方提供服务,另一方去调用服务。我们类比水流,上游发生变化可能会影响下游,所以我们把提供服务的一方称为“上游”,使用服务的一方称为“下游”。这与调用关系刚好是相反的。
比如:在电商场景中,订单上下文依赖库存上下文,所以库存上下文就是订单上下文的上游。

发布-订阅

发布-订阅(publisher/subscriber)也是一种很常见的上下文映射方式,在实际的开发过程中往往是通过消息中间件来实现。
发布-订阅模式源自于设计模式中的“观察者模式”,上下游通过消息去通信,下游注册观察者,上游作为发布者,如果上游发生了变化,会发布一个业务事件,下游收到这个事件后进行后续的操作。
发布-订阅模式与客户-供应模式最大的不同,在于发布-订阅模式,是上游主动发起业务的变化,而不是被动等下游去调用上游。它相较于客户-供应模式而言,耦合程度会低一些。
比如:在电商场景中,订单上下文和物流上下文,就可以通过发布-订阅模式来做。订单完成后,发生订单完成事件,物流上下文监听事件开始物流配送。

More...

什么是维度退化

概念:

维度退化(Degenerate-Dimension,DD):将维度退化到事实表中,减少事实表和维度表的关联,在维度建模的数据仓库中,有一种维度叫Degenerate Dimension,中文一般翻译为“退化维度”。这种退化维度一般都是事务的编号,如订单编号、发票编号等。这类编号需要保存到事实表中,但是不需要对应的维度表,所以称为退化维度。

特点/举例:

退化维度是维度建模领域中的一个非常重要的概念,它对理解维度建模有着非常重要的作用,尤其是对维度建模的入门者。

特点:

  1. 没有对应的维度表的维度。
  2. 存储在事实表中
More...

维度表设计注意事项

1、什么是维度表?

维度是维度建模的基础和灵魂。在维度建模中,将度量称为“事实” , 将环境描述为“维度”。
维度表包含了事实表中指定属性的相关详细信息,最常用的维度表有日期维度、城市维度等。

例如:日期维表:

num 字段名 字段中文名 描述 数据类型
1 date 日期 日期 yyyMMdd格式 bigint
2 week 星期,数字型 星期,数字型 0-6 bigint
3 week_cn 星期中文名 星期中文名 星期一…… string
4 year_weeks 一年中的第几周 一年中的第几周 1 2 3…… bigint
5 mon_dt 本周周一日期 本周周一日期 bigint
6 sun_dt 本周周日日期 本周周日日期 bigint
7 month 年月 年月,yyyyMM格式 bigint
8 month_short 月份简写 月份简写,MM格式1~12 bigint
9 month_cn 月份中文名 月份中文名 一月…… string
10 quarter 季度 季度,yyyyQ1\2\3\4 string
11 quarter_short 季度 数字型 季度 数字型 1-4 bigint
12 quarter_cn 季度中文名 季度中文名 第一季度…… string
13 year 年份 年份,yyyy格式 bigint

2、维度表设计原则

维度的作用一般是查询约束、分类汇总以及排序等,我们在进行维度表设计时,应当提前考虑:

(1)维度属性尽量丰富,为数据使用打下基础

比如淘宝商品维度有近百个维度属性,为下游的数据统计、分析、探查提供了良好的基础。

(2)给出详实的、富有意义的文字描述

属性不应该是编码,而应该是真正的文字。在阿里巴巴维度建模中, 一般是编码和文字同时存在,比如商品维度中的商品 ID 和商品标题、 类目 ID 和 类目名称等。 ID 一 般用于不同表之间的关联,而名称一般用 于报表标签

(3)区分数值型属性和事实

数值型宇段是作为事实还是维度属性,可以参考字段的一般用途。 如果通常用于查询约束条件或分组统计,则是作为维度属性;如果通常用于参与度量的计算, 则是作为事实。比如商品价格,可以用于查询约 束条件或统计价格区间 的商品数量,此时是作为维度属性使用的;也可 以用于统计某类目 下商品的平均价格,此时是作为事实使用的。另外, 如果数值型字段是离散值,则作为维度属性存在的可能性较大;如果数 值型宇段是连续值 ,则作为度量存在的可能性较大,但并不绝对,需要 同时参考宇段的具体用途。

(4)沉淀出通用的维度属性,为建立一致性维度做好铺垫

有些维度属性获取需要进行比较复杂的逻辑处理,有些需要通过多表关联得到,或者通过单表 的不同宇段混合处理得到,或者通过对单表 的某个字段进行解析得到。此时,需要将尽可能多的通用的维度属性进 行沉淀。一方 面,可以提高下游使用的方便性,减少复杂度;另一方面,可以避免下游使用解析时由于各自逻辑不同而导致口径不 一致。

(5)退化维度(DegenerateDimension)

在维度类型中,有一种重要的维度称作为退化维度。这种维度指的是直接把一些简单的维度放在事实表中。退化维度是维度建模领域中的一个非常重要的概念,它对理解维度建模有着非常重要的作用,退化维度一般在分析中可以用来做分组使用。

(6)缓慢变化维(Slowly Changing Dimensions)

维度的属性并不是始终不变的,它会随着时间的流逝发生缓慢的变化,这种随时间发生变化的维度我们一般称之为缓慢变化维(SCD),缓慢变化维一般使用代理健作为维度表的主健。

缓慢变化维的三种处理方式:

① TYPE1 直接覆盖原值
适用于:不看历史数据,简单粗暴

② TYPE2 拉链表
需要在维度行再增加三列:有效日期、截止日期、行标识(可选)。
在旧的一行数据增加关链时间(end_date),新的一行数据增加开链时间和**关链时间,**多条数据加起来是一个完整的时间周期。

③ TYPE3 增加属性列
保留老字段,把新增的属性放到新的字段。比如 department 变成 old_department and new_department.

关于需求分析和客户沟通

背景

关于需求分析和工期预估一直是老大难问题,Joel 的这篇文章谈到了这个问题,我挑一些记录下来。

客户不知道他们想要什么是问题吗?

通常,在定制项目中,最常见的超支、失败和悲剧的原因总是归结为,基本上,"客户不知道他们想要什么?”
事实上,有一件事是每个初级BA都需要在开展工作前要知道的,那就是:客户其实不知道他们想要什么。不要再期望客户知道他们想要什么。这是不可能发生的。忘掉它吧。
相反,你要假设你将不得不制造一些东西,而客户将不得不喜欢它,但他们将有一点惊喜。你必须做研究,想出一个设计,以一种愉悦的方式解决客户的问题。
设身处地为客户想想。想象一下,你决定装修你的厨房,因此,你聘请了一位专业的设计师,指示要把它弄得高大上一些。你不知道如何完成这个任务,你不知道那些事厨房中必须要装的,哪些事提升体验的,这些都不是你擅长的,你想让设计师帮你完成这个事情,这就是你雇用他的原因。
有一种办法是让客户进入房间,让他们作为开发团队的成员参与到设计过程的每一步。我认为,这有点太 "极端 "了。这就好像我的设计师在设计厨房时让我出现,并要求我对每一个小细节提供意见。这对我来说很无聊,我会觉得这个设计师对我毫无价值。
假设你的客户不知道他们想要什么。根据你对这个领域的理解,你自己设计它。如果你需要花一些时间来学习这个领域,或者你需要一个领域专家来帮助你,这很好,但软件的设计是你的工作。如果你做了领域方面的功课并创造了一个好的用户界面,客户会很高兴。

冰山理论

现在,我答应告诉你一个关于在你的软件的客户(或非技术经理)的语言和程序员的语言之间进行翻译的秘密。
你知道一座冰山有90%在水下吗?那么,大多数软件也是这样的–有一个漂亮的用户界面,需要大约10%的工作,然后90%的编程工作是在盖子下面。如果你考虑到你大约一半的时间是用来修复bug的,那么用户界面只占5%的工作。如果你把自己限制在用户界面的视觉部分,即像素,也就是你在PowerPoint中看到的东西,现在我们说的是不到1%。

More...

Hexo博客安装PlantUML

Hexo 静态 blog 支持 markdown 绘制 plantuml。

安装步骤

  • 进入hexo 目录
  • 安装 npm install --save hexo-tag-plantuml

使用示例

简单时序图:

1
2
3
4
5
6
7
{% plantuml %}
Alice -> "Bob()" : Hello
"Bob()" -> "This is very\nlong" as Long
' You can also declare:
' "Bob()" -> Long as "This is very\nlong"
Long --> "Bob()" : ok
{% endplantuml %}

实现效果:

事实表设计注意事项

概述

在设计事实表的时候,和设计普通的数据库有一些差异,有以下一些注意事项:

graph LR
B(包含完整业务事实)---A(事实表设计)
C(去除多余业务事实)---A
D(分解不可加事实)---A
E(先声明粒度)---A
A---F(避免不同粒度)
A---G(保持单位一致)
A---H(处理null 值)
A---I(退化维度)
More...

三种事实表

什么是事实表?

事实表作为数据仓库维度建模的核心,紧紧围绕着业务过程来设计,通过获取描述业务过程的度量来表达业务过程,包含了引用的维度 和与业务过程有关的度量。

每个数据仓库都包含一个或者多个事实数据表。事实数据表可能包含业务销售数据,如现金登记事务所产生的数据,事实数据表通常包含大量的行。事实数据表的主要特点是包含数字数据(事实),并且这些数字信息可以汇总,以提供有关单位作为历史的数据。事实数据表不应该包含描述性的信息,也不应该包含除数字度量字段及使事实与纬度表中对应项的相关索引字段之外的任何数据。

事实表的三种类型

事实表有三种类型 : 事务事实表、周期快照事实表和累积快照事实表。

事务事实表

事务事实表记录的事务层面的事实,保存的是最原子的数据,也称“原子事实表”。事务事实表中的数据在事务事件发生后产生,数据的粒度通常是每个事务记录一条记录。一旦事务被提交,事实表数据被插入,数据就不再进行更改,其更新方式为增量更新。

周期快照事实表

周期快照事实表以具有规律性的、可预见的时间间隔来记录事实,时间间隔如每天、每月、每年等等。典型的例子如销售日快照表、库存日快照表等。它统计的是间隔周期内的度量统计,如历史至今、自然年至今、季度至今等等。周期快照表没有粒度的概念,取而代之的是周期+状态度量的组合,如历史至今的订单总数,其中历史至今是一个周期,订单总数是度量。

周期快照事实表的粒度是每个时间段一条记录,通常比事务事实表的粒度要粗,是在事务事实表之上建立的聚集表,比如说时间周期是1周,那么这个周期快照事实表的一条记录就是这一周的对于某个度量的统计值(我理解的)。

累积快照事实表

累积快照事实表和周期快照事实表有些相似之处,它们存储的都是事务数据的快照信息。但是它们之间也有着很大的不同,周期快照事实表记录的确定的周期的数据,而累积快照事实表记录的不确定的周期的数据。累积快照事实表代表的是完全覆盖一个事务或产品的生命周期的时间跨度,它通常具有多个日期字段,用来记录整个生命周期中的关键时间点。例如订单累计快照事实表会有付款日期,发货日期,收货日期等时间点。

事务事实表中一条交易记录会每天有一条数据来记录整个交易过程;而累积快照事实表只会有一条记录,数据会一致更新直到过程结束。

累积快照事实表代表的是完全覆盖一个事务或产品的生命周期的时间跨度,它通常具有多个日期字段,用来记录整个生命周期中的关键时间点。另外,它还会有一个用于指示最后更新日期的附加日期字段。由于事实表中许多日期在首次加载时是不知道的,所以必须使用代理关键字来处理未定义的日期,而且这类事实表在数据加载完后,是可以对它进行更新的,来补充随后知道的日期信息。

周期快照事实表记录的是重复的可预测到的时间间隔的事实,例如帐户月结余事实表,用来记录每个月末的帐户结余信息。一般周期快照的数据会按报表需要的周期进行记录,比较适合周期长一些的情况。

累计快照适用于较短周期,有着明确的开始和结束状态的过程,如一个订单执行的过程,并记录过程中每个步骤的执行时间,使分析人员对执行的过程有整体的把握。周期快照事实表记录上每个步骤的执行时间是逐步建立的,随着执行的过程逐步更新的事实表中。

类型比较

事务事实表 周期快照事实表 累积快照事实表
时期/时间 离散事务时间点 以有规律的、可预测的 用于时间跨度不确定的不断变化的工作流
日期维度 事务日期 快照日期 相关业务过程涉及的多个日期
粒度 每行代表实体的一个事务 每行代表某时间周期的一个实体 每行代表一个实体的生命周期
事实 事务事实 累积事实 相关业务过程事实和时间间隔事实
事实表加载 插入 插入 插入与更新
事实表更新 不更新 不更新 业务过程变更时更新

业务数据治理实施流程-1

数据治理实施流程,是我们依据业务数据治理标准化框架在实施解决具体数据问题时,总结抽象出来的一套适用于大多数治理场景解决问题的通用标准流程。标准流程的好处在于更加规范化数据治理工程师的操作流程,来保证实施的质量。流程一共包含5个步骤:

STEP 1:发现问题和制定目标

发现问题要从业务数据开发团队的视角出发,围绕服务好业务、遵守数据研发规范、收集好用户反馈,尽可能全地发现和收集相关需要解决的问题。同时,制定的目标要具备可实现性。

STEP 2:设计指标

针对问题进行拆解,设计可衡量的指标,并通过元数据的采集建设进行实现,用做对目标的进一步量化,并作为实施过程监控及治理抓手。

STEP 3:制定SOP并工具化

对衡量出来的具体问题,制定相关的解决SOP,并且检查相应的研发标准规范是否完善,通过问题发生的事前、事中、事后几个阶段,建设或完善相应的工具化解决问题的能力。

STEP 4:推广实施

推广运营,以拿结果为核心目标,针对不同角色运用不同策略,重点关注问题解决过程是否会与用户利益发生冲突,控制好节奏,根据问题的重要程度有规划地进行解决。

STEP 5:总结沉淀方法论

总结方法论,迭代认知,持续探索问题的最优解,优化治理方案和能力。

graph LR
subgraph Step1 
A(发现问题)-->B(制定目标)
end

subgraph Step2
B-->C(设计衡量指标)-->D(元数据及指标建设)
end

subgraph Step3
D-->E(解决方案SOP)-->F(工具化)
end

subgraph Step4
F-->G(运营治理)-->H(效果评估)
end

subgraph Step5
H-->I(方法论沉淀)
end

反思:那些你不该做的事情

最近在重新看 Joel on Software 的blog,时隔二十年,很多错误还在重复发生,很有感触,比如这篇,翻译过来,引以为戒。

网景发生了什么?

网景6.0终于要进入第一个公开测试版了。实际上,从来没有5.0版本。上一个主要发布版本4.0已经发布了将近三年。在互联网世界,三年是相当长的时间。在此期间,网景无助地坐视其市场份额大幅下降。批评网景发布版本之间的等待时间如此之长似乎有点伪善。他们不是有意这么做的,对吧?其实是的。他们犯下了任何软件公司都会犯的最糟糕的战略错误:他们决定从头开始重写代码

程序员在想什么?

程序员的内心是建筑师,当他们到达一个地方时,首先想做的事情就是将其铲平,建造一些宏伟的东西。程序员对逐步翻新、改进和种花园不感兴趣。程序员总是想要扔掉代码重新开始有一个微妙的原因。原因是他们认为旧的代码很糟糕。这里有一个有趣的观察:他们可能是错的。他们认为旧代码很糟糕的原因是基于程序设计的一个基本法则:
阅读代码比编写代码更困难。
这就是为什么代码再利用非常困难的原因。这就是为什么你团队中的每个人都喜欢用不同的函数将字符串分割成字符串数组。他们编写自己的函数,因为这比弄清楚旧函数如何工作更容易、更有趣。

这样做的问题是什么?

新代码比旧代码更好这种想法显然是荒谬的。旧代码已经被使用过了。它已经经过了测试。已经发现了许多错误,并对其进行了修复。它没有任何问题。它不会只是因为闲置在你的硬盘上而产生错误。恰恰相反,它会变得更加完善!
比如,一个程序员在分析旧代码时会说:“看一下这个函数,它有两页长!里面的这些东西都不应该放在这里!我不知道这些API调用的一半是干什么用的。“
这个API只是一个简单的用来显示窗口的函数,但它变得越来越复杂,无人知晓。其实,原因是:那些复杂的代码都是为了修复bug。一个修复了Nancy在试图在没有安装Internet Explorer的电脑上安装应用时遇到的问题。另一个修复了在低内存情况下出现的bug。还有一个修复了当这个文件在软盘上且用户在重要时刻拔出软盘时出现的bug。而那个丑陋的LoadLibrary调用,却可以让代码在旧版本的Windows 95上正常运行。
每一个bug都需要经过几周的现实世界使用才能被发现。程序员可能花了几天时间在实验室里重现bug并修复它。如果像很多bug一样,修复可能只需一行代码,甚至可能只需要几个字符,但这几个字符需要付出大量的工作和时间。
当你丢弃代码并从头开始时,你也在丢失所有那些知识、所有那些收集到的bug修复,以及多年的编程工作

正确的做法是什么?

当程序员说他们的代码一团糟时(他们总是这么说),代码可能存在三方面问题。
首先,存在架构问题。代码的分配不正确。网络代码突然跳出自己的对话框,这些应该由UI代码处理。这些问题可以逐一解决,通过仔细地移动代码、重构、更改接口。这可以由一个程序员仔细地工作,并一次性检查他的改变,以便不会干扰其他人。即使在不丢弃现有代码的情况下,也可以进行相当大规模的架构变更。在Juno项目中,我们曾经花了几个月时间进行重新架构:只是移动代码、清理它们、创建有意义的基类,并在模块之间创建尖锐的接口。但我们以现有的代码库仔细进行,没有引入新的bug或扔掉正在工作的代码。
第二个原因是代码效率低下。据说,Netscape的渲染代码很慢。但这只影响了项目的一小部分,您可以进行优化甚至重写它。您不必重写整个项目。优化速度时,1%的工作可以让您获得99%的成果。
第三,代码可能很丑陋。我曾经参与的某个项目实际上有一种叫做FuckedString的数据类型。另一个项目开始时使用了以下划线开头的成员变量命名规则,但后来转为更标准的“m_”。因此,一半的函数以“”开头,另一半以“m”开头,看起来很丑。老实说,这种问题可以在Emacs中使用宏几分钟内解决,而不是从头开始。

总结

重要的是要记住,当你从头开始时,没有任何理由相信你会比第一次做得更好。你可能没有与原版本一起工作的相同编程团队,因此您实际上没有“更多经验”。您只会再次犯许多旧的错误,并引入一些原版本中没有的新问题。

SIKT模型构建的用户画像

SIKT模型原理

SIKT模型是一套梳理标签的方法。标签是用户画像的基础,是构建画像体系的砖石。想让用户画像系统有用,标签本身得是有价值的。这就要求,在做标签的时候,不能凭着数据人员的直觉,像说贯口一样把“性别、年龄”胡乱怼进系统,而是从业务场景出发,层层递进。

模型 说明 角色
Scene 场景 具体的业务场景 业务主导
Index 指标 该场景要达成的目标 业务主导
Key Behavior 关键行为 达成目标要做的行动 业务主导
Tag 标签 为了提升效率所需标签 数据主导

步骤

  1. 梳理业务场景。使用方先思考:我要解决什么问题?
  2. 梳理关键指标。使用方再思考:用什么指标衡量问题解决?
  3. 梳理关键行动。使用方再思考:我做什么事能解决问题?
  4. 筛选有用标签。数据协助思考:用什么标签能提升行动效率。

如何进行关键行动

可以从以下行动入手:

graph LR
A(认知现状)-->B(制定策略)-->C(选择方法)-->D(监控执行)-->E(复盘效果)
More...

请我喝杯咖啡吧~

支付宝
微信