为什么80%的码农都做不了架构师?>>>   

摘要: Eric Evans的《领域驱动设计》问世已经14年之久,到今天几乎所有业务团队都或多或少有涉及DDD。然而较真起来会发现,认真遵循DDD设计原则的团队仅是少数,多数团队的现状依然是: **数据库关系=模型。** # 贫血模型 DDD崇尚充血模型,对以关系型数据库为中心的贫血模型则甚至可以用鄙夷形容。Martin Fowler在《企业应用架构模式》书中有系统谈过贫血模型, > 他定义“

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

贫血模型

DDD崇尚充血模型,对以关系型数据库为中心的贫血模型则甚至可以用鄙夷形容。Martin Fowler在《企业应用架构模式》书中有系统谈过贫血模型,

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

假设有业务场景--向购物车添加商品,使用贫血模型代码如下:

public class CartLine{@Getter @Setterprivate String skuCode;@Getter @Setterprivate 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 CartLineinsert}}
}

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

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。

充血模型

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

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

Jdk以及各类优秀中间件都是以内存操作为主,可以参考它们的选择:即便是主推pojo规范的ejb都没有选择贫血模型,反而是极度充血-- 暴露一组行为给外部,由外力驱动对象变化。

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

在Vaughn Vernon的《实现领域驱动设计》中关于“六边形架构”如何在领域实践应用中已经阐述很清楚:
数据库仅仅只是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变化=事务脚本(逻辑执行)变化。

小结

贫血模型的本质是反oo的,是数据库关系对代码的入侵,是先假定关系已经存在再想办法把执行逻辑映射到关系上,最终收口就落在了数据库,而非程序本身,各service最终都会变成一根冗长枯燥难读的“面条”。这是”修改一处,全量回归“的悲剧源头,也是代码写久后枯燥、无聊、觉得都是重复劳动的源头,过程式代码是不需要设计更不需要模式的。

作者: 鹰波
原文链接
本文为云栖社区原创内容,未经允许不得转载。

转载于:https://my.oschina.net/yunqi/blog/1925020

思辨领域模型--DDD和关系型数据库相关推荐

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

    Eric Evans的<领域驱动设计>问世已经14年之久,到今天几乎所有业务团队都或多或少有涉及DDD.然而如果较真会发现,认真遵循DDD设计原则的团队仍是少数,在多数团队的现都是:**领 ...

  2. 【领域驱动设计】(4):从 DDD 落实到数据库设计的整个过程

    云世  公众号 过去,系统的软件设计是以数据库设计为核心,当需求确定下来以后,团队首先开始进行数据库设计.因为数据库是各个模块唯一的接口,当整个团队将数据库设计确定下来以后,就可以按照模块各自独立地进 ...

  3. 领域模型DDD与聚合根

    领域模型:聚合.聚合根详解 聚合和聚合根是领域模型里面很重要的一个概念,其实我们在从真实世界对业务对象进行识别和概念建模的时候,关注的就是聚合根,这才是我们真正要管理的业务对象.一个对象可能有多个层次 ...

  4. mysql 应用前景_图数据库在企业应用中前景如何,相比关系型数据库有哪些优势?...

    图数据虽然在国内的应用还不是非常广泛,但发展前景还是很可观的. 跟以Oracle为代表的传统关系型数据相比,图的逻辑可以很好的解决绝大多数底层数据分析问题, 图数据的数据逻辑维度要远高于关系型数据,所 ...

  5. mysql类exadata功能_几类关系型数据库的数据解决方案

    今天聊下几类关系型数据库的数据解决方案,算是抛砖引玉,近期也要对技术方向上做一些扩展,也算是前期的小结吧. 1 3 Oracle 目前市面上的主流版本应该还是11gR2,记得很多年前有个网站做过一次调 ...

  6. Mac MySQL 数据库配置(关系型数据库管理系统)

    本文已停止更新,点击此链接查看本文最新内容 !!! 前言 MySQL 关系型数据库管理系统. 1.配置准备工作 1)配置数据库准备工作 下载相关软件 mysql-5.7.21-1-macos10.13 ...

  7. 关系型数据库设计要领(值得收藏)

    欢迎关注方志朋的博客,回复"666"获面试宝典 摘要 本文讨论关系数据库设计相关的一些内容,涉及关系模型,表结构设计等内容,以学生选修课程讲述设计过程,在尽量讲清楚设计要领的前提下 ...

  8. 关系型数据库管理系统和SQL介绍

    1. 关系型数据库管理系统的介绍 数据库管理系统(英语全拼:Relational Database Management System,简称RDBMS)是为管理关系型数据库而设计的软件系统,如果大家想 ...

  9. 大数据分析中使用关系型数据库的关键点

    相当一部分大数据分析处理的原始数据来自关系型数据库,处理结果也存放在关系型数据库中.原因在于超过99%的软件系统采用传统的关系型数据库,大家对它们很熟悉,用起来得心应手. 在我们正式的大数据团队,数仓 ...

最新文章

  1. Single Shot Multibox Detection (SSD)实战(下)
  2. 微软打造了全球最大的Git代码库
  3. 这才是GraphQL最详尽的解释
  4. opencv中traincascade训练分类器
  5. 判断 iframe 是否加载完成的完美方法
  6. java多线程 -- 创建线程的第三者方式 实现Callable接口
  7. 安卓进阶系列-07数据库框架(GreenDAO)的使用
  8. AS3 --调用Js
  9. verilog奇偶分频
  10. python编程快速上手
  11. python三引号的作用_Python学习笔记(三)基本数据类型
  12. vyos v1.2安装flask
  13. Java小农养成记第七天
  14. 取十位数,百位数,千位数的各位方法小姐
  15. 读书《你能写出好故事:写作的诀窍、大脑的奥秘、认知的陷阱》
  16. junit 测试似有方法_JUnit测试私有方法(protected方法类同)
  17. 【转载】刘未鹏的C++学习历程 + 小诗一首
  18. 【分享】免梯子的GPT,玩 ChatGPT 的正确姿势
  19. 对付木马:空手入白刃谁动了我的电脑系统(转)
  20. 图像加权和制作鬼影---OpenCV-Python开发指南(2)

热门文章

  1. 开发者账号APP转让流程
  2. 索尼电视怎么安装奈飞,详细的图文教程分享
  3. UVM Object
  4. python人机对战小游戏
  5. 音乐下载器源码,需要自取
  6. 构建用户安全评级,UGC智能化审核应用实践
  7. OSChina 周三乱弹 ——没见过这么漂亮的女司机啊!
  8. 用matplotlib和pandas绘制股票MACD指标图,并验证化交易策略
  9. [源码阅读] 阿里SOFA服务注册中心MetaServer(1)
  10. 阿里云——轻量应用服务器