Eric Evans的《领域驱动设计》问世已经14年之久,到今天几乎全部业务团队都或多或少有涉及DDD。然而若是较真会发现,认真遵循DDD设计原则的团队还是少数,在多数团队的现都是:领域模型=数据库关系。DDD崇尚的是oo式表达,也就是常说的充血模型,对以关系型数据库实体关系为中心的关系模型甚至是能够用鄙夷来形容。sql
以数据库关系指导编程实践,是关系对程序的外延入侵,是预假设关系经存在再按图索骥将执行逻辑映射到关系,最终收口是落在数据库而非程序自己。程序自己成了一条条执行通道,每一条通道服务于特定场景的关系,后果必然是过程思惟和面条代码。数据库
假设有业务场景--向购物车添加商品,以关系为中心,代码组织以下:编程
绝大多数人都应该会有相似代码的编写经历,最多见在经典三分层架构中的Service层,它本质上就是一个类存储过程,对其执行过程作翻译:json
业务处理就是在有序执行一条条sql,老外给它取了个好听的名字叫事务脚本。事务脚本是很是典型的过程式表述,相似是串联sql完成一段完整的业务,能够用求和数学公式“事务脚本=∑fi,fi代指一条sql”来定义架构
架构模式上管这种代码叫贫血模型,即无行为,表达力贫瘠。Martin Fowler在《企业应用架构模式》中nosql
定义它是反模式,简单系统使用它开发没问题,而对于复杂业务,业务逻辑、各类状态散布在大量的函数中,维护扩展的成本会变得很高。
数据库中心的设计是”修改一处,全量回归“的悲剧源头,也是代码写久后枯燥、无聊、以为都是重复劳动的源头。过程式代码并不须要精心设计和组织,天然写代码也就成了无心义的翻译器。函数
oo模型
假设内存无限大且永不宕机,即已经没有持久化必要,换句话说彻底能够不使用数据库,此时应该如何编写代码?性能
• 是使用与现实世界的活动实体作链接、特征和行为封装在一块儿,职责明确、逻辑合理分布的有状态对象,再按场景将合适类联系起来?spa
• 仍是使用仅映射活动实体特征的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变化=事务脚本(逻辑执行)变化。
本文做者:鹰波
本文为云栖社区原创内容,未经容许不得转载。