初探DDD

简介: 基于学习《殷浩详解DDD:领域层设计规范》后的动手实践,简单总结,以及个人理解

基础概念:Domain Primitive(DP/原子领域)

就好像Integer、String是所有编程语言的Primitive一样,DP是 DDD的 原子类型、基础类型

在DDD里,DP可以说是一切模型、方法、架构的基础,就像Integer、String一样,DP在DDD里,又是无所不在的。

DP的定义

Domain Primitive (DP)是一个在特定领域里,拥有精准定义的、可自我验证的、拥有行为的 Value Object 。

  1. DP是一个传统意义上的Value Object,拥有Immutable的特性
  2. DP是一个完整的概念整体,拥有精准定义
  3. DP使用业务域中的原生语言
  4. DP可以是业务域的最小组成部分、也可以构建复杂组合

什么情况下应该用Domain Primitive

常见的DP的使用场景包括:

  • 有格式限制的String:比如NamePhoneNumberOrderNumberZipCodeAddress
  • 有限制的Integer:比如OrderId(>0),Percentage(0-100%),Quantity(>=0)等
  • 可枚举的int:比如Status(一般不用Enum因为反序列化问题)
  • DoubleBigDecimal:一般用到的DoubleBigDecimal都是有业务含义的,比如TemperatureMoneyAmountExchangeRateRating
  • 复杂的数据结构:比如Map>等,尽量能把Map的所有操作包装掉,仅暴露必要行为

DP三原则

  • 将隐性的概念显性化。
  • 将 隐性的 上下文 显性化
  • 封装 多对象 行为

案例




使用DDD进行应用架构

一个好的应用架构,应该需要实现以下几个目标:

  1. 独立于框架:架构不应该依赖某个外部的库或框架,不应该被框架的结构所束缚。
  2. 独立于UI:前台展示的样式可能会随时发生变化(今天可能是网页、明天可能变成console、后天是独立app),但是底层架构不应该随之而变化。
  3. 独立于底层数据源:无论今天你用MySQL、Oracle还是MongoDB、CouchDB,甚至使用文件系统,软件架构不应该因为不同的底层数据储存方式而产生巨大改变。
  4. 独立于外部依赖:无论外部依赖如何变更、升级,业务的核心逻辑不应该随之而大幅变化。
  5. 可测试:无论外部依赖了什么数据库、硬件、UI或者服务,业务的逻辑应该都能够快速被验证正确性。

防腐层


这种常见的设计模式叫做Anti-Corruption Layer(防腐层或ACL)。

很多时候我们的系统会去依赖其他的系统,而被依赖的系统可能包含不合理的数据结构、API、协议或技术实现,如果对外部系统强依赖,会导致我们的系统被”腐蚀“。

这个时候,通过在系统间加入一个防腐层,能够有效的隔离外部依赖和内部逻辑,无论外部如何变更,内部代码可以尽可能的保持不变。

ACL不仅仅只是多了一层调用,在实际开发中ACL能够提供更多强大的功能:

  • 适配器:很多时候外部依赖的数据、接口和协议并不符合内部规范,通过适配器模式,可以将数据转化逻辑封装到ACL内部,降低对业务代码的侵入。在这个案例里,我们通过封装了ExchangeRate和Currency对象,转化了对方的入参和出参,让入参出参更符合我们的标准。
  • 缓存:对于频繁调用且数据变更不频繁的外部依赖,通过在ACL里嵌入缓存逻辑,能够有效的降低对于外部依赖的请求压力。同时,很多时候缓存逻辑是写在业务代码里的,通过将缓存逻辑嵌入ACL,能够降低业务代码的复杂度。
  • 兜底:如果外部依赖的稳定性较差,一个能够有效提升我们系统稳定性的策略是通过ACL起到兜底的作用,比如当外部依赖出问题后,返回最近一次成功的缓存或业务兜底数据。这种兜底逻辑一般都比较复杂,如果散落在核心业务代码中会很难维护,通过集中在ACL中,更加容易被测试和修改。
  • 易于测试:类似于之前的Repository,ACL的接口类能够很容易的实现Mock或Stub,以便于单元测试。
  • 功能开关:有些时候我们希望能在某些场景下开放或关闭某个接口的功能,或者让某个接口返回一个特定的值,我们可以在ACL配置功能开关来实现,而不会对真实业务代码造成影响。同时,使用功能开关也能让我们容易的实现Monkey测试,而不需要真正物理性的关闭外部依赖。


抽象数据存储层

抽象第三方服务

抽象中间件



个人理解

 目前个人理解就是将一些可封装成DP的数据类型,封装成DP,在DP内完成对数据的强校验,错误处理,提高在入参时的清晰度,最终实现降低业务代码的耦合度,提高可测性,做到一次修改DP模型内的校验,定义,在逻辑发生改变时,在DP模型内进行修改即可。

在应用层面,就是面向接口编程,尽量实现不耦合强依赖某个实现,可灵活替换底层实现。通常加入防腐层设计模式实现依赖解耦。

相关文章
|
微服务
微服务迁移模式之Martin Flower绞杀者模式
绞杀者模式(Strangler Pattern)是一种非常流行的从单体系统向微服务迁移的策略,其主张通过用新服务替换特定功能来将单体系统逐步转换为微服务,一旦新服务已经能够代替原有旧有功能,就将原有功能组件绞杀(即彻底停用)。
3606 1
微服务迁移模式之Martin Flower绞杀者模式
|
6月前
|
存储 机器学习/深度学习 弹性计算
阿里云8核16G云服务器收费标准、可选实例规格、价格、应用场景测评
阿里云8核16G云服务器属于中高端算力配置,仅在ECS云服务器系列推出对应规格,无轻量应用服务器版本,1:2的CPU与内存配比让它能兼顾高算力输出和基础内存需求,是中小企业核心业务和大型企业轻量负载的常用选择。2026年该配置的收费标准随实例类型、计费方式、地域选择有明显差异,优惠活动则围绕长期购买折扣、地域专属福利展开,整体定价和福利均贴合不同业务的实际使用需求,适配企业高并发Web服务、中型数据库部署、视频编码、机器学习推理等多种场景。
486 10
|
12月前
|
数据安全/隐私保护 Python
大话西游自动打怪脚本,大话西游抢摊位脚本,刷图刷怪抢元宝工具
完整的游戏刷怪脚本实现,包含多模块功能(怪物生成、波次控制、掉落系统等),使用Python编写
|
9月前
|
存储 机器学习/深度学习 监控
网络管理监控软件的 C# 区间树性能阈值查询算法
针对网络管理监控软件的高效区间查询需求,本文提出基于区间树的优化方案。传统线性遍历效率低,10万条数据查询超800ms,难以满足实时性要求。区间树以平衡二叉搜索树结构,结合节点最大值剪枝策略,将查询复杂度从O(N)降至O(logN+K),显著提升性能。通过C#实现,支持按指标类型分组建树、增量插入与多维度联合查询,在10万记录下查询耗时仅约2.8ms,内存占用降低35%。测试表明,该方案有效解决高负载场景下的响应延迟问题,助力管理员快速定位异常设备,提升运维效率与系统稳定性。
364 4
|
缓存 JSON 前端开发
超详细讲解:http强缓存和协商缓存
超详细讲解:http强缓存和协商缓存
|
Linux 知识图谱
Centos7安装killall,fuser, killall,pstree和pstree.x11
通过上述步骤,您已在CentOS 7系统中成功部署了killall、fuser、pstree以及pstree.x11,为高效管理系统进程打下了坚实基础。更多关于服务器管理与优化的知识,获取全面技术支持与解决方案。
820 1
|
消息中间件 负载均衡 NoSQL
Redis系列学习文章分享---第七篇(Redis快速入门之消息队列--List实现消息队列 Pubsub实现消息队列 stream的单消费模式 stream的消费者组模式 基于stream消息队列)
Redis系列学习文章分享---第七篇(Redis快速入门之消息队列--List实现消息队列 Pubsub实现消息队列 stream的单消费模式 stream的消费者组模式 基于stream消息队列)
429 0
|
关系型数据库 MySQL 数据库
mysql查看用户的过期时间
通过本文的介绍,希望您能够深入理解和掌握在MySQL中查看用户过期时间的方法,并在实际项目中灵活运用这些技术,提升数据库管理的安全性和效率。
1246 3
|
NoSQL Java C#
Java 缺失的特性:扩展方法
本文介绍了如何使用 Manifold 在 Java 中实现扩展方法,来助力提升开发效率和代码可读性。
121069 11
Java 缺失的特性:扩展方法
|
Kubernetes 监控 API
在K8S中,滚动更新的过程是什么?
在K8S中,滚动更新的过程是什么?

热门文章

最新文章