打开 DDD 相关的书籍,你会被一系列生硬、高深的概念充斥,拜读完毕,满头雾水。这不是你的问题,而是 DDD 自己的问题,表现形式太概念化。学习它的内核,就不要被它给出的概念所迷惑,而要去思索这些概念背后所蕴含的设计原则,多问一些为何,本质无外乎是 SOLID。最重要的,要学会运用设计原则去解决问题,而非所谓的 “设计规范”。web
本文将会以系列解答的方式展开,由浅入深,篇幅不长,无妨一看。缓存
本质上是一种方法论,提供了一套系统开发的设计方法。面对须要解决的问题,从复杂的现实中抽象出业务模型的思惟方式与实践技巧。初衷是清晰设计思路,规范设计过程。架构
DDD 强调是说得先把 “领域” 中涉及到的数据、流程、规则等都弄明白了,而后以面向对象的观点为其创建一个模型(即领域模型),而这个模型,决定了你将用什么技术、什么架构、什么平台来实现这个系统。因此技术在这个过程当中是 “被动的”,是被 “选来” 实现 “领域模型” 的。对于项目的成败,技术不是决定性因素,领域模型是否符合事物的本质才是关键。app
能够看出,领域驱动设计的出发点是业务导向,技术服务于业务。运维
学习 DDD 有一些常见的误区。第一个要避免的就是,你必需要清楚,DDD 对于工程领域没有提出多么创新的技法,它更可能是把前人在生产系统中用惯的技法概括,总结,包装了一下 ——dom
你可能认为 DDD 是一把 “瑞士军刀”,可以解决全部的设计问题,而实际上 “DDD 只是一把锤子”,有个谚语叫作 “若是你手里有一把锤子,那么全部的问题都变成了钉子”,若是你拿着 DDD 这把锤子处处去敲,要么东西被敲坏,要么就不起做用。curl
为何说 DDD 只是一把锤子呢?做者明确指出,DDD 只适合业务复杂度很大的场景,不适用于技术复杂性很大但业务领域复杂性很低的场景。能够看出,DDD 只专一一个领域,高复杂业务 —— 经过它能够为你的系统创建一个核心、稳定的领域模型,灵活、可扩展的架构。分布式
DDD 是拥抱复杂的,拥抱变化的,但自己也是有成本有前提的;简单系统,不必搞这么复杂,强上 DDD 就是一种反模式了。因此,当你遇到一个问题就想到 DDD 的时候,必定要注意 “DDD 只是一把锤子”,不要拿着这把锤子处处去敲!工具
如何判断业务是否复杂,判断依据不胜繁数。在我看来,没那么复杂,就两个:单元测试
只要知足其中一个,我认为就是复杂的。
DDD 并不是 “银弹”,天然也不是解决全部疑难杂症的 “灵丹妙药”。在我看来,它只解决一个问题:过分耦合。
业务初期,系统功能大都很是简单,CRUD 就能知足,此时的系统是清晰的。随着业务的不断演化,系统的频繁迭代,代码逻辑变得愈来愈复杂,系统也愈来愈冗余。模块彼此关联,谁都很难说清模块的具体功能意图是啥;修改一个功能,每每光回溯该功能须要的修改点就须要很长时间,更别提修改带来的不可预知的影响面。
归根到底在于系统架构不清晰,划分出来的模块内聚度低、高耦合,致使代码很差复用、很差扩展、很差运维。
第一种解决方案:按照演进式设计的理论,让系统的设计随着业务的演进而增加。这固然是可行的,工程实践中的重构、持续集成就能够对付各类混乱问题。问题在于,只是没有章法的、小范围的代码重构,很难具有通用型,容易变成了重构者的自娱自乐,代码继续腐败,从新重构…… 无休止的循环
第二种解决方案:从新抽象,如何抽象,DDD 建议咱们左右开弓:一、分治(实体对象、值对象、聚合根);二、分层(展现、应用、领域、通用)
核心概念三句话:
核心关系一句话:
talk is cheap,show me the code
//聚合根 class Order { public String id;//订单ID,全局惟一 public Address customerAddress;//配送地址 public List<Item> items;//商品信息 public Pay pay;//支付信息 public LogisticsDetail logisticsDetail;//物流信息 public Pingjia pingjia;//评价信息 } //实体 class Item { public Long id; //商品ID,实体主键,Order内惟一 public String name;//商品名 public float price;//价格 public int count;//数量 } //实体 class Pay { public Long id; //支付ID,实体主键,Pay内惟一 public String source;//支付方式 public int currency;//币种 public float total;//价格 } //实体 class LogisticsDetail { public Long id; //物流ID,实体主键,LogisticsDetail内惟一 public int cpCode;//物流公司 public String mailNo;//物流单 public float status;//当前状态 } //实体 class Pingjia { public Long id; //评价ID,实体主键,Pingjia内惟一 public String desc;//描述 public byte[] image;//图片 } //值对象 class Address{ public String province;//省 public String city;//市 public String county;//区 }
能够看到,经过业务限界,DDD 将大型复杂的 DO 分解为若干简单的 DO,从而保证 DO 的扩展性、灵活性。具体到 DB 层面,须要五张表:
但若是业务没有这么复杂,我依然推荐 DO 的 4 要素设计:基础要素、核心要素、扩展要素以及冗余要素。就拿 open 店面绑定的 *App 设计举例:
于此,咱们能够概括出领域模型设计的通常步骤:
这里多说一句,若是你读过金字塔原理的话,会发现思惟方式分为两种:概括性思惟和演绎性思惟。人的原始思惟方式是演绎性的,这就决定了咱们常常基于流程去思考问题,而面向对象建模偏偏须要的是概括性思惟。这也是着重须要自我训练的地方。
首先须要划分模块。模块(Module)是 DDD 中明确提到的分层前置手段,在工程实践中,较为常见的模块策略有两种:
技术职责分包:
业务职责分包:(就拿开放平台 open 举例)
复杂系统建议采用技术分包策略,提升模块代码的复用性。
而后咱们再来说模块内的分层,这里特指 core 模块的内部分层。DDD 描述了几个分层概念,分别是:防腐层、服务层、资源库、领域对象、基础层。
如代码中所示,通常的工程中包的组织方式为 {com. 公司名。组织架构。业务。上下文.*},这样的组织结构可以明确的将一个上下文限定在包的内部。
这里着重释义一下防腐层,该层主要是将外部系统 DO 转义成本系统 DO,避免外部 DO 一旦发生变化,本系统改动范围过大的状况,收敛影响面。
//接口层 public class Consume { public BaseResultDTO consumeMessage(OrderDetailMessage orderDetailMessage ) throws Exception, Throwable{ OpenOrderDO openOrderDO = transferAdapter.orderDetailMessage2openOrderDO(orderDetailMessage);//调用防腐层 openOrderService.handleOrderMeasage(openOrderDO);//调用服务层 } } //防腐层 public class TransferAdapter { public OpenOrderDO orderDetailMessage2openOrderDO(OrderDetailMessage orderDetailMessage){//转换DO OpenOrderDO openOrderDO = new OpenOrderDO(); openOrderDO.setTradeId(orderDetailMessage.getTradeId()); openOrderDO.setOrderSource(orderDetailMessage.getOrderSource()); openOrderDO.setBuyerUid(orderDetailMessage.getBuyerId()); openOrderDO.setSellerUid(orderDetailMessage.getSellerId()); return openOrderDO; } }
落地就是模型到代码的转换,核心是保证模型和代码的一致性。实际状况下,因为没有好的保持模型和代码一致的办法,不少系统每每开始搞的不错,慢慢就不一致了,也就逐渐烂掉了。
落地的方式,理论上有三种:
1 不可控,2 不现实。推荐 3 的作法:架构师给出设计方案,并给出骨干实现,开发人员有了可类比的代码,就可以比较准确的去作功能开发。这比空讲要有效的多。
此外,落地中还要注意三点:
DDD 实际上是面向对象方法论的一个升华。咱们回头来看它,无外乎是经过划分领域(聚合根、实体、值对象)、领域行为封装到领域对象(充血模式)、内外交互封装到防腐层、职责封装到对应的模块和分层,从而实现了高内聚低耦合 —— 这也是它最精华的部分。
那么 DDD 的各个概念重要么?并不重要。概念掌握确实有助于提升咱们的业务思考能力,但 DDD 强调的是理论结合实践,没有通过实战考验,都是纸上谈兵。经验丰富的开发人员,即使没有据说过 DDD,其思想也每每与 DDD 相符。我更建议的是:不要受限于对概念的掌握,而要透过层层表象思考它的本质,追求设计原则与实践方法的融汇贯通。只有如此,才能针对不一样的场景灵活地运用 DDD,而非生搬硬套。毕竟,设计老是如此,即便前人已经总结了许多的方法,也不能像数学公式那样套用获得准确无误的结果。它不存在惟一的答案。
经验有限,我对 DDD 的理解不免会有不足之处,欢迎你们共同探讨,共同提升。
一、领域驱动设计精简版二、实现领域驱动设计