一、前言
DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了。自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平台也算是对DDD的推广尽了一份绵薄之力。一开始接触这个东西是在2014年,真的觉得像是发现了一片新大陆一般,对我整个程序开发视野有了新的理解,但是像[Vaughn Vernon]《实现领域驱动设计》里写的那样,景色虽好,可是自己很长一段时间内很混乱,理不清眼前的陌生世界,因为它与传统的观念完全不同。我相信大部分同学刚接触DDD的时候也会有一样的感觉。
这次开始写这系列,也希望有越来越多的人能够加入到DDD的队列中,在我之前,园子里的
netfocus、
dax.net、
田园里的蟋蟀等园友都已经对推广DDD做了很多事情,再此感谢下各位,这些分享在我学习DDD的道路上给予了很多帮助。
本次系列中多处引用[Vaughn Vernon]《实现领域驱动设计》一书里的语句,我认为此书可以作为各位进入DDD的敲门砖,希望大家能够去拜读一下此书。
二、名词解释
首先我觉得有必要先把DDD中的常用名词做一个解释。
界限上下文:代表一个系统、一个应用程序或者一种业务服务。限界上下文所包含的领域模型概念应该恰如其分,不多也不少。
通用语言:作用于某个“限界上下文”,在一个特定的限界上下文中只使用一套通用语言,并且保证它的清晰性(避免一个概念在同一个界限上下文中的二义性)和简洁性。举个例子:像京东和天猫这样的B2C系统中会用到系统的人有2种,买家和卖家,对于系统来说都可以称为用户,但是这样破坏了清晰性的特点。如果使用一个类似Type的枚举来区分,破坏了简洁性。所以对于这种场景,就应该直接设计2个对象:买家和卖家。
领域:从大了看,领域代表整个公司的运作一切。从小了看,是每个组织运作中的一切。所以领域的概念必然与公司的组织架构所承担的职责有一定的关系。
子域:一个领域内可以包含1个或者多个子域。理论上一个子域对应一个限界上下文是最优也是最理想的情况,但是有时又要考虑到业务关联度需要做出权衡。子域又分核心域、支撑子域、通用子域。
核心域:它是整个业务领域的一部分,也是业务成功的主要促成因素。从战略层面上讲,企业应该在核心域上胜人一筹。我们应该给予核心域最高的优先级、最资深的领域专家和最优秀的开发团队。在实施DDD的过程中将主要关注核心域。
支撑子域:对应着业务的某些重要方面,但却不是核心,那么它便是一个支撑子域。
通用子域:某个支撑子域的运用范围是整个系统,那么这个子域便是通用子域。
上下文映射图:由多个界限上下文和子域组成的表示当前单个领域或者多个领域之间的集成关系图。
三、实施DDD的关键
我认为实施DDD最最最关键的东西有2样。
一是“通用语言”,没有基于通用语言建立的所谓的聚合,实体,值对象,只能算是DDDLite,只是技术层面的一种设计方式。
二是“建模”,建模又分为战略建模和战术建模,这2者相辅相成,来构建合理的上下文映射图。
①战略建模:战略建模是以一种最宏观的角度去审视整个项目对它进行拆分,来划分“界限上下文”,最终形成一个具有俯瞰视角的“上下文映射图”。
②战术建模:在我们战略建模划出的“界限上下文”中进行“聚合”、“实体”、“值对象”的建模,并且按模块分组。
四、如何构建一个领域的上下文映射图
对于如何构建一个上下文映射图,分为思想和操作2个层面。
首先思想层面需要引入2个空间的概念:问题空间和解决方案空间。
在问题空间中,我们思考的是业务所面临的挑战,而在解决方案空间中,我们思考如何实现软件以解决这些业务挑战。
评估问题空间和解决方案空间的问题:
①这个战略核心域的名字是什么,它的目标是什么?
②这个战略核心域中包含哪些概念?
③这个核心域的支撑子域和通用子域是什么?
其次操作层面就是[Vaughn Vernon]《实现领域驱动设计》里提到的9种组织模式和集成模式。
①合作关系(Partnership):如果2个限界上下文的团队要么一起成功,要么一起失败,此时就是这种关系。应该为相互关联的软件功能制定好计划表,这样可以确保这些功能在同一个发布中完成。
②共享内核(Shared Kernel):对模型和代码的共享将产生一种紧密的依赖性,对于设计来说,这种依赖性可好可坏。我们需要为共享的部分模型指定一个显式边界,并保持共享内核的小型化。共享内核具有特殊的状态,在没有与另一个团队协商的情况下,这种状态是不能改变的。我们应该引入一种持续集成过程来保证共享内核与通用语言的一致性。【简单的说就是数据库共享】
③客户方——供应方(Customer-Supplier Development):当2个团队处于一种上游——下游关系时,上游团队可能独立于下游团队完成开发,此时下游团队的开发可能会受到很大的影响。因此,在上游团队的计划中,我们应该顾及到下游团队的需求。
④遵奉者(Conformist):在存在上游——下游关系的2个团队中,如果上游团队已经没有动力提供下游团队之需,下游团队便孤军无助了。处于利他主义,上游团队可能向下游团队做出种种承诺,但是有很大的可能是:这些承诺是无法实现的。下游团队只能盲目地使用上游团队模型。
⑤防腐层(Anticorruption Layer):在集成2个设计良好的限界上下文时,翻译层可能很简单,甚至可以很优雅的实现。但是,当共享内核,合作关系或客户方——供应方关系无法顺利实现时,此时的翻译将变得复杂。对于下游客户来说,你需要根据自己的领域模型创建一个单独的层,该层作为上游系统的委派向你的系统提供功能。防腐层通过已有的接口与其他系统交互,而其他系统只需要做很小的修改,甚至无需修改。在防腐层内部,它在你自己的模型和他方模型之间进行翻译转换。【为每个防腐层定义相应的领域服务】
⑥开放主机服务(Open Host Service):定义一种协议,让你的子系统通过该协议来访问你的服务。并且需要将协议公开。
⑦发布语言(Published Language):在2个限界上下文之间翻译模型需要一种公用的语言。此时你应该使用一种发布出来的共享语言来完成集成交流。发布语言通常与开放主机服务一起使用。
⑧另谋他路(SeparateWay):在确定需求时,我们应该做到坚持彻底。如果2套功能没有显著的关系,那么它们是可以被完全解耦的。集成总是昂贵的,有时带给你的好处也不大。声明2个限界上下文之间不存在任何关系,这样使得开发者去另外寻找简单的、专门的方法来解决问题。
⑨大泥球(Big Ball of Mud):当我们检查已有系统时,经常会发现系统中存在混杂在一起的模型,它们之间的边界是非常模糊的。此时你应该为整个系统绘制一个边界,然后将其归纳在大泥球范围之列。在这个边界内,不要试图使用复杂的建模手段来化解问题。同时,这样的系统有可能会向其他系统蔓延,应该对此保持警觉。
五、构建我们的上下文映射图
本次的系列的主题是电商网站,那么现在开始构建一个电商网站的上下文映射图。
①这个战略核心域的名字是什么,它的目标是什么?
销售核心域,目标是卖更多的商品,获取更多的利润。这点是整个组织的共同目标,所以应该很容易界定,并且应该是在整个组织中能最终够达成一致的某个观点。如图1。
【图1,点击图片查看大图】
②这个战略核心域中包含哪些概念?
我一般会使用“划分”,“组合”,“梳理”的3步走方式去做。先根据我们对这个领域的理解进行划分出各个上下文,然后重新审视每个上下文在销售这个领域中的定位来“圈地(划分子域)”,最后再思考是否能够完全体现出每个子域的功能和职责。我的思路是这样的,先根据我自己的经验得出了图2。
【图2,点击图片查看大图】
我思考了一下,整个销售过程中,订单只是一个结果,一旦到达这个环节,其实销售的工作已经结束了,那么订单的相关业务其实不应该属于销售的子域内,所以把它拿出去,变为图3这个样子。
【图3,点击图片查看大图】
然后看了一下全图,销售上下文的概念太泛,无法得知其中应该干什么,怎么去销售,有哪些影响销售概念,我又做了拆分。如下图4。
【图4,点击图片查看大图】
到这里几个核心域内包含的概念已经出来了:价格体系(促销、会员价)、销售方式(打包、捆绑赠品)、内容管理、购买。这其中最核心的又是购买。
③这个核心域的支撑子域和通用子域是什么?
这里开始我们需要对我们整理出的各个上下文和子域结合起来,并且根据9种组织模式和集成模式表达出各上下文之间的关系。如下图5。
【图5,点击图片查看大图】
这里的U和D分别代表UpStream和DownStream,表达出上下游关系,并且图的位置也需要看出这点。另外OHS+PL = Open Host Service+Published Language,ACL = Anticorruption Layer。
发现这里的5个子域中,订单和物流子域分别承担着2个子域的下游支撑作用,那么成为了通用子域。
最终这个问题的结果为商品子域[支撑]、会员子域[支撑]、支付子域[支撑]、订单子域[通用]、物流子域[通用]。
最后再考虑到一些非必须的辅助性概念,数据分析系统和预测系统,形成我们的最终上下文映射图。
【图6,点击图片查看大图】
六、结语
以上的每个环节都应该尽可能有领域专家参与,这样才能得出最符合实际的上下文映射图。DDD之路不好走,并且从短期的表面来看,需要花费的时间和精力会比我们常规的数据驱动开发方式看上去多的多。但是从沟通的便捷性、理解的错误率、代码的可维护性来看,DDD能够让对项目的把控更上一个层级。并且为了应对互联网行业的快速变化,我们得到的远比我们付出的多的多。
作者:Zachary_Fan
出处:http://www.cnblogs.com/Zachary-Fan/p/5991674.html
- 如何一步一步用DDD设计一个电商网站(九)—— 小心陷入值对象持久化的坑
阅读目录 前言 场景1的思考 场景2的思考 避坑方式 实践 结语 一.前言 在上一篇中(如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成),有一行注释的代码: public interfa ...
- 如何一步一步用DDD设计一个电商网站(八)—— 会员价的集成
阅读目录 前言 建模 实现 结语 一.前言 前面几篇已经实现了一个基本的购买+售价计算的过程,这次再让售价丰满一些,增加一个会员价的概念.会员价在现在的主流电商中,是一个不大常见的模式,其带来的问题是 ...
- 如何一步一步用DDD设计一个电商网站(十)—— 一个完整的购物车
阅读目录 前言 回顾 梳理 实现 结语 一.前言 之前的文章中已经涉及到了购买商品加入购物车,购物车内购物项的金额计算等功能.本篇准备把剩下的购物车的基本概念一次处理完. 二.回顾 在动手之前我对之 ...
- 如何一步一步用DDD设计一个电商网站(七)—— 实现售价上下文
阅读目录 前言 明确业务细节 建模 实现 结语 一.前言 上一篇我们已经确立的购买上下文和销售上下文的交互方式,传送门在此:http://www.cnblogs.com/Zachary-Fan/p/D ...
- 如何一步一步用DDD设计一个电商网站(六)—— 给购物车加点料,集成售价上下文
阅读目录 前言 如何在一个项目中实现多个上下文的业务 售价上下文与购买上下文的集成 结语 一.前言 前几篇已经实现了一个最简单的购买过程,这次开始往这个过程中增加一些东西.比如促销.会员价等,在我们的 ...
- 如何一步一步用DDD设计一个电商网站(五)—— 停下脚步,重新出发
阅读目录 前言 单元测试 纠正错误,重新出发 结语 一.前言 实际编码已经写了2篇了,在这过程中非常感谢有听到观点不同的声音,借着这个契机,今天这篇就把大家提出的建议一个个的过一遍,重新整理,重新出发 ...
- 如何一步一步用DDD设计一个电商网站(四)—— 把商品卖给用户
阅读目录 前言 怎么卖 领域服务的使用 回到现实 结语 一.前言 上篇中我们讲述了“把商品卖给用户”中的商品和用户的初步设计.现在把剩余的“卖”这个动作给做了.这里提醒一下,正常情况下,我们的每一步业 ...
- 如何一步一步用DDD设计一个电商网站(三)—— 初涉核心域
一.前言 结合我们本次系列的第一篇博文中提到的上下文映射图(传送门:如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念),得知我们这个电商网站的核心域就是销售子域.因为电子商务是以信息网络 ...
- 如何一步一步用DDD设计一个电商网站(十一)—— 最后的准备
阅读目录 前言 准备 实现 结语 一.前言 最近实在太忙,上周停更了一周.按流程一步一步走到现在,到达了整个下单流程的最后一公里——结算页的处理.从整个流程来看,这里需要用户填写的信息是最多的,那么 ...
- 如何一步一步用DDD设计一个电商网站(十二)—— 提交并生成订单
阅读目录 前言 解决数据一致性的方案 回到DDD 设计 实现 结语 一.前言 之前的十一篇把用户购买商品并提交订单整个流程上的中间环节都过了一遍.现在来到了这最后一个环节,提交订单.单从业务上看,这个 ...
随机推荐
- UWP 律师查询 MVVM
APP简介 律师查询是基于聚合数据的律师查询接口做的,这个接口目前处于停用状态,但是,由于我是之前申请的,所以,还可以用,应该是无法再申请了. 效果图 开发 一.HttpHelper 既然是请求接口的 ...
- [APUE]进程控制(上)
一.进程标识 进程ID 0是调度进程,常常被称为交换进程(swapper).该进程并不执行任何磁盘上的程序--它是内核的一部分,因此也被称为系统进程.进程ID 1是init进程,在自举(bootstr ...
- RecyclerView使用大全
RecylerView介绍 RecylerView是support-v7包中的新组件,是一个强大的滑动组件,与经典的ListView相比,同样拥有item回收复用的功能,这一点从它的名字recyler ...
- B样条基函数的定义和性质
定义:令U={u0,u1,…,um}是一个单调不减的实数序列,即ui≤ui+1,i=0,1,…,m-1.其中,ui称为节点,U称为节点矢量,用Ni,p(u)表示第i个p次(p+1阶)B样条基函数,其定 ...
- ASP.NET WebApi OWIN 实现 OAuth 2.0
OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用. OAuth 允许用户提供一个令牌, ...
- 【NLP】Python NLTK处理原始文本
Python NLTK 处理原始文本 作者:白宁超 2016年11月8日22:45:44 摘要:NLTK是由宾夕法尼亚大学计算机和信息科学使用python语言实现的一种自然语言工具包,其收集的大量公开 ...
- 2013 Asia Changsha Regional Contest---Josephina and RPG(DP)
题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=4800 Problem Description A role-playing game (RPG and ...
- JavaScript对象和数组
1.JavaScript中有两个非常重要的数据类型是对象和数组. 通过"."或者"[]"来访问对象属性 举例:var book = { topic:" ...
- 原生JS实现-星级评分系统
今天我又写了个很酷的实例:星级评分系统(可自定义星星个数.显示信息) sufuStar.star();使用默认值5个星星,默认信息 var msg = [........]; sufuStar.sta ...
- iOS之解决崩溃Collection <__NSArrayM: 0xb550c30> was mutated while being enumerated.
崩溃提示:Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <CAL ...