10分钟理解如何进行DDD领域建模,实操设计企业级项目,从零开始划分领域和聚合

DDD领域驱动设计记账软件实战教程–划分领域和聚合

本文章为实战教程,主要内容包括:

  • 从零使用DDD构建一个记账软件

你是否有以下疑问?

  • 你是一个DDD软件开发人员,但是只是按照现有的项目去写,如果自己搭建一个新项目就无从下手了?
  • 你是一个传统软件开发人员,想使用DDD重构软件,却不知道如何入手?
  • 你看过很多DDD的文章,了解DDD概念,却没有一个从头到尾的完整教程,无法把碎片化的知识串起来?

本文章带你从头构建一个完整的DDD项目。由点到面的串联起来所有的知识。达成一个完整的知识图谱。

事件风暴

首先需要进行事件风暴。整理出来所有的命令业务流事件

  • 命令:可以简单理解为一些行为。这些命令可以映射成代码模型中的一个个方法。
  • 业务流:可以按照场景进行分析业务流,比如对于记账软件来说:记账场景、查账场景就是两个不同的场景,可能有不同的业务流。业务流可以映射成代码模型中的接口。
  • 事件:可以简单理解成上面的命令产生的一些事件。会有一些监听者异步监听这些事件并实现一些逻辑。

我们来分析一些记账场景会有哪些业务流。

首先用户需要注册登陆。因此我们可以识别出注册业务流登陆业务流。继而识别出注册命令登陆命令

对于登陆业务流中来说,除了登陆命令,还可以有查询用户、校验用户信息、生成token等命令。这些命令共同完成了一次登陆业务流。

此外,还会产生对应的事件。比如用户已注册事件已登陆事件等。

对于已注册事件,我们可以发送注册成功的消息提醒。还可以参与注册活动等。

对于已登陆事件,我们可以写入登陆成功日志,发送登陆成功消息提醒等。

登陆成功以后,首先需要创建一个账本。接下来可以更新账本信息。更新预算。设置角色信息。然后就可以添加收支记录在账本上了。

DDD领域其实是现实世界的映射。这里我们要思考现实世界的记账是怎么样的。
现实世界需要一个账本、需要记录收支信息、账本的最上方可能会写上本月的预算。记录收支信息的时候会写上分类、角色、金额等信息。

场景分析

在这里我仅仅分析了记账场景的业务流。其实还有查账场景的业务流。此外还有一些其他的通用业务流。比如监控,数据统计等。

大家可以自己分析一下将结果发到评论区。我会一一回复的。

纸上得来终觉浅,绝知此事要躬行

领域建模

当我们分析完所有的命令事件以后。我们就可以根据上面分析出来的这些进行领域建模了。通过这些我们可以分析出领域对象。

比如,登陆注册行为是围绕用户领域对象来完成的行为。写入登陆日志、登陆日志事件是围绕用户日志领域对象的行为。

此外,我们还可以从上面的分析中找到账本预算角色收支记录这几个领域对象。

如下图:

领域建模

领域对象也分三种类型

  • 聚合根:聚合的入口,操作聚合下所有的实体和值对象都需要通过聚合根来操作
  • 实体:具有唯一ID的实体,使用充血模型实现,也就是要包含属性和方法。所有对于实体的操作都应该放到这个实体中。可以映射为数据库的一个表。
  • 值对象:值对象分为简单值对象和复杂值对象。值对象和实体最大的区别在于可变性,值对象不能单独存在,只能作为实体的一部分存在。同样的,值对象不支持变更,如果需要变更则是整个值对象删除再创建一个新的。
    • 简单值对象:基本类型的属性可以视为简单值对象。
    • 复杂值对象:引用其他类(自定义的类)作为属性的值,可以视为复杂值对象。

接下来我们就可以分析出上面的领域对象哪些是实体,哪些是值对象了。

实体:

  • 用户实体:用户实体包括用户信息和用户的一些操作。这里可以将最新的一个登陆日志作为值对象放到用户实体中。而不是将所有的登陆日志都作为值对象放到用户实体中,因为那样的话太庞大了。
  • 用户登陆日志实体:存储登陆日志的信息,虽然从用户的角度来看,登陆日志不能单独存在。但是如果从数据分析的角度看,登陆日志则可以单独存在了。因此可以作为一个实体存在。
  • 账本实体:账本作为一个现实世界记账必须的物品,自然也要作为一个实体存在,有账本ID保证唯一性。并且可以修改账本的部分信息。
  • 收支记录实体:每一个收支记录都是账本上的一条信息,都是可以独立修改的个体。比如修改某一个记录的金额等。所以收支记录也作为一个实体存在。

值对象:

  • 预算值对象:预算信息是依赖于账本的一个信息,修改预算就是修改预算这个整体。因此预算作为账本的一个值对象存在。
  • 角色值对象:多个人记账,每个人记账的时候的角色都是不一样的,修改角色也是修改角色这个整体,角色离开账本以后就没有存在的意义了,因此角色也作为值对象存在。

聚合建模

经过上面的步骤,我们已经找到了我们需要的实体值对象。接下来我们可以根据他们之间的关系,来划分成一个个聚合了。

如果把实体理解成一个个的人,那么聚合就相当于部落家族等等。相近的一些人聚集在一起,就变成了一个聚合。

变成聚合以后,当对外沟通的时候,需要一个话事人,也就是聚合根

首先我们要有用户聚合,用户聚合来管理用户信息。用户实体就是用户聚合的聚合根。

其次,用户登陆日志也作为一个单独的聚合存在。可以思考一下。

✅ 1. 领域含义:登录日志是用户行为的“历史轨迹”,而非用户本身的组成部分

  • 用户(User)是一个业务主语,它的职责是认证、权限、信息管理
  • 登录日志是用户行为产生的事实事件,它与用户的生命周期边界无关

✅ 2. 技术设计上:日志数据量大、写入频繁、读取模式完全不同

特性用户聚合(User)登录日志(LoginLog)
写入频率中低(修改信息、改密码)高(每次登录都写)
数据量小(一个用户一条)非常大(每用户数百/数千条)
删除时是否联动删除用户可保留日志有审计/合规要求时不能删除
查询模式通过用户 ID 查询分页查询 / 最近10条等

这说明 LoginLog 应该用独立表、独立聚合、独立读写模型管理。

✅ 3. 聚合的职责原则(Evans 原则)

一个聚合应尽可能小,保持一致性边界清晰,仅包含保护不变式所必须的部分。

登录日志并不影响用户不变式(用户名、密码唯一性、身份验证),因此它不应该进聚合。

所以,我们把登陆日志放到一个单独的聚合中。登陆日志就是聚合根。

接下来,账本实体、预算角色值对象一起构成了账本聚合。账本作为聚合根存在。

剩下的收支记录作为一个聚合。

领域建模

有了4个聚合。我们还需要划分领域。将相近的聚合放到一个领域中。

领域可以简单理解为粗粒度的聚合。聚合中是实体和值对象。而领域中则是聚合。

我们将4个聚合划分成两个领域,分别是用户领域记账领域

总结

至此,我们完成了整个领域建模的工作,下一步就是将领域建模转换成代码建模了。

下一遍文章会将我们这次的领域建模转换成代码建模,并用spring框架来实现整个代码。

大家可以先自己将领域建模转换成代码建模,并将结果发到评论区一起讨论~

文末福利

关注我发送“MySQL知识图谱”领取完整的MySQL学习路线。
发送“电子书”即可领取价值上千的电子书资源。
发送“大厂内推”即可获取京东、美团等大厂内推信息,祝你获得高薪职位。
发送“AI”即可领取AI学习资料。
部分电子书如图所示。

概念学习

概念学习

概念学习

概念学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值