前言
随着业务的不断拓展,一个项目再也不是只需要运行一段时间就可以了,也不是一个很小的项目,随着业务的增长,最终开发的代码会变成一个很大的项目,此时如果不能做好很好的重构,会造成越来越难的拓展,最后导致无法拓展。所以DDD(Domain Driven Design,领域驱动设计)就是从领域业务这个方面对代码做重构与设计的方法论之一。
另外不可能将DDD用于任何一处代码,原因如下:
- 成本不允许,一般只有项目的核心领域才会考虑使用DDD重构,培养DDD团队会消耗比较长的时间,且整个时间成本是需要持续投入的。
- 因为DDD无法使用于任何一个环境,只使用DDD无法做到满足全部的业务场景,比如G端企业做数据化,很多时候是只需要从数仓查询数据;但是DDD只是一个业务思想,单纯的数据也无法做到成为业务领域
- DDD要求可以将业务分的很清晰,需要一个"聚合根"将一组业务模型组起来,这要求需要一组业务专家作为开发主力,这通常意味着需要将团队中比较聪明的几位来将项目解构成DDD的模型,很多时候这群人的工作重点不在这里。
- 就算使用DDD也不可能将其全部的教条应用在代码中,因为工作主要是为了盈利,如果使用DDD会帮助提升代码价值,全使用DDD会降低代码价值。在这种情况下,必然是只会使用一部分的DDD思想。
Model-Driven Design
好的模型可以反应真实的业务情况
-
好的模型需要统一的语言,在一个开发团队中,需要让每个开发者对同一个术语的概念是了解一致的,且需要业务专家将对应信息传达下去。
如"数据资产",数据专家和一个应届生想到的肯定是不一样的。
-
模型类与其他非DDD层的POJO需要分离开来,尤其需要和数据库的POJO分离开来,DDD是软件设计思想,与数据库、技术组件、远程调用是完全不相关的。
-
除了核心组件外,其他组件,如MVC的DAO层,因为其确实是需要做到和固定数据库做好对接,所以面向结果编程是完全值得提倡的。
-
DDD中一组很重要的思想是Entity和Value Object,Entity可以被认为是数据库中一张表的自增主键,具有连续性和唯一性,Value Object相当于是一个更关注其数据属性的一个组件,因此只能依附于一个Entity
-
Entity和Value Object只是提供了一组事物信息,如广告转化率、广告使用地区等。但是另外还需要非"事物"的部分引入到模型中,如行为,如一组模型相关的业务,如广告投放业务中,如果需要对广告相关的信息做校验,其中就包括业务校验和技术校验,如果将这两个模块混淆了,则会导致业务层被"腐化",无法实现很好的解耦与以后的拓展,因此需要将业务操作和技术操作分离出来,我们可以将基于Entity和Value Object的操作称之为"Service"。
模型中的Service有应用层面的Service、领域层面的Service(因为需要将真实业务层和技术实现层解耦,在Java的开发中可以使用依赖反转实现)、技术层的Service,应用层面的Service、领域层面的Service无法很好的区分,有时候可能不需要做区分,但是一旦涉及到业务领域核心,且不已不同应用场景为变更,则可能需要区分。
以下有一个例子:
在C端进行广告投放业务,会需要做到以下代码
javapublic class AdvertisementService { @Resource OnlineRetailersFeign onlineRetailersFeign; @Resource AdvertisementDAO advertisementDAO; @Resource AdvertisementDomainService advertisementDomainService; public Result<AdvertisementDTO> showAdvertisementInfo(AdvertisementRequestAO advertisementRequestAO) { String reginId = advertisementRequestAO.getReginId(); //查看广告校验是否有效 if (checkAdvertisementDisable(reginId)) { return new Result<AdvertisementDTO>(); } //进行本业务相关操作 /** * 1.查看投放地域、用户信息,查看用户所处环境(APP、Web等) * 2.从数据库中取出用户个人画像、当地广告服务商提供的服务 * 3.通过算法取得合适的投放信息,并且按照需要从电商微服务等服务获取信息包装 */ advertisementDomainService.domainOperate(); //最后进行写入数据库操作 advertisementDAO.insert(); //给其他微服务发送对应消息 onlineRetailersFeign.notify(); //最后返回信息 return new Result<AdvertisementDTO>(); } /** * 校验广告地址信息 * * @param reginId * @return */ private boolean checkAdvertisementDisable(String reginId) { return true; } }
其中最重要的是advertisementDomainService.domainOperate(),这个可以认为是领域相关Service,不能算作是应用Service,应用Serivce可以是advertisementDomainService.domainOperate()中的实现,通过状态模式/策略模式,实现具体逻辑。因为取得服务可能因为地区、用户不同、具体的实现逻辑是不一样的,调用的组件是不一样的,所以细节上的实现需要应用层去协调和跟技术层Service实现,但是总的来说,投放一个广告就是需要这几步,因此这一组需要作为领域层的Service。
Service是当需要Entity和Value Object做一组操作时候出现的,且Service最好是无状态的。
-
最后的Entity和Value Object应当以一组模块的形式出现。
业务域
- 设计好模型后就需要将模型组合成为领域,那么就需要一个model作为聚合根实现聚合。其中占主导作用的,和外部做主要交互的model作为聚合根,是这个领域存在的前提条件。
- 开发一个领域就是为了让隐性概念显性化,让隐形上下文显性化,配置多对象行为。
- 需要根据业务需要将领域划分为核心子域、支撑子域、通用子域。这些领域之间需要做到可以使用限界上下文、通用语言、上下文映射图做好划分。
- 限界上下文是当一个领域形成时候,需要将业务与其他业务进行物理隔离,只能通过几个固定方式进行交互,这个边界就是限界上下文
总结
- 只学习DDD并不能帮你更好的架构与设计,更好的方式应当是学习软件常见的架构设计模式、自己使用的编程语言的基本特性、然后再积极的使用
- (另外就是Eric Evans的软件驱动设计:软件核心复杂性应对之道不推荐看,因为翻译比较捉襟见肘,很多时候不知道在讲什么)
- DDD思想知易行难,期间需要花大量精力打通多个团队,是个人硬实力与软实力的体现,另外就是需要正确的构建出模型,需要可以满足实际的需要。实践DDD思想是一项风险收益都较高的事情。