辑导语:DDD(Domain-driven design,领域驱动设计)是一种架构设计方法论,通过边界划分,将复杂业务领域简单化,帮助我们设计出清晰的领域和应用边界,保证业务模型与代码模型的一致性。本文作者结合实际经验,介绍了如何从0到1实践DDD,一起来看看吧。
随着业务的不断发展,我们发现自己的系统开始变得有点臃肿,为了减少复杂性,我们尝试借助DDD来改善我们的系统。本文记录了自己对DDD的理解和实践过程,欢迎大家一起探讨。见识所限,难免有理解不到位,希望路过的大佬不吝赐教。
如果你有以上的一些疑问,那你可以试试领域驱动设计:
DDD(Domain-driven design,领域驱动设计)是一种架构设计方法论,通过边界划分,将复杂业务领域简单化,帮助我们设计出清晰的领域和应用边界,保证业务模型与代码模型的一致性。
在细看这个定义之前,我们可以思考一下,为什么我们的业务系统会慢慢变得复杂?
常见的情况是,业务在发展过程中为了探寻发力点,需要不断地试错迭代,调整方向,而系统在设计之初,难以预期到后面的瞬息万变,为了应付业务,修修改改,久之,系统也变得复杂起来。
可以怎么办呢?及时重构呗——不改变软件系统外部行为的前提下,改善它的内部结构。
然而重构是从技术层面上抽炼出来的模型,往往不具有实际的业务含义,其他同学可能难以自然地将业务问题映射到对应的设计模型。另外,如果不能如实映射业务模型,随着业务方向调整,代码可能又开始腐败……有点像芝诺悖论中,阿基里斯永远追不上小乌龟。
那DDD怎么搞?
DDD是这么想的:”将业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构”。可能大家平时有这样的想法,但是比较模糊,未形成体系,而DDD就提供了一套完整的方法论。从业务角度去审视我们的系统,从而实现高内聚低耦合的代码。
整体而言,领域驱动设计包括战略建模和战术建模: 战略设计侧重于高层次、宏观上去划分和集成限界上下文,而战术设计则关注更具体使用建模工具来细化上下文。
1)领域、子域
在讨论问题之前,我们需要先定义好问题。
领域即问题域,通常是根据一个组织所处的行业进行识别,它基于业务的愿景,定义了系统要解决的现实问题的目标和范围。领域越大,业务的范围也越大,大的领域可以拆分成小的问题域,称之为子域。根据子域重要性和功能属性划,可以将其分为三类。
核心域、支撑域和通用域:
这个几个概念其实很容易理解,不过在划分的时候,注意要从业务的视角,而不是技术功能模块来划分。
2)限界上下文
我们语言博大精深,同样的话在不同语境下就可演变出不同含义,这在沟通时总是带来不必要的麻烦。为了准确地沟通,我们需要统一语言的边界,在相同的语言边界内沟通,才不容易出差错。
一则阿凡提当理发师惩罚一个狡猾牧师的趣事:理发时,阿凡提刮脸时问牧师:“牧师,是否要眉毛?”牧师答:“这还用问,眉毛岂能不要?”.“好,你要我就给你!”,说着就把牧师的眉毛刮下来递到他手里,牧师气得说不出话来,谁叫自己说要呢。阿凡提又问:“牧师,胡子要吗?”.“不要,不要!”牧师连忙说。“好,你不要就不要。” 嗖嗖几刀就把牧师的胡子刮下来。
在一个系统中,一个名词在不同语境可能有不同的含义,我们对它关注的属性和行为也有所不同。例如,在电商系统中,对于产品Product, 在采购上下文,需要关注产品的进价、最小起订量与供货周期;在市场上下文中,则关心产品的品质、售价,以及用于促销的精美图片和销售类型;在仓储上下文中,仓库工作人员更关心产品放在仓库的哪个位置,产品的重量与体积,是否易碎品以及订购产品的数量。
限界上下文在《实现领域驱动设计》中,用了很大篇幅去讲,它有几个重要的意义:
经过战略建模之后,我们可以得到以下的一个模型:
为了更好地理解,我们对手上的一个项目:“IoT设备增值产品管理系统”进行实践。该项目中,我们提供给商户在IoT设备上管理增值运营产品的能力。这里的IoT设备主要是微信支付刷脸设备等。商户可以在系统中创建我们业务中的增值运营产品,如电子海报、互动海报等,创建完之后,相关的增值产品会被投放到IoT设备上,进行展示、运作:
一开始我们从业务的用例出发,认为我们的系统主要是商户在我们页面网站使用,以及IoT设备通过接口连接我们后台服务,认为这两个分属不同的子域,然后梳理了一些支撑的功能:
画完草图之后,感觉不是很确定,于是便去咨询部门的DDD专家王老师(十分感谢王立老师的指导),得到了一些宝贵的建议:我们应该避免直接从表现层去看业务,表现层就像是冰山露在水面上的棱角,这些棱角看起来毫不相干,但是实际上底层是连成一块的,这些才是我们需要关注的。
就像这个项目,表面上商户和设备是分开的,实际上它们在操作都是我们的增值运营产品,应该看成我们的系统提供统一对外的服务,然后商户和设备来使用我们的服务。UGC内容存储业务用例其实没有涉及到的,属于实现时候的东西。一番建议让我们理清了思路,于是重新梳理,得到以下的战略建模图:
整体而言,我们将整体系统梳理为8个子域:
其中我们系统中的商户信息依赖了微信支付商户账号信息和IoT设备铺设服务信息,这里使用防腐层进行隔离,将外部的商户信息“翻译”为我们业务中的商户信息。三、如何实现DDD之战术建模梳理清楚上下文之间的关系后,我们基本了解业务的概貌,接下来需要细化上下文,进一步完善我们的模型。这里也需要用到DDD的一些基本概念。
1)实体、值对象
实体和值对象是组成领域模型的基础单元。当一个对象由其标识(而不是属性)区分时,这种对象称为实体。如在校园教务系统中,每个账户是对应着一个学生,根据学号来唯一标识,可以认为是一个实体。传统的数据建模大多是根据数据库范式设计的,每一个数据库表对应一个实体,每一个实体的属性值用单独的一列来存储,一个实体主表会对应 N 个实体从表。
与其不同,DDD 是先构建领域模型,再将业务对象映射为持久化对象。这可能导致DDD建立出来的实体,映射到具体数据库表时,可能是1对多,多对1的关系。
如一个账户实体,有它的基本信息和权限角色信息,可能就对应了2个持久化对象。另一方面,有时候为了某些查询场景的方便,会把教师账户、学生账户等对应成一个持久化对象,就成了多对1。
通过对象属性值来识别的对象,则可以认为是一个值对象。如地址信息{“省”: “广东省”,”市”:”深圳市”},我们是通过它的属性来区分出不同的地址。值对象实际上是想把一些不变的属性组合起来,减少系统的复杂性。在设计值对象的时候,需要满足以下的特性:
2)聚合、聚合根
在 DDD 中,实体和值对象是基础的领域对象。实体一般对应业务对象,它具有业务属性和业务行为;而值对象主要是属性集合,对实体的状态和特征进行描述。但是我们的一个业务流程中,一般会同时涉及多个实体、值对象的操作,这里业务逻辑紧密的实体和值对象便组合成一个聚合。
从数据层面来看,同个聚合内的数据需要保持强一致性。
每一个聚合有一个聚合根实体,设置聚合根的主要目的是为了避免由于复杂数据模型缺少统一的业务规则控制,而导致聚合、实体之间数据不一致性的问题。聚合根可以看成是聚合的管理者,或是说handle。对内其协调实体和值对象完成业务逻辑。对外则提供通过聚合ID供其他聚合关联引用,屏蔽外部对内部实体的直接访问和修改。
建议的聚合设计原则:
3)领域服务
领域服务的定义:领域中的服务表示一个无状态的操作,它用于实现特定于某个领域的任务。当某个操作不适合放在聚合(实体)或值对像上时,最好的方式便是使用领域服务。
举个例子,在一个路线导航的项目中,“路线”可能是其中的一个实体,如果业务中有“推荐路线上相关的美食”这样一个功能,那我们会想,这个功能应该归给哪个领域对象,给“路线”实体吗?有点不合适,应该路线本身关注的是起终点,时间人物等。
此时可以将其这个功能归为领域服务,它是一个路线状态无关的服务,输入路线各个节点,来得到沿路的各种美食。当然,要注意不要过度地使用领域服务,因为这很可能导致你把实体的行为都放在里面了,实体本身都变成了一些只有getter和setter的“贫血模型”。
4)领域事件
领域事件是领域模型中非常重要的一部分,用来表示领域中发生的事件。一个领域事件将导致进一步的业务操作,在实现业务解耦的同时,还有助于形成完整的业务闭环。
领域事件含义很广泛,可以是业务流程的一个步骤,也可以是一个事件发生后触发的后续动作,缴费完成之后,触发短信通知;上面在设计聚合的时候,我们提到一个原则:在边界之外使用最终一致性,一次事务最多只能更改一个聚合的状态。如果一次业务操作涉及多个聚合状态的更改,应通过领域事件,达到最终一致性。
实际上是通过事件驱动的这种异步方式,对系统进行解耦。当然,如果你觉得某两个步骤,业务流程上不允许是不一致的,那就得重新考虑将其归在同个聚合中了。
我们以增值运营服务上下文为例,根据上面的理解,结合业务实际,得到以下模型:
其中增值产品是其中的一个聚合根,通过该聚合根进行各种领域操作。海报缩略图是其中的一个领域服务,通过输入产品素材中的海报url,来得到一个海报的缩略图。
在《领域驱动设计——软件核心复杂性的应对之道》一书中,Eric提出了这样的一种分层结构,将整个系统划分为四层:用户接口层、应用层、领域层和基础设施层。
用户接口层:用户接口层负责向用户显示信息和解释用户指令。
应用层:应用层相对来说是较“薄”的一层,主要是部署了应用服务。应用服务的实现中,它负责编排和转发下一层的领域层的接口,将要实现的功能委托给一个或多个领域对象来实现,本身只负责处理业务用例的执行顺序以及结果的拼装。
领域层:领域层是比较“厚”的一层,它包含聚合根、实体、值对象、领域服务等领域模型中的领域对象,实现了核心的业务逻辑。领域层和应用层的职责看起来有点模糊。
个人觉得,可以理解是应用层描述了一个具体操作从开始到结束的每一个环节,而领域层则是对其的细化,用来处理具体的某一个环节。
比如,比如线上购物中,购物车结算这一场景可看成是一个应用行为。而这个行为又主要包括金额计算、支付、生成订单,这些子环节就可以理解为一个领域层的服务了。
基础设施层:可以看到上面三层都有箭头指向基础设施层,它的作用就是为其它各层提供通用的技术和基础服务,如数据持久化、消息中间件等DDD 分层架构中的要素与传统三层架构(用户界面层、业务逻辑层、数据访问层)还是挺相似的,一个主要的变化是将业务逻辑层的服务拆分到了应用层和领域层。应用层响应业务用例的变化,领域层关注不变的领域模型。
图片来自极客时间
《DDD实战课》在实际的代码工程便是按照这样的目录来划分,最近部门在推的整洁Git,也是这样划分目录:
接下来,便是将领域对象映射到实际的类,实现对应的属性和行为。当然,具体实现中有很多范式可参考和讨论,我们也在摸索中,待后续慢慢补充……
DDD首先不是关于技术的,而是关于讨论、聆听、理解、发现业务价值的。——Vaughn Vernon《实现领域驱动设计》
如Vernon所说的,DDD首先是关注业务的价值的。一开始我们对业务的边界、目标可能有个大概了解,但是见解还是不尽相同。
通过一起对业务的讨论与思考,我们了解了业务的概貌及核心,明确价值所在。关注到了核心,自然可以帮助我们实现与业务契合的系统。通过这次学习与实践,我们进一步接触了DDD。
当然,这也还只是开始,更多的关联知识还隐藏在冰山之下。同时我们也明白,DDD也只是一种方法论上的参考,不是“银弹”,需要不断地去实践与思考,才能体会出它的价值。
参考:
作者:bryanzhao,微信支付后台开发工程师
本文由 @腾讯大讲堂 原创发布于人人都是产品经理,未经许可,禁止转载。
题图来自 Pixabay,基于CC0协议。
概念
SGML称为:标准通用标记语言。
定义电子文档结构和描述其内容的国际标准语言。
特点
1:通用标言为语法置标提供了异常强大的工具,同时具有极好的扩展性,因此在数据分类和索引中非常有用;
2:是所有 电子文档标记语言的起源,早在万维网发明之前“通用标言”就已存在。
只描述文档标记应该是怎么样的元语言。
总结:HTML是SGML的一个实例,它的DTD作为标准被固定下来,因此,HTML不能作为定义其它置标语言的元语言。
概念
标记电子文件使其具有结构性的标记语言,可扩展标记语言。
特点
1:结合
结合了HTML和SGML,是一种非常复杂的文档的结构。
2:友好
网站设计者可以定义自己的文档类型。
用途
1:大量高度结构化数据的防卫区
2:其他各种工业领域,利于分类和索引。
下面是 小二哥 写给 小二妹 的便签,存储为 XML:
<note>
<to>小二妹</to>
<from>小二哥</from>
<heading>Reminder</heading>
<body>记得给小伙伴们发福利哦!</body>
</note>
总结:XML 被设计用来传输和存储数据,是扩展标记语言(EXtensible Markup Language),很类似 HTML。XML 的设计宗旨是传输数据,而非显示数据,标签没有被预定义。需要自行定义标签。具有自我描述性,是 W3C 的推荐标准。
DHTML 不是 W3C 标准。指动态 HTML(Dynamic HTML)。
DHTML 不是由万维网联盟(W3C)规定的标准,是一个营销术语 - 被网景公司(Netscape)和微软公司用来描述 4.x 代浏览器应当支持的新技术。
用途
DHTML 是一种用来创建动态站点的技术组合物。
总结:对大多数人来说,DHTML 意味着 HTML 4.0、样式表以及 JavaScript 的结合物,是动态的HTML。
XHTML是更严格更纯净的 HTML 代码。是指可扩展超文本标签语言(EXtensible HyperText Markup Language)。
XHTML是当前HTML版的继承者。HTML语法要求比较松散,这样对网页编写者来说,比较方便,但对于机器来说,语言的语法越松散,处理起来就越困难,对于传统的计算机来说,还有能力兼容松散语法,但对于许多其他设备,比如手机,难度就比较大。因此产生了由DTD定义规则,语法要求更加严格的XHTML。
实例:
<!DOCTYPE Doctype goes here>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Title goes here</title>
</head>
<body>
</body>
</html>
总结:XHTML 的目标是取代 HTML,与 HTML 4.01 几乎是相同的, 是更严格更纯净的 HTML 版本。XHTML 是作为一种 XML 应用被重新定义的 HTML。是一个 W3C 标准。
HTML5是下一代的 HTML。将成为 HTML、XHTML 以及 HTML DOM 的新标准。
HTML 的上一个版本诞生于 1999 年。自从那以后,Web 世界已经经历了巨变。
HTML5 仍处于完善之中。然而,大部分现代浏览器已经具备了某些 HTML5 支持。
特点
1:语义特性(Class:Semantic)
2:本地存储特性(Class: OFFLINE & STORAGE)
3:设备兼容特性 (Class: DEVICE ACCESS)
4:连接特性(Class: CONNECTIVITY)
5:网页多媒体特性(Class: MULTIMEDIA)
6:三维、图形及特效特性(Class: 3D, Graphics & Effects)
一个简单的媒体播放实例
<!DOCTYPE HTML>
<html>
<body>
<video width="320" height="240" controls="controls">
<source src="kecheng.ogg" type="video/ogg">
<source src="kecheng.mp4" type="video/mp4">
你的浏览器不支持html5,请更换浏览器。
</video>
</body>
</html>
总结:至于html5和html就不用多讲了吧。HTML5在2007年被万维网联盟(W3C)新的工作组采用。这个工作组在2008年1月发布了HTML 5的首个公开草案。眼下,HTML5处于“最火”状态,HTML5 已成为 HTML、XHTML 以及 HTML DOM 的新标准。
上面分别介绍了SGML、XML、DHTML、XHTML、HTML5和HTML的之间的关系。顺便也看看HTML吧。
概念
超文本标记语言,标准通用标记语言下的一个应用。
特点
1:一种规范。
2:一种标准。
3:简单但是功能强大,灵活方便。
4:具有极强的扩展性。
5:不牵扯平台。
实例:
<html>
<body>
<h1>这个是标题</h1>
<p>这个是段落。</p>
</body>
</html>
总结:是用来描述网页的一种超文本标记语言 (Hyper Text Markup Language), 不是一种编程语言,而是一种标记语言 (markup language),使用标记标签来描述网页。
·欢迎留言评论哦~
.它是一种计算机(PC)的超文本标记语言(Hyper Text Markup Language),缩写为HTML,HTML是一种标记语言(markup language),是制作网页所必备的语言,语法较为松散,不严格的web语言;标签可以不闭合,不区分大小写。
2.标记标签通常被称为HTML标签,标签由尖括号包围的关键字组成,通常都是成对出现的,有开始标签和结束标签,如<html></html>。
浏览器不会显示html标签(白话:浏览器只会显示标签里边的内容),而是使用标签来解释页面的内容。
比如这一行代码:<body>你好 </body>要放在<html></html>里边,网页只展示了你好 其他内容并没有展示。
代码
页面
以下资料均由自己的认知和资料整理所得:
1989年,欧洲物理量子实验室(CERN)的信息专家蒂姆·伯纳斯·李发明了超文本链接语言, 使用此语言能轻松地将一个文件中的文字或图形连到其它的文件中去,这就是HTML的前身。1991年,蒂姆伯·纳斯·李在CERN定义了HTML语言的第一个规范,之后成为W3C组织为专门在互联网上发布信息而设计的符号化语言规范。可以说,HTML(Hypertext Markup Language)是SGML的一个实例,它的DTD作为标准被固定下来。因此,HTML不能作为定义其它符号化语言的元语言。
作为World Wide Web的一个组成部分,HTML语言发展很快,在短短的几年里,它已历经了HTML1.0、HTML2.0和HTML3.0、HTML4.0等多个版本,同时DHTML (动态)、VHTML(虚拟)、SHTML等也飞速发展起来。HTML以简单精练的语法、极易掌握的通用性与易学性,使Web网页可以亲近于每一个普通人,互联网因此得以普及发展以至今日辉煌。
但是,目前的HTML还不稳定,不同的浏览器会产生不同的显示效果。此外,由于HTML对超级链接支持不足,并缺乏空间立体描述,处理图形、图像、音频、视频等多媒体能力较弱,图文混排功能简单,不能表示多种媒体的同步关系等缺点,也影响HTML的大规模应用以及用于复杂的多媒体数据处理,一种语言各有各的优点和缺点。
人们常常赞美蒂姆·伯纳斯·李“与其他所有推动人类进程的发明不同,这是一件纯粹个人的劳动成果,万维网只属于蒂姆·伯纳斯·李一个人。”而这个心怀浪漫、献身科学的英国学者,却将只属于他一个人的发明,无偿献给了世界。
*请认真填写需求信息,我们会在24小时内与您取得联系。