领域驱动设计 DDD

一、序言

领域驱动设计是一种解决业务复杂性的设计思想,不是一种标准规则的解决方法。在领域驱动设计理念上,各路大侠的观点也是各有不同,能力有限、欢迎留言讨论。

二、领域驱动设计

DDD是什么

wiki释义:

领域驱动设计(英语:Domain-driven design,缩写 DDD)是一种通过将实现连接到持续进化的模型[1]来满足复杂需求的软件开发方法。领域驱动设计的前提是:

  • 把项目的主要重点放在核心领域(core domain)和域逻辑
  • 把复杂的设计放在有界域(bounded context)的模型上
  • 发起一个创造性的合作之间的技术和域界专家以迭代地完善的概念模式,解决特定领域的问题

领域驱动设计是一种由域模型来驱动着系统设计的思想,不是通过存储数据词典(DB表字段、ES Mapper字段等等)来驱动系统设计。领域模型是对业务模型的抽象,DDD是把业务模型翻译成系统架构设计的一种方式。

领域驱动设计(简称 ddd)概念来源于2004年著名建模专家eric evans发表的他最具影响力的书籍:《domain-driven design –tackling complexity in the heart of software》(中文译名:领域驱动设计—软件核心复杂性应对之道)一书。,书中提出了“领域驱动设计(简称 ddd)”的概念。

         领域驱动设计一般分为两个阶段

        1.   以一种领域专家、设计人员、开发人员都能理解的“通用语言”作为相互交流的工具,在不断交流的过程中发现和挖出一些主要的领域概念,然后将这些概念设计成一个领域模型;

         2.   由领域模型驱动软件设计,用代码来表现该领域模型。领域需求的最初细节,在功能层面通过领域专家的讨论得出。

       领域驱动设计告诉我们,在通过软件实现一个业务系统时,建立一个领域模型是非常重要和必要的,因为领域模型具有以下特点

     1.   领域模型是对具有某个边界的领域的一个抽象,反映了领域内用户业务需求的本质;领域模型是有边界的,只反应了我们在领域内所关注的部分;

     2.   领域模型只反映业务,和任何技术实现无关;领域模型不仅能反映领域中的一些实体概念,如货物,书本,应聘记录,地址,等;还能反映领域中的一些过程概念,如资金转账,等;

     3.   领域模型确保了我们的软件的业务逻辑都在一个模型中,都在一个地方;这样对提高软件的可维护性,业务可理解性以及可重用性方面都有很好的帮助;

     4.   领域模型能够帮助开发人员相对平滑地将领域知识转化为软件构造;

     5.  领域模型贯穿软件分析、设计,以及开发的整个过程;领域专家、设计人员、开发人员通过领域模型进行交流,彼此共享知识与信息;因为大家面向的都是同一个模型,所以可以防止需求走样,可以让软件设计开发人员做出来的软件真正满足需求;

     6.  要建立正确的领域模型并不简单,需要领域专家、设计、开发人员积极沟通共同努力,然后才能使大家对领域的认识不断深入,从而不断细化和完善领域模型;

     7.  为了让领域模型看的见,我们需要用一些方法来表示它;图是表达领域模型最常用的方式,但不是唯一的表达方式,代码或文字描述也能表达领域模型;

     8.  领域模型是整个软件的核心,是软件中最有价值和最具竞争力的部分;设计足够精良且符合业务需求的领域模型能够更快速的响应需求变化;

术概念

DDD中的模型

Model与传统的POJO(DTO、DO、DAO)类等对比,都是一个类中有属性、属性有Get/Set方法,并且做传输对象。

Model与传统MVC三层架构层的业务逻辑层中的Service对比,都是处理业务行为(Action)层。

模型(Model)承载着业务的属性和具体的行为,是业务表达的方式、是DDD的内核。是一个类中有属性、属性有Get/Set方法,并且业务的行为(Action)操作也是在模型类中(充血模型)即做业务逻辑处理,又做数据传输对象,模型分为Entity、Value Object、Service这三种类型。

  1. Entity (实体)
    1. 有特定的标识,标识着这个Model在系统中全局唯一
    2. 内部值可以是变化的,可能存在生命周期 (比如订单对象,状态值是连续变化的)
    3. 有状态的Value Object
  2. Value Object (值对象)
    1. 内部值是不变的,不存在生命周期 (比如地址对象不存在生命周期)
    2. 无状态对象
  3. Service (服务)
    1. 无状态对象
    2. 当一个属性或行为放在Entity、Value Object中模棱两可或不合适的时候就需要以Service的形式来呈现

三种模型的复杂度是不一样的,在领域建模选Model模棱两可时,优先选择简单模型原则。模型复杂度顺序 Service > Entity > ValueObject

DDD模型的生命周期

  1. Factory (工厂)

用来创建Model,以及帮助Repository (数据源)注入到Model中

  1. Aggreagte (聚合根)

封装Model,一个Mode中l可能包含其他Model(类似一个对象中包含其他对象的引用,实际概念更为复杂些)

  1. Repository (数据源)

数据源的访问网关层、通过Repository来对接不同的数据源

DDD模型的边界

  1. 限界上下文,领域边界上下文
    1. 域的拆分
      1. 按业务抽象进行划分
      2. 一个业务拆分成几个独立的域,每个域又可细拆成不同子域
  2. 防腐
    1. 一个域在访问其他域的模型时,把获取到的模型做层转换映射到自己域的模型中(不直接使用别的域模型作为自己域模型中的一部分)
      1. 防止源域模型发生变更,依赖源域模型的调用方,在需要源域模型新功能时,必须要全局依赖修改,才在能兼容
      2. 防止域上下文不一致产生的冲突

其他

一个团队,一种语言

一条业务线由研发、产品、业务共同协作和维护,大家只是在不同维度做同一件事情。领域驱动设计的实现方式不仅仅是代码,也可以是PRD、MRD、业务模型图,选择一种大家都能看懂的方式(领域模型术语),统一团队语言,从上层业务到底层实现都是同一个领域模型,减少信息传递经过翻译造成理解不一致。

DDD设计的特点

根据业务模型设计系统

不是通过数据库等数据源驱动设计,是根据业务语义抽象梳理设计成领域模型

数据模型统一

通过真实业务背景,梳理出业务域模型自然会形成出参、入参、中间临时属性收口统一为域模型

业务模型与数据源无关

  1. 数据源数据结构无论怎么变、数据源无论怎么换,领域模型统一无感知,无须变更。
  2. 一个域模型底层对应的数据源可以是1个或n个不同类型数据源
  3. 系统升级底层数据源结构改造时,变更对业务层是透明,域模型可无缝对接,可达到开着飞机换引擎的效果

业务属性字段命名统一、引用唯一

在现在MVC模式开发中,入参model、数据传输model、数据源model 同一个业务属性含义可能有多种不同的命名,引用情况很难直接排除,当丰富某个业务字段值时,很难直接判断对原有业务的影响范围

业务行为Action收口

在原有开发模式下,一个Model类是一个POJO、DTO、DO,仅做数据传输,没有任何业务相关Action,属于典型的贫血模型。在DDD中一个Model就表述一个业务的域(可能是子域),这个Model不仅有属性,还有业务行为Action,并且这个域的所有操作都在这个Model中,这个Model不仅是数据传输的作用也是一个具体的Service,是属于充血模型。开发人员可以通过关注这个域模型就可以cover负责领域的全部,更不会出现大量的复制-粘贴重复代码。

业务操作高内聚、低耦合

所有这个域的操作都内聚在这个Model中,不会存在同一个相同业务行为在多个Service中存在现象。很多时候一个业务行为功能变更,在原有开发模式下需要把所有的service中有这个业务行为的地方都要变更(粘贴复制代码更为严重)

系统更能直观体现业务逻辑

产品是一直在演进的,PRD、技术方案都很难准确的表明现在产品的真实逻辑,很多时候大家都会遇见这种现象,产品经理不确定当前某个业务点的准确逻辑,需要开发阅读代码翻译给产品经理业务逻辑。

铁打的代码、流水的产研,产品经理、开发流动性都很大,新人仅看文档cover整个系统,很难做到。每个业务准确的细节点,还是要看系统代码实际的逻辑规则。

领域驱动架构

领域驱动设计没有特定的架构风格,它的核心是域模型驱动业务的思想,常见的领域驱动设计架构有经典的三层架构、REST架构、事件驱动架构、CQRS架构、六边形架构等。

领域驱动三层架构

API层

API层是作为对外打包、前端接口调用使用。Domian层是整个域模型,不能直接把它打包成maven给别人使用,也不能直接把它作为接口给前端使用,有些需要API层作为进行转换后调用Domain,对调用Domain返回的数据进行包装筛选后再返回出去。

Domain层

系统的核心层,所有具体的业务逻辑处理、事件处理等都在这层域模型中处理

Repository 层

数据源代理层,Repository 层类似一个网关代理,它本身没有数据,数据都是通过它的代理来被Domain层访问,被代理的数据源不仅仅可以是DB、ES还可以是HTTP、RPC任何与Domain层进行数据交互的都叫Repository

领域驱动设计优势和劣势

DDD不是银弹,它只是复杂性业务的一种解决方式。DDD解决了系统设计的‘复杂性’,DDD设计思想本身又存在复杂性。

优势

系统演进更方便

随着业务的变化、系统设计也要演进升级。好的架构设计一定演化来的,不是一开始就设计出来的,但系统演进过程中的成本,一定是最开始的设计决定的。一个健康公司的成长,业务横向、纵向会发展的会越来越复杂,支持业务的系统也一定会越来越复杂。在领域驱动设计中,域模型对应的是业务模型,是系统架构的内核,通过域模型来驱动与外界的交互。

业务复杂性变化的演进

域模型可能是简单新增属性或action就能支撑整体的业务发展。企业订餐的业务系统要同时有用户端、运营端、企业端、商户端的数据展示和操作,当业务演进出一个新功能时,这四端系统可能都要同时改造支撑。在领域驱动中,系统的域模型是同一套,只需在领域层进行改造,即可同时支撑四端。

业务数据量变化的演进

公司业务数据量的变化后,现有的架构往往很难支持业务的发展,一定会进行新的技术选型支持业务。在DDD中,域模型为内核,在内核外的一层是代理层,通过这层代理来抽象透明化掉业务模型对系统底层设计的感知。比如原本数据量很小,一个简单的搜索直接使用MySQL like 模糊查询即可满足,在数据量巨大这种方式无法满足的时候,需要使用ES这种专业的搜索技术来实现,这时候仅需要在数据源层把原本指向数据源MySQL改成ES即可,业务代码全程透明无感知,可以达到给正在飞行的飞机换引擎的效果。

更方便测试

对于测试(包含开发自测)来说,流程跑不通是痛苦的,由于IO造成阻塞而非系统逻辑是更痛苦。测试的时候最喜欢的纯函数测试,不依赖任何IO(不包含机器内存层面),DDD设计思想是天然的在代码上把纯函数和普通函数区分开,Repository层是非纯函数,在Repository层Mock掉,整体系统就成了纯函数系统,对测试在Mock数据、切换数据源是非常方便和友好的。

劣势

系统改造成DDD复杂

我们常用的架构基本都是MVC三层架构方式,在常用的MVC三层架构中基本所有的业务逻辑都在service层中,并且是按service功能属性设计的Service层,现在要进行DDD思想开发,需要打破原有的设计,有些严重的还必需要进行重构设计。

团队开发熟悉DDD思想困难

改变自己比较困难,对别人产生影响更加困难。一个开发团队如果之前对DDD都没有了解,要推进和对团队产生影响是一个艰难的过程。

最后

DDD不仅是统一语言、以业务驱动系统设计,在熟悉新业务和系统重构时,领域驱动设计思想更能很好快速梳理业务。如下图领域驱动设计是以领域(业务模型)为核心,通过数据代理层(Repository)来与其他系统交互,来驱动整个系统架构设计。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值