背景

几年前我总结过DDD战术设计的一些落地经验可落地的DDD(5)-战术设计,和一次关于聚合根的激烈讨论最近两年有些新的落地体验,回过头来发现,当初对这些概念的理解还是没有深入,这篇文章重新阐述下。

之前理解不到位的点有

  1. 战术设计的各个模块是的协作关系
  2. 哪些是问题空间问题,哪些是方案空间问题边界没有划分清楚。
  3. 实体和聚合根的区别理解不不深刻,实体和聚合根建模的方法不对。

以上问题将会在下文解释清楚。

战术设计拆解

DDD的战术设计即设计某个子域的领域模型以及代码落地。领域事件、领域对象、聚合根、实体、值对象、领域服务、工厂、资源库等这些概念都属于这个范畴。

笔者将这些概念重新分层组装了下,如下图所示。



首先将整体分成两部分,问题空间和方案空间。

  1. 问题空间即领域建模。是对业务问题的描述,以及我们如何对这些问题进行抽象。这些是需要在业务、产品、开发都必须达成一致的,与具体的技术方案无关。
  2. 方案空间即如何用技术手段来解决问题,与具体技术的实现有关。

问题空间即领域建模,是通过实体、值对象、领域服务、领域事件来表达。

  1. 实体和值对象是模型对象,实体是重中之重,包括核心模型数据、行为、状态。之所以要区分实体和值对象,是为了降低复杂度,因为值对象是个常数对象,不需要花太多精力。

    注意某个对象在某个领域内是个值对象,在另外的领域可能是个实体,所以脱离领域上下文,说某个对象是值对象,肯定是不对的,比如大家常说的地址是个值对象,这一定是对的吗?

  2. 领域事件即实体产生的事件

  3. 领域服务包括一些逻辑的计算,和业务策略。比如商业决策逻辑、业务流程等。

方案空间即如何解决问题,实现领域模型与代码的映射。实现设计与实现的一致性。主要通过工厂,聚合,资源库来表达。

  1. 聚合是对实体、值对象的封装。领域外部对领域对象所有访问都基于聚合来。如基础设施层操作聚合进行数据保存。其他领域引用聚合对象数据。

    聚合的设计一般是围绕着技术来的,比如聚合对象事务性。

  2. 工厂,复杂对象的创建工厂类

  3. 资源库,对聚合的操作。

从笔者的实践角度来说,落地DDD过程中,问题空间比方案空间更重要,收益更大。因为通常我们吐槽的某些代码写的烂,贫血模型。背后并不是因为没有用DDD,而是问题空间没有定义好,对于业务没有深刻理解,导致模型抽象不足。

如何建模

为什么要建模

通常在某个子域落地DDD,我们会按照业务分析-》用例分析-》领域建模(问题空间) -》技术落地(方案空间)这些步骤来操作。但其实即使我们不在代码里落地DDD,只用前面3步维护一个子域内的领域模型也同样能够带来很多收益,包括但不限于

  1. 统一业务组各个角色的认知,业务、产品、开发大家对同一概念的认知是一致的。
  2. 指导开发工作的拆分。

比如在淘宝有个血的教训,至今这个历史债还无法被修复。早期在淘宝开店。一个卖家只能开一个店。卖家和店铺是两个领域对象,关系是1:1。店铺服务觉得是1:1的关系,对外提供的服务有根据sellerId获取店铺信息,所以其他调用方就无意识的直接引用了卖家id,这样也可以拿到店铺。导致shopId被等同于了sellerID。这个误引用发生在成千上万个地方,最后导致后续需要支持一个卖家开多店铺时无法支持。只能通过其他trick方式实现。

以之前介绍过的CRM领域 来讲解。

省略业务分析,直接拿到用例。

用例分析



按用户角色罗列所有的用例,用来推导模型、以及模型之间的关系。领域模型建立好了,需要根据列出的用例来走查一遍,要确保所有的用例都能走通。完整的用例集才能推导出正确的模型,所以当有变化时,首先调整用例集,再来修改领域模型

建模

领域建模就是定义模型对象,以及模型对象之间的关联关系。分两步建模,第一步通过名词找模型对象。第二步通过动词、形容词分析对象关联关系

名词

通常反复出现的主语和宾语中的名词就是模型对象,比如市场人员创建一个活动,活动就是一个模型对象。当然定语中出现的名词也可能是模型对象。

1.名词的定义一定要清晰。比如说crm领域有通用的名词叫商机。但是你对口的产品经理不熟悉crm领域,新造了一个词,那你要及早纠正他。

2.名词的含义在限界上下文内语义唯一,在不同的上下文中概念就不一定一样了。比如市场人员创建的活动,和做营销时创建的活动就不一定。

动词、活动

1个市场人员可以创建多个活动,所以市场人员和活动关联关系是1对多。两者独立存在,普通关联关系。

这里为了简化描述,只列了市场活动、线索、客户、商机这些域。用户、角色、权限、数据分析这些域先忽略了。

产出物

在推导的过程中,我们是按照自底向上的方式推导的,最后我们呈现出来的结果是按照如下方式

  1. 领域名词

    市场活动: 市场人员为了展示公司形象、推广公司产品,获取线索而举办的活动。一个活动中可以创建多个线索。

线索: 销售人员基于线索发掘潜在客户,多个线索转换为一个客户。线索可以由一个市场活动生成,或者其他渠道。

客户:有意向购买公司产品的用户,销售人员可以通过跟进客户,转化销售机会。

销售机会:更高质量的线索,有机会签单。可以通过客户转换得到,也可以通过其他渠道来获取

  1. 领域模型

    如上图

  2. 主要领域状态转换。

    因为复杂的领域对象生命周期以及一些跨领域对象交互情况在领域模型图中表达不出来,所以需要借助额外的图来表达。

可落地的DDD(7)-战术设计上的一些误区的更多相关文章

  1. 可落地的DDD(5)-战术设计

    摘要 本篇是DDD的战术篇,也就是关于领域事件.领域对象.聚合根.实体.值对象的讨论.也是DDD系列的完结篇. 这一部分在我们团队争论最多的,也有很多月经贴,比如对资源库的操作应该放在领域服务还是领域 ...

  2. 战术设计DDD

    可落地的DDD(5)-战术设计   摘要 本篇是DDD的战术篇,也就是关于领域事件.领域对象.聚合根.实体.值对象的讨论.也是DDD系列的完结篇.这一部分在我们团队争论最多的,也有很多月经贴,比如对资 ...

  3. DDD领域驱动设计落地实践(十分钟看完,半小时落地)

    一.引子 不知今年吹了什么风,忽然DDD领域驱动设计进入大家视野.该思想源于2003年 Eric Evans编写的"Domain-Driven Design领域驱动设计"简称DDD ...

  4. DDD(领域驱动设计)--战术设计

    前言 战术设计 战略设计为我们提供一种高层视野来审视我们的软件系统,主要包括领域/子域.通用语言.限界上下文和架构风格等概念, 而战术设计则将战略设计进行具体化和细节化,它主要关注的是技术层面的实施, ...

  5. C#进阶系列——DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  6. DDD领域驱动设计初探(二):仓储Repository(上)

    前言:上篇介绍了DDD设计Demo里面的聚合划分以及实体和聚合根的设计,这章继续来说说DDD里面最具争议的话题之一的仓储Repository,为什么Repository会有这么大的争议,博主认为主要原 ...

  7. 可落地的DDD(4)-如何利用DDD进行微服务的划分(2)

    摘要 在前面一篇介绍了如何通过DDD的思想,来调整单体服务内的工程结构,为微服务的拆分做准备.同时介绍了我们在进行微服务拆分的时候踩过的一些坑. 这篇介绍下我们最终的方案,不一定对,欢迎留言讨论. 微 ...

  8. 浅谈我对DDD领域驱动设计的理解

    从遇到问题开始 当人们要做一个软件系统时,一般总是因为遇到了什么问题,然后希望通过一个软件系统来解决. 比如,我是一家企业,然后我觉得我现在线下销售自己的产品还不够,我希望能够在线上也能销售自己的产品 ...

  9. DDD 领域驱动设计-商品建模之路

    最近在做电商业务中,有关商品业务改版的一些东西,后端的架构设计采用现在很流行的微服务,有关微服务的简单概念: 微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成.系统中的各个微服务可被独 ...

随机推荐

  1. 《手把手教你》系列基础篇(九十六)-java+ selenium自动化测试-框架之设计篇-跨浏览器(详解教程)

    1.简介 从这一篇开始介绍和分享Java+Selenium+POM的简单自动化测试框架设计.第一个设计点,就是支持跨浏览器测试. 宏哥自己认为的支持跨浏览器测试就是:同一个测试用例,支持用不同浏览器去 ...

  2. Java语言的词法分析器的Java实现

    一.实验目的 1. 学会针对DFA转换图实现相应的高级语言源程序. 2. 深刻领会状态转换图的含义,逐步理解有限自动机. 3. 掌握手工生成词法分析器的方法,了解词法分析器的内部工作原理. 二.实验内 ...

  3. Spring Ioc源码分析系列--前言

    Spring Ioc源码分析系列--前言 为什么要写这个系列文章 首先这是我个人很久之前的一个计划,拖了很久没有实施,现在算是填坑了.其次,作为一个Java开发者,Spring是绕不开的课题.在Spr ...

  4. TCP 协议有哪些缺陷?

    作者:小林coding 图解计算机基础网站:https://xiaolincoding.com 大家好,我是小林. 忽然思考一个问题,TCP 通过序列号.确认应答.超时重传.流量控制.拥塞控制等方式实 ...

  5. Next.js 在 Serverless 中从踩坑到破茧重生

    作者 杨苏博,偏后端的全栈开发,目前负责腾云扣钉的 Cloud Studio 产品.在团队中负责接技术架构设计与 Review.Cloud Studio 编辑器内核设计与开发.部分核心插件设计与开发: ...

  6. 【多线程】线程创建方式三:实现callable接口

    线程创建方式三:实现callable接口 代码示例: import org.apache.commons.io.FileUtils; import java.io.File; import java. ...

  7. Object类和对象类型转换

    学习内容:Object类和对象类型转换 一.Object类 1.Object类是所有类的父类,是Java类层中最高层的类. 2.getClass()方法:返回对象执行时的Class实例,然后用此实例调 ...

  8. 直观比较 popcount 的效率差异

    问题 求 \(\sum\limits_{i=1}^{3\times 10^8} popcount(i)\) . 仅考虑在暴力做法下的效率. 枚举位 __builtin_popcount #includ ...

  9. 【可视化分析案例】用python分析B站Top100排行榜数据

    一.数据源 之前,我分享过一期爬虫,用python爬取Top100排行榜: 最终数据结果,是这样的: 在此数据基础上,做python可视化分析. 二.数据读取 首先,读取数据源: # 读取csv数据 ...

  10. 【转】理解 CI 和 CD 之间的区别

    有很多关于持续集成(CI)和持续交付(CD)的资料.很多文章用技术术语来进行解释,以及它们怎么帮助你的组织.可惜的是,在一些情况下,这些方法通常与特定工具.甚至供应商相关联.在公司食堂里非常常见的谈话 ...