深入认识DDD

数字化转型中DDD的重要性愈加突出

数字化转型是当前非常热门的一个话题。那到底什么是数字化转型呢?它是指通过各种数字化的手段对各行业进行改造以重塑其运营模式、商业模式和客户交互模式。我对其本质特征的理解自动化一切,以达到极致效率,最典型的表现便是:

1. 通过互联网技术使信息传递自动化,从而消除一切信息不对等。传统产业的很多运作模式其实是基于信息不对等的历史状况产生的。因为信息不对等,传统B2B商业模式需要通过人与人的频繁沟通并借助复杂的解决方案包装完成商业闭环。再比如传统公司中金字塔式的组织架构里领导有很强的权威,但很多时候并不是因为他们水平高而是因为掌握着更多的信息(来自上级的信息,来自本团队的全局信息,来自周边团队的信息)。为什么要这样呢?因为传统组织中担负信息流动的手段很低效,最典型的便是Email这种点对点的人肉通信手段。而在这种层次沟通效率下,只有金字塔式的组织架构才能确保最高领导的意志高效地在组织中得到贯彻。但在今天数字化的背景下,大部分商业环节和要素都可以通过互联网技术在线协同从而消除信息不对等。比如当前如火如荼的公有云(一切产品信息在线透明)对传统IT厂商形成的封喉之势。再比如京东和淘宝(所有商品信息在线透明)对传统零售业的沉重打击。再比如谷歌(去中心化小团队运作,所有员工的工作成果在线透明)的创新和运营效率超出传统行业几个数量级的。

2. 通过人工智能技术使服务迭代自动化,从而快速提升服务水平。在人工智能之前大数据很火,大数据之前BI(商业智能)很火。但后两者只是汇总和挖掘历史数据来呈现信息的总体趋势和内在关联,以供决策层进行决策参考。而人工智能与前两者的本质区别是通过消费大量的数据来实现算法的自我优化,即通过给算法喂养数据它会自我成长。这便大大加快了服务趋善的效率。当前各电子商务网站的自动推荐系统和谷歌的搜索业务便是明显的例子。

可以说一个企业未来数字化应用的广度和深度,将直接决定其竞争力。数字化转型考验着所有企业决策者的想象力、魄力和执行力。它将涉及到企业组织结构的转变、商业模式的转变和企业文化的转变,这些转变都不可能是一蹴而就的,一定需要经历多轮的试错迭代和反馈闭环。而这一切的基础必然是一套全新的IT系统,其复杂性将当前典型系统高出几个数量级,并且必须非常灵活敏捷以适应未来业务的不断变化调整。如何做到这一点呢?DDD(领域驱动设计)将会是缓解这一问题的一剂良药

什么是DDD(领域驱动设计)

Domain-Driven Design由Eric Evans 在2004年的同名著作中提出的,书的副标题是软件核心复杂性的应对之道。其实这本书在很长一段时间里并没有引起太大反响,但随着软件项目面对的业务复杂性快速上升且需求变更速度不断加快,人们越来越认识到DDD在处理软件业务复杂性时的价值。在软件项目中,人们经常会将关注点更多地放在技术上,而忽视业务领域本身复杂性。在DDD的推荐实践中,项目组首先需要做的便是和领域专家一起梳理和理解业务领域以建立起一套富含知识的领域模型。业务领域的一种抽象,是对真实世界进行选择性的简化和有意的结构化的结果。这里可能有人会想这难道不就是传统瀑布模式中的需求分析么?但建立领域模型和传统需求分析的根本区别在于领域模型中的元素会被映射成代码而传统需求分析恰恰反对这么做。当然这个过程不是单次和单向的,而是需要经过很多轮反馈迭代(即敏捷实践)才能产生出一个精炼的领域模型及与之相对应软件代码。另外书中作者给出了很多有用的模式来帮我们更平滑地完成这个过程,后面我们也会讲到。

为什么DDD更有效

有效控制复杂性是软件项目成功的关键。如我们提到的软件复杂性的核心来自业务领域。为什么DDD能更有效地应对业务领域的复杂性呢?我们拿它与传统的瀑布模式来比较。在传统模式中,首先系统分析员深入客户业务环境做需求调研和分析,然后将分析结果交给设计师进行软件设计,最后设计成果交给程序员进行编码实现。这是活生生地将传统建筑业的工程实践搬到了软件业中,也是造成无数软件项目失败的原因。为什么在建筑业中非常成功的实践在软件业中就不灵了呢?还是因为复杂性。建筑工人可以根据设计图纸用钢筋和砖头原样盖出大楼,但程序员很少能完全按照软件设计师的设计成果编码出可以工作的软件。因为很多设计决策只有到编码时才会遇上,而软件设计师是不可能提前预料到所有情况的。如果将软件开发中逼近真实需求的过程比作射击的话,我们面对的局面则是目标正离我们越来越远并且移动得越来越快。这种情况下如何射中目标呢?这个问题貌似无解不过DDD有办法,怎么做到呢?先从控制论说起举个例子,火炮射击目标的准确度非常依赖于瞄准的水平,并且对距离超出一定范围的目标便无能为力了。而导弹便不存在这个问题,甚至可以跨洲射击远程目标(现在甚至已经可以打卫星了)。如何做到呢?利用了控制论的反馈原理,即在射出后能不断校正自己目标的相对位置直到射中目标并且即使移动的目标导弹也可以轻易射中。而火炮射击的准确性完全依赖于炮兵射击的一刹那所以目标的距离越远越难射中,移动目标就更难了。传统的瀑布模型就好比是在用火炮射击目标,完全依赖于一开始的需求分析水平。但随着项目复杂度的提升企图一开始便理解清楚所有的业务逻辑几乎是不可能的,于是也必然射不中目标了。而DDD就类似于用导弹射击目标,即紧盯业务领域(需求)然后通过反馈迭代不断向目标逼近。所以DDD必然比传统模式更有效了。这里提到反馈迭代,可能又有人想这不就是传统的敏捷吗?DDD的实践中确实是非常推荐敏捷过程DDD在敏捷过程中注入了很多新的内容,最核心的便是始终瞄准业务领域。这和典型的敏捷实践是不一样的,比如在经典的《敏捷软件开发——原则、模式与实践》中,Bob大叔便强调敏捷过程中需求分析时不用考虑后期的设计。这类似于将每一轮迭代做成一个小型的瀑布,其初衷是:1.让分析过程更聚焦,紧紧围绕用户需求;2. 要给后期的设计留有灵活的空间。但DDD的推荐实践是始终围绕业务领域,在分析、设计和编码活动中领域专家和开发人员共用同一种语言。在理想情况下,开发人员写出的领域相关代码领域专家是可以看懂的,即“程序员写代码时也要说人话”。

DDD的基础:统一语言

项目中相关方使用的这种语言便被称为统一语言。不要将统一语言和我们日常生活所讲的语言混淆。统一语言是日常语言的子集,由经过明确定义的业务术语,领域概念和专有概念(专门为软件开发而提炼出的概念)等组成。在平时的项目交流中,领域专家要用统一语言描述业务,开发人员要用统一语言设计和编码。当然统一语言的形成也是一个迭代的过程。随着对领域的理解越来越深入,我们要不停地为统一语言注入新的元素,删除掉过时的元素,或重新界定一些概念的范围等等。这是一个充满挑战的过程,不过也是软件设计工作的魅力所在。人们认识事物的过程便是从混乱中寻找秩序的过程。拿学习摄影来说,一开始我们只知道一些照片好看,另一些照片不好看。但我们说不出来好看的原因,只有一种朦朦胧胧似懂非懂混乱的感觉。经过学习我们发现原来好的照片是因为其鲜明的色彩,新奇的构图或巧妙的后期处理通过概念建立了秩序。再进一步,构图好的照片又可能要归因于线条和形状的组合,或节奏与对比的把握等等逐步深入。这个对摄影领域深入认识的过程便是一个从混乱中逐步发现秩序并识别出概念的过程。我们对业务领域的认识过程与之完全类似,为什么要使用统一语言呢?因为如果没有一个统一语言,分析、设计和编码便经常被划分成三个独立的阶段。但将这三个阶段割裂的直接后果便是每一个阶段的成果要想在下一个阶段应用都需要跨越鸿沟,并且前一个阶段的错误会累积到下一个阶段。由控制论可以知道,控制的效率取决于反馈周期的长短和信息流动的带宽。即导弹如果要更准确地射中目标便需要更快地完成从瞄准到调整自身的周期(反馈周期短)或每个周期能捕获到目标更准确的信息(信息带宽大)。显然统一语言能够最大化地缩短这一反馈回路,并增大信息流动的带宽。如下图所示:

 

DDD核心模式解析

1. 限界上下文 传统的IT系统中一些数据库表动辄上百个字段,这些表最终会被映射成程序中的类。很明显,这些类承担了过多的职责,内聚性很差。根本原因是没有对问题领域进行适当的划分以分离业务关注点。比如在电子商务业务中,商品实体在库存场景中我们关注的应该是其在仓库中的位置,入库时间和保质期等;同样的实体在线上销售目录场景中我们则应该关注其售价、商品描述、展示图片等;而在发货场景中关注点又变成了重量、是否包含危险品和运输条件等信息了。可见对于同一个实体在不同的场景中,我们的关注点是完全不同的,而传统的方法则是将所有这些场景混在一起,从而导致了上百个字段的壮观场面。在DDD中很重要的一步便是按照业务关注点对领域进行划分,从而形成不同的限界上下文。前述的统一语言和领域模型应该是以限界上下文为边界的。如果这里的限界上下文划分的粒度足够小的话,便为下一步的微服务架构的实施打下了坚实的基础。

2. 实体、值对象和领域服务 用统一语言对业务领域进行建模的过程便是从混乱中发现秩序和分解概念的过程。DDD中将这些概念分为三种类型

I. 实体是指领域中存在生命周期的对象通常会经历创建、修改和最终被删除的整个过程。实体最显著的特点便是存在一个唯一性标识。只要标识相同,无论实体的状态经历怎样的变化都代表同一个实体。比如HR系统中的员工显然是一个实体。每一个员工实例都有一个明显的生命周期,并伴随整个生命周期其标识保持不变。

II. 值对象是对其他概念进行描述的对象,通常一个值对象一旦被创建便不应该再对其进行修改,只能对其进行整体替换。比如HR系统中的地址便可以被建模成一个值对象。它包含省、市、街道和邮编等字段。一旦一个地址对象被创建便代表了一个确定的地址,不应该再对其进行修改。如果一个员工搬家了那就将其整个地址字段进行替换,而不应该通过修改地址值对象来实现。为什么要创建值对象呢?首先它是对领域概念的抽象,并封装了一定的业务逻辑,所以它可以大大提升实体内聚性,比如地址对象之于员工实体(地址对象可以封装地址有效性校验和邮编有效性校验等逻辑);其次值对象最大的优势便是其不可修改性,即没有副作用,所以在分布式环境下也不会遇到状态同步等难题。在实现层面(以Java举例),实体类的equals方法只需要比较标示字段。而值对象类的equals方法必须比较所有字段。

III. 另外有一类概念不适合建模成实体和值对象,比如银行业务中的转账概念,它是一个发生在两个账户之间的业务动作。对于这一类概念我们可以将其建模成领域服务,通常它是一个动词并且没有状态(不包含字段)。

3. 聚合 同样是出于分离复杂度的目的,我们不会直接用实体和值对象组成领域模型。而是先将一些关系密切的实体和值对象组成聚合,然后通过给聚合发消息完成特定的功能。聚合有两个特点:

I. 它是对象的组合它封装概念之间的业务规则。每个聚合都有个根实体,外界不应该和聚合内的元素直接通讯,而应该通过根实体完成与聚合整体的交互。

II. 聚合本身是一个一致性边界聚合内的各元素应该始终处于一致的状态。

比如在一个车辆出租业务中,我们可以将汽车、方向盘和轮胎建模成一个聚合。其中汽车是根实体,方向盘和轮胎是值对象。一个汽车包含4个轮胎和一个方向盘,便是聚合本身封装的业务规则。

DDD举例

我们来看一个具体例子,下图是来自GitHub上DDD-MicroService-Kata项目。

 

这是宠物商店案例的DDD模型图。图中我们看到三个限界上下文Inventory库存)、Account账号)、Order订单)。上下文中包含有实体(实线椭圆)和值对象(虚线椭圆)并且一些实体组成了聚合(大的虚线椭圆)。

 


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值