应无所住,而生其心
排名
1
文章
860
粉丝
112
评论
163
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

领域驱动设计DDD ABP VNext 三:领域模型 之 失血模型,贫血模型,充血模型,胀血模型

9498人阅读 2022/5/22 16:56 总访问:5182560 评论:0 收藏:0 手机
分类: 软件架构

领域模型分为:失血模型,贫血模型,充血模型,胀血模型。

一、失血模型

传统的三层架构,实体对象就是简单的POJO或者POCO,只包含属性没有行为,也就是单纯的实体对象,是不包含任何业务逻辑的,就是失血模型 。


该模型的缺点是不够面向对象,领域对象只是作为保存状态或者传递状态使用,它是没有生命的,只有数据没有行为的对象不是真正的对象,是失血的对象,在业务逻辑层里面处理所有的业务逻辑。


虽然简单的对象也通过了私有化字段使用getter/setter方法修改对象属性。也是属于面向对象三大特征的封装,但这还是太过失血,完全不够面向对象的设计。

事务的管理是在业务逻辑层完成的,业务逻辑层实现具体的业务逻辑,领域模型只有get,set。这种模式下会造成领域逻辑散落在各个应用服务中,导致代码重复。

二、贫血模型

简单来说,就是domain ojbect(领域对象)包含了不依赖于持久化的领域逻辑,而那些依赖持久化的领域逻辑被分离到业务逻辑层。

使用DDD要注意应用服务与领域服务的区别,也能更好理解贫血模型与应用

应用层起到一个编排的作用。只有应用逻辑,没有领域逻辑。

应用服务不做出任何业务决策,它将这些决策委托给领域模型。请注意,领域模型是隔离的:Atm实体不会将自身保存到数据库,也不会通过支付网关直接收费。我们在这里有一个很好的关注点分离:业务逻辑放入领域模型,而与外部世界的交互 - 应该放入应用服务。
您可以注意到大多数遵循此指南的代码库中的模式。他们的执行流程如下:

  • 准备业务操作所需的所有信息:从数据库加载参与实体并从其他外部源检索所需的所有数据。
  • 执行操作。该操作由领域模型做出的一个或多个业务决策组成。这些决定导致更改模型的状态,或生成一些结果(上面示例中的amountWithCommission值)。
  • 将操作结果应用于外界。

只有第1步和第3步涉及与外部依赖关系的工作。在第一步中检索到数据下接着实现第二步:接受的入参数及其生成的输出仅包含实体,值对象和基本类型。
请注意,在简单的CRUD应用程序中,没有第二步,因为没有做出决定的地方。在这种情况下,所有操作都可以仅由应用服务执行,无需将它们委托给领域模型。实际上,不存在任何富领域模型。在这种情况下,贫血领域模型也可以正常工作。

实体不光有数据,还要有行为。谁拥有这些数据谁就负责数据的维护,否者就会造成模型失血。


既然是面向对象,那么对象就应该不仅仅只有属性,而且应该还有包含该对象拥有的行为也就是方法,否者就是面向过程而不是面向对象。所以以数据库为核心,实体对象只有属性没有行为方法其实更多的是面向过程的开发方式,开始的时候简单到后面会越来越复杂,容易越写越乱,什么乱七糟八的业务逻辑都没有边界的往里边堆,越来越难以维护。


如果领域层的对象不包含任务业务逻辑,也就是对象仅仅就是属性没有方法,那么其实还是三层架构而已,相当于把实体单独提出来一层而已,领域对象几乎只作传输介质之用。

这种模型是Martin Fowler所指的真正的领域模型(domain model)。

如何划分domain logic和service logic的标准

我们知道了这种模式,是把部分业务逻辑(不包含数据持久化的业务逻辑)从业务逻辑层(领域服务层)放到了领域模型中来,但是我们在实际拆分的时候应该遵循一些什么原则呢。


领域逻辑应该放在只和一个domain object的实例状态有关的地方,而不应该和一批domain object的状态有关的地方。

比如:


比如遇到需要两个领域对象协调的,那么则放到领域服务中处理。如:实体账户A给实体账户B转账,这种转账过程放到那个实体都不合适,那么就放到领域服务中处理。领域服务是没有状态的只有行为。

这种方式相比失血模型 ,更加的面向对象。domain ojbect(领域对象)包含了不依赖于持久化的领域逻辑,但还没有把所有业务逻辑都放到领域对象。

三、充血模型

充血模型和第二种模型差不多,所不同的就是如何划分业务逻辑,即认为,绝大多业务逻辑都应该被放在domain object里面(包括持久化逻辑),而Service层应该是很薄的一层,仅仅封装事务和少量逻辑,不和DAO层打交道。

Service(事务封装) —-> domain object <—-> DAO

这种模型就是把第二种模型的domain object和business object合二为一了。

该模型的优缺点

该模型的优点:


1、更加符合OO的原则

2、Service层很薄,只充当Facade的角色,不和DAO打交道。

这种模型的缺点:


1、持久化层和domain object形成了双向依赖,复杂的双向依赖会导致很多潜在的问题。不过我们依赖的是仓储,其实这块也不是什么问题了。只是架构发展的某个阶段可能会要考虑一下这种问题,这里也列一下。


2、如何划分Service层逻辑和domain层逻辑是非常含混的,在实际项目中,由于设计和开发人员的水平差异,可能导致整个结构的混乱无序。

3、考虑到Service层的事务封装特性,Service层必须对所有的domain object的逻辑提供相应的事务封装方法,其结果就是Service完全重定义一遍所有的domain logic,非常烦琐,而且Service的事务化封装其意义就等于把OO的domain logic转换为过程的Service TransactionScript。该充血模型辛辛苦苦在domain层实现的OO在Service层又变成了过程式,对于Web层程序员的角度来看,和贫血模型没有什么区别了。

四、胀血模型

基于充血模型的第三个缺点,有人提出,干脆取消Service层,只剩下domain object和DAO两层,在domain object的domain logic上面封装事务。

domain object(事务封装,业务逻辑) <—-> DAO

似乎ruby on rails就是这种模型,他甚至把domain object和DAO都合并了。

该模型的优缺点

该模型优点:


1、简化了分层

2、也算符合OO

该模型缺点:


1、很多不是domain logic的service逻辑也被强行放入domain object,引起了domain ojbect模型的不稳定。

2、domain object暴露给web层过多的信息,可能引起意想不到的副作用。

五、使用推荐


贫血模型和充血模型在不同的业务场景下,都能够有用武之地。但失血模型和胀血模型是属于两个极端,不推荐使用。


相比充血模型其实个人更加推荐使用贫血模型。如果把数据库持久化的操作和实体对象包含在一起,虽然更加的OO,但是需要领域对象与持久化层的双向依赖,对领域逻辑与服务逻辑的拆分需要更加大的考验,更适用于业务逻辑比较复制的系统。但是实际情况实际分析,对于一些业务逻辑不是特别复杂的项目,我们在设计上也可以不用那么聚合。其实还是要看情况,不同的设计都有它的优缺点,比如双向依赖其实我们也是依赖的仓储,其实也不是什么问题,也更加符合OO,所以还是要根据不同的系统不同的业务逻辑来针对性的选择不同的模式吧。

当然很多说到领域模型都把上面的四种模块简化成,两种模型。贫血模型和充血模型,
贫血模型指的是上面的失血模型除了属性没有任何逻辑。


欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

领域驱动设计DDD的一点理解

有人误认为项目架构中加入xxRepository,xxDomain,xxValueObject就变成了DDD架构。如果没有悟出其精髓就在项目中加入这些...

领域驱动设计DDD ABP VNext项目架构搭建模块使用

[TOC]用户接口层改造nuget中下载abp依赖VoLo.Abp.AspNetCore.Mvc ItemGroup添加方式 &lt;ItemGroup&gt; &lt;Packa...

领域驱动设计DDDRepository

DDD中的Repository模式Repository模式也称存储库模式或仓储模式,根据Eric Evans的《领域驱动设计》一书,“存储库是一种封...

领域驱动设计DDD ABP VNext使用仓储

[TOC]领域驱动设计仓储介绍在领域层和数据映射层之间进行中介,使用类似集合的接口来操作领域对象.” (Martin Fowler)。 实...

领域驱动设计概念的理解

[TOC]领域服务(Domain Service)领域中的一些概念不太适合建模为对象,即归类到实体对象或值对象,因为它们本质上就是一些...

这样学英语个月超过你过去学

本文作者三年间从四级勉强及格到高级口译笔试210,口试232。找工作面试时给其口试的老外考官听了一分钟就说你的英语不用考...

行代码 人民币金额大写转换

publicstaticStringConvertToChinese(Decimalnumber) { vars=number.ToString(&quot;#L#E#D#C#K#E#D#C#J#E#D#C#I#E#D#C#H...

C.Net 配合小程序实现经过第方服务器中转文件

某些时候,微信小程序前段上传文件的时候需要经过第三方服务器再将文件上传到客户的服务器;操作如下:1:(小程序内向中端服...

分钟快速复习MVC知识

读这篇文章不意味着你一定要去并且能搞定MVC面试。这篇文章的目的是在面试之前让你快速复习MVC知识。什么是MVC (模型 视图 ...

redis中主从、哨兵和集群这个有什么区别

主从模式:备份数据、负载均衡,一个Master可以有多个Slaves。sentinel(哨兵)发现master挂了后,就会从slave中重新选举一个...

怎样解决安卓在微信浏览器对于第方网页的两次请求问题?

之前的项目是没有这种情况的,但是最近发现安卓手机在微信浏览器访问第三方网页时,页面点击某个按钮,写日志显示只触发了...

微信开发 使用反射根据消息类型自动调用不同方法

微信只会向我们一个地方推送消息,如果全部逻辑都写到一起,代码会非常多。所以我们可以考虑通过消息类型,来实现不同的消...

c使用unity实现依赖注入:unity常用生命周期

常用的生命周期有瞬时模式,单利模式,每个线程一个对象模式等。一:TransientLifetimeManagerTransientLifetimeManager模...

推荐一款无压缩软件bandizip

哈喽,大家好,我是刘小贱相信大家作为程序员的一员,都在使用压缩软件,但是呢我们常常使用的压缩软件都有很多的弊端,不但对...

rabbitmq六大版块(Fanout类型交换机相同数据多逼格处理)

Fanout类型交换机的特点是:同样的数据分发给每一个自己所绑定的队列,每个队列可以按照自己的需求对数据进行不同的处理【...