危险的DDD聚合根
原文:危险的DDD聚合根
DDD的核心是聚合。这没有问题,大家都认同。但关于DDD中的聚合方式,其实我还是有些担心,下面说说我的想法,希望大家参与讨论。
其实当初第一次看到DDD中关于聚合根部分论述的时候,就感觉有些僵化。DDD中的聚合根的分析设计思路大致是这样:1、业务本质逻辑分析;2、确认聚合对象间的组成关系;3、所有的读写必须沿着这些固有的路径进行。
这是一种静态聚合的设计思路。理论上讲,似乎没有什么问题。但实际上,人对第一步中的业务逻辑分析就是一个渐进的过程,不是稳定不变的。不是谁都可以成为业务领域专家,就算是业务领域专家也不一定都是对的。在我看来,从时间维度和多用户场景下看,这种静态的聚合分析设计方法是根本无法保证领域模型的稳定性。
也许有人不理解,那可以打个比喻:过去几个孩子可以和爸爸妈妈高高兴兴地一家人生活在一起,但是孩子们长大后是必然要分家的。其实我只是在强调,人们对业务过程的认识是有局限性的,谁也无法避免。
DDD本来就是处理复杂业务逻辑设计问题。我看到大家用DDD去分析一些小项目的时候,往往为谁是聚合根而无法达成共识。这说明每个人对业务认识的角度、深度和广度都不同,自然得出的聚合根也不同。试想,这样的情况下,领域模型怎么保持稳定。
更现实的解决方式是怎么在动态过程中尽可能地保证业务领域模型的稳定性。在我看来:对象之间是平等的,没有谁高人一等(也就是没有聚合根);场景(业务)是聚合对象行为的唯一理由;复杂的场景是由简单场景聚合而成。不管业务如何变化,总有子场景是不变的,这样就能获得最大的“维护利润”(业务不变性)。
作为企业软件开发而言,最大的挑战就是业务变化。这一方面来源于业务本身的变化(应用系统应用不断深入和推广),另一方面是我们对业务认识不断深入的过程。不能适应变化的系统只有死路一条。复杂的业务要求软件架构必须具备很强的适应能力。如前面所说,这是一个渐进的过程。DDD本身就是为了解决复杂业务的软件开发问题的。“如何避免颠覆式修改”是最大的挑战。如果发现找的聚合根是错误的,那领域模型还可重用的价值还有多大,这种代价和成本是否能够承受?
核心观点:
- 每个人对业务认识的角度、深度和广度都不同,得出的聚合根也就会不同;这才有了很多时候我们无法对谁是聚合根以及聚合根的边界大小达成共识;
- 我们对业务的认识是一个不断深入的过程,在这个过程中我们的模型也会相应调整;
- 从DDD的最后落地实现角度来看,最终出来的是一个个聚合。但是因为聚合不仅仅只是一个根实体,而是还内聚了一堆子实体和值对象。那它的这种内聚结构对于后期因各种原因而发现原来的聚合是错误的时候,此时模型重构的成本和代价会相对于“一个所有Entity对象都地位平等的模型”的重构成本会更大,特别是在引入了Event Sourcing时问题更加凸显,这点也可见我上篇发表的帖子;
危险的DDD聚合根的更多相关文章
- ddd 聚合根 之 聚合与不聚合 设计
聚合 不聚合 订单和订单明细 论坛主贴与贴子回复 订单和收货地址(vo)
- DDD:使用EntityFramework的话,如果只为聚合根设计仓储,其它实体如何处理?
背景 DDD中只有聚合根可以有仓储,仓储负责整个聚合持久化的相关生命周期,在不使用工作单元或POCO的情况下,我们可以让Order内部直接调用DAL操作OrderItem.我们也可以让Order跟踪所 ...
- 从壹开始微服务 [ DDD ] 之六 ║聚合 与 聚合根 (下)
前言 哈喽大家周二好,上次咱们说到了实体与值对象的简单知识,相信大家也是稍微有些了解,其实实体咱们平时用的很多了,基本可以和数据库表进行联系,只不过值对象可能不是很熟悉,值对象简单来说就是在DDD领域 ...
- DDD之4聚合和聚合根
聚合就是归类的意思,把同类事物统一处理: 聚合根也就是最抽象,最普遍的特性: 背景 领域建模的过程回顾: 那么问题来了? 为什么要在限界上下文和实体之间增加聚合和聚合根的概念,即作用是什么? 如何设计 ...
- DDD的实体、值对象、聚合根的基类和接口:设计与实现
1 前置阅读 在阅读本文章之前,你可以先阅读: 什么是DDD 2 实现值对象 值对象有两个主要特征:它们没有任何标识.它们是不可变的. 我们举个例子:小明是"浙江宁波"人,小红也是 ...
- DDD中聚合、聚合根的含义以及作用
聚合与聚合根的含义 聚合: 聚合往往是一些实体为了某项业务而聚类在一起形成的集合 , 举个例子, 社会是由一个个的个体组成的,象征着我们每一个人.随着社会的发展,慢慢出现了社团.机构.部门等组织,我们 ...
- 关于ABP聚合根类AggregateRoot的思考
AggregateRoot和Entity的区别 AggregateRoot继承于Entity,并实现了IGeneratesDomainEvents接口 public class AggregateRo ...
- ASP.NET Core Web API下事件驱动型架构的实现(四):CQRS架构中聚合与聚合根的实现
在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件总线的实现.接下来对于事件驱动型架构的讨论,就需 ...
- NET Core Web API下事件驱动型架构CQRS架构中聚合与聚合根的实现
NET Core Web API下事件驱动型架构在前面两篇文章中,我详细介绍了基本事件系统的实现,包括事件派发和订阅.通过事件处理器执行上下文来解决对象生命周期问题,以及一个基于RabbitMQ的事件 ...
随机推荐
- 如何在一个div中使其子div居中
网上其他地方已讲述过对其的不同实现方式,今天主要做一个详细的汇总,希望对大家有帮助. ps:我面试的时候就被问到过这个问题,当时都回答错了,蓝瘦. 假设父div的类名为father,子div的类名为s ...
- WPF入门(四)->线形区域Path内容填充之渐变色(LinearGradientBrush)
原文:WPF入门(四)->线形区域Path内容填充之渐变色(LinearGradientBrush) 前面我们介绍到,Path对象表示一个用直线或者曲线连接的图形,我们可以使用Path.Data ...
- kindeditor 4 指定生成文件的时间日期/动态获取My97的时间
最近后台要求要指定上传附件的时间日期,编辑器是kindeditor,仔细研究后发现可以借助clickToolbar事件,传递时间到后台,但是中途发现传递的时间总是初始化my97的时间,改变后的时间只有 ...
- dom4j解析xml获取所有的子节点并放入map中
dom4j递归解析所有子节点 //解析返回的xml字符串,生成document对象 Document document = DocumentHelper.parseText(resultXml); / ...
- PHP设计模式——迭代模式
声明:这一系列的博客引用<大话设计模式>.程洁作者. 迭代器模式:迭代器模式是遍历集合的成熟模式.迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,它的工作时遍历并选择序列中的对 ...
- gridview分页
protected void lnkbtnFrist_Click(object sender, EventArgs e) { //首页 ; this.ReadData(); } protected v ...
- MyEclipse各种版本号注冊码
一:MyEclipse_6.0.1GA_E3.3.1_FullStackInstaller注冊码 Subscriber:javp Subscription Code:wLR7ZL-655551-685 ...
- WPF 实现繁花曲线
原文:WPF 实现繁花曲线 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/nihang1234/article/details/83346919 X ...
- 各种工具的使用 tricks
1. 搜狗搜索引擎 因为搜狗与腾讯的合作关系,搜狗搜索引擎提供了"微信"的搜索选项,可直接定位到相关文章,或者公众号. weixin.sougou.com 2. PyCharm P ...
- NYOJ 24 素数的距离问题
素数的距离问题 时间限制:3000 ms | 内存限制:65535 KB 难度:2 描写叙述 如今给出你一些数.要求你写出一个程序,输出这些整数相邻近期的素数,并输出其相距长度.假设左右有等距离长 ...