思辨领域模型--DDD≠数据库关系模型

简介: Eric Evans的《领域驱动设计》问世已经14年之久,到今天几乎所有业务团队都或多或少有涉及DDD。然而较真起来会发现,认真遵循DDD设计原则的团队仅是少数,多数团队的现状依然是: **数据库关系=模型。

Eric Evans的《领域驱动设计》问世已经14年之久,到今天几乎所有业务团队都或多或少有涉及DDD。然而如果较真会发现,认真遵循DDD设计原则的团队仍是少数,在多数团队的现都是:领域模型=数据库关系。DDD崇尚的是oo式表达,也就是常说的充血模型,对以关系型数据库实体关系为中心的关系模型甚至是可以用鄙夷来形容。

数据库关系模型

以数据库关系指导编程实践,是关系对程序的外延入侵,是预假设关系经存在再按图索骥将执行逻辑映射到关系,最终收口是落在数据库而非程序本身。程序本身成了一条条执行通道,每一条通道服务于特定场景的关系,后果必然是过程思维和面条代码。

假设有业务场景--向购物车添加商品,以关系为中心,代码组织如下:

public class CartLine{
    @Getter @Setter
    private String skuCode;
    @Getter @Setter
    private int buyNo;//数量
}

public class CartServiceImpl implements CartService{
    public void addLine(String skuCode, int buyNo, ......){
        CartLine line= getLine(skuCode,  .....)
        if(line!= null){
            line.setBuyNo(buyNo);
            update(line);
        }else{
            new CartLine
            insert
        }
    }
}

绝大多数人都应该会有类似代码的编写经历,最常见在经典三分层架构中的Service层,它本质上就是一个类存储过程,对其执行过程做翻译:

CartLine line= select * from cart_line where sku_code= #{skuCode}
if(line!= null){
  update cart_line set buy_no= #{buyNo} where sku_code= #{skuCode}
}else{
  insert cart_line values(xxxx)
}
      

业务处理就是在有序执行一条条sql,老外给它取了个好听的名字叫事务脚本。事务脚本是非常典型的过程式表述,类似是串联sql完成一段完整的业务,可以用求和数学公式“事务脚本=∑fi,fi代指一条sql”来定义

架构模式上管这种代码叫贫血模型,即无行为,表达力贫瘠。Martin Fowler在《企业应用架构模式》中

定义它是反模式,简单系统使用它开发没问题,而对于复杂业务,业务逻辑、各种状态散布在大量的函数中,维护扩展的成本会变得很高。

数据库中心的设计是”修改一处,全量回归“的悲剧源头,也是代码写久后枯燥、无聊、觉得都是重复劳动的源头。过程式代码并不需要精心设计和组织,自然写代码也就成了无意义的翻译器。

oo模型

假设内存无限大且永不宕机,即已经没有持久化必要,换句话说完全可以不使用数据库,此时应该如何编写代码?

  • 是使用与现实世界的活动实体做连接、特征和行为封装在一起,职责明确、逻辑合理分布的有状态对象,再按场景将合适类联系起来?
  • 还是使用仅映射活动实体特征的pojo,按场景忠实反应发生过程依次get/set操作pojo属性?

Jdk以及各类优秀中间件都是以内存操作为主,可以参考它们的选择:即便是主推pojo规范的ejb都没有选择贫血模型,反而是极度充血-- 有行为,有联系、表达力强,容易组织。

数据持久化应只被当成是程序的暂停而非结束,对暂停而言,下一次再执行时需要忠实还原对象的上次执行后状态,而对结束则下一次是一个新的开始。即load; do; new; setter/getter; persist的区别。如果从这个角度出发,对象就变得近似是常驻在内存。

在Vaughn Vernon的《实现领域驱动设计》中关于“六边形架构”如何在领域实践应用中对数据库和DDD的关系有很清晰的阐述很:
数据库仅仅只是Domain Model的右向适配(被驱动者)。

数据库只是持久化手段,是一种基础设施,不该作为指导程序运行的模型。写代码时要时刻保持一种警惕,如果把关系型数据库替换成json、普通文本或者无schema的nosql数据库,要如何保证逻辑层的无感?

正确的方法是以对象和对象联系而非数据库表关系作为指导程序运行的基础。一次完整的业务操作由各实体对象行为协同完成,结果最终会反映在内存中各对象实例的内在属性上。这样无论怎么修改持久化方案,都只需要改变与特定持久化方案的适配策略。

某逆向交替系统,其逆向状态是记录在正向交易上的,

| order_id | order_status |pay_fee|item_id|refund_amount |refund_status|attributes|...... |

refund_amount和refund_status分别代表逆向退款金额以及退款状态。很显然,这样的表设计会导致两个问题:1) 无法多次逆向,前一次状态在下一次发起后被覆盖,即逆向无法追溯;2)逆向需要更新交易订单属性,方式是调用交易接口更新,因此某些情况下可能会有乐观锁问题-- 逆向更新了锁版本导致交易再去更新失败。

在小规模试跑阶段,业务上会严格限制一笔订单一次逆向,同时对于乐观锁问题采用多次重试机制,因此问题并不明显。逐渐的业务开始起来,首先业务量大之后重试导致的性能问题凸显-- 在加事务的情况下,数据库连接是一直持有直到提交或回滚,多次重试相当于增加了几倍持有连接时间,因此会明显明显降低数据库吞吐;其次,对于不能多次逆向合作伙伴也开始有反弹。因此交易和逆向的表拆分变得势在必行。

因为逆向在设计时使用的充血+六边形架构,实际上迁移并没有很大工作量,只是重新建了表,然后对数据库适配进行修改,将输出表由A指向B。

而如果使用贫血模型,对表结构做了较大变更的情况下,逻辑代码一定会需要修改。可以用事务脚本的公式做个简单的逆推导:
表变化=sql变化=事务脚本(逻辑执行)变化。

目录
相关文章
|
5月前
|
存储 缓存 NoSQL
数据库实体与关系模型
【5月更文挑战第16天】本文介绍了数据库模型和UML关系,UML包括依赖、关联(聚合、组合)、泛化和实现4类关系。UML有13种图,分为结构图、行为图和交互图。数据库的基本数据模型包括外模式(用户视图)、概念模式(全局逻辑结构)和内模式(物理存储)。数据模型三要素是数据结构、操作和约束条件。简单易用的缓存数据模型适用于需求灵活、高性能、大数据量且不要求强一致性的场景。
81 5
数据库实体与关系模型
|
5月前
|
机器学习/深度学习 存储 安全
数据库模型:层次模型、网状模型、关系模型
数据库模型:层次模型、网状模型、关系模型
|
5月前
|
SQL 数据可视化 关系型数据库
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
轻松入门MySQL:深入探究MySQL的ER模型,数据库设计的利器与挑战(22)
313 0
|
5月前
|
SQL 存储 调度
从 Volcano 火山模型到 Pipeline 执行模型,阿里云数据库 SelectDB 内核 Apache Doris 执行模型的迭代
一个合适的执行模型对于提高查询效率和系统性能至关重要。本文全面剖析 Apache Doris Pipeline 执行模型的设计与改造历程,并在 2.1 版本对并发执行模式与调度模式进一步优化,解决了执行并发受限、执行及调度开销大等问题。
从 Volcano 火山模型到 Pipeline 执行模型,阿里云数据库 SelectDB 内核 Apache Doris 执行模型的迭代
|
9天前
|
前端开发 IDE 数据库连接
ThinkPHP6 模型层的模型属性,表映射关系,以及如何在控制层中使用模型层和模型层中的简单CRUD
本文详细介绍了ThinkPHP6中模型层的使用,包括模型属性设置、表映射关系、以及如何在控制层中使用模型层进行CRUD操作。
ThinkPHP6 模型层的模型属性,表映射关系,以及如何在控制层中使用模型层和模型层中的简单CRUD
|
28天前
|
前端开发 数据库 开发者
数据模型(数据库表设计)生成代码
BizWorks ToolKit 插件集成 Mybatis-Plus 代码生成工具,支持从数据库表批量生成代码,简化开发流程。本文详细介绍配置方法及项目示例,包括配置文件格式、生成选项及具体操作步骤,帮助开发者快速实现代码同步更新。配置文件 `.mp.yaml` 支持自定义输出目录、生成组件等,适用于多种项目结构。
30 0
|
2月前
|
API 数据库 开发者
【独家揭秘】Django ORM高手秘籍:如何玩转数据模型与数据库交互的艺术?
【8月更文挑战第31天】本文通过具体示例详细介绍了Django ORM的使用方法,包括数据模型设计与数据库操作的最佳实践。从创建应用和定义模型开始,逐步演示了查询、创建、更新和删除数据的全过程,并展示了关联查询与过滤的技巧,帮助开发者更高效地利用Django ORM构建和维护Web应用。通过这些基础概念和实践技巧,读者可以更好地掌握Django ORM,提升开发效率。
23 0
|
2月前
|
SQL Shell API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
python Django教程 之 模型(数据库)、自定义Field、数据表更改、QuerySet API
|
3月前
|
存储 SQL Oracle
|
4月前
|
存储 关系型数据库 MySQL
MySQL周内训参照1、ER实体关系图与数据库模型图绘制
MySQL周内训参照1、ER实体关系图与数据库模型图绘制
55 1
下一篇
无影云桌面