MVC和DDD的贫血和充血模型对比
文章目录
- 架构区别
 - MVC三层架构
 - DDD四层架构
 
- 贫血模型
 - 代码示例
 
- 充血模型
 - 代码示例
 
架构区别
MVC三层架构
MVC三层架构是软件工程中的一种设计模式,它将软件系统分为 模型(Model)、视图(View)和控制器(Controller) 三个核心部分。具体如下:
- 模型(Model):模型代表的是数据和业务逻辑,它负责管理应用程序的数据和定义操作数据的规则。模型直接与数据库进行交互,处理如数据库查询、更新等操作,并返回结果给视图或控制器。
 - 视图(View):视图是用户界面元素,它负责显示模型层提供的数据。视图通常不包含程序的逻辑,而是专注于数据的展示和用户交互的界面。一个模型可以对应多个视图,即相同的数据可以以不同的方式展现给用户。
 - 控制器(Controller):控制器是模型与视图之间的协调者,它处理用户的输入和系统的事件,执行相应的业务逻辑,并选择相应的视图来展示模型处理后的数据。控制器确保模型和视图之间的同步和数据的一致性。
 
DDD四层架构
DDD的四层架构旨在通过明确的层次划分来组织代码结构,促进模块化、可维护性和可扩展性。尽管DDD本身并未严格定义必须使用四层架构,不过在实践中,开发者常参考以下四层来构建DDD应用:
-  
表示层(Presentation Layer):
这一层负责用户界面的展示和用户交互。它包括Web页面、移动应用的界面或是命令行界面等。此层的主要职责是接收用户的输入并展示处理结果,同时调用领域层的服务来完成业务操作。 -  
应用层(Application Layer):
应用层是领域层和表示层之间的桥梁,它负责协调领域对象执行业务操作,处理事务边界,以及执行任何与具体技术框架相关的任务(如安全、权限控制等)。应用层中的服务通常很薄,主要封装领域逻辑的入口点,而不包含业务规则。 -  
领域层(Domain Layer):
DDD的核心所在,封装了复杂的业务规则和逻辑。这一层包含了领域模型、实体(Entities)、值对象(Value Objects)、聚合(Aggregates)、领域事件(Domain Events)等概念。领域层的设计关注于反映业务领域的本质,使得业务逻辑清晰、可维护。 -  
基础设施层(Infrastructure Layer):
提供底层技术支持,如数据库访问、消息队列、外部服务调用等。这一层实现了技术细节,如ORM映射、数据库访问接口、日志记录等,为上层提供必要的服务,同时也隐藏了技术实现细节,使得领域层和应用层能更加专注于业务逻辑。 
贫血模型
在MVC(Model-View-Controller)架构中,模型(Model)负责管理应用程序的数据和业务逻辑。如果模型的实现过于简单,仅仅作为数据的容器,而没有包含足够的业务逻辑,就可能导致所谓的“贫血模型”。
 在这种情况下,控制器(Controller)可能会变得过于臃肿,因为它需要处理大量的业务逻辑,而这些逻辑本应该由模型来管理。这种设计问题被称为“贫血模型,膨胀控制器”(Anemic Model, Bloated Controller),其中“贫血模型”指的是缺乏业务逻辑的模型,而“膨胀控制器”则指代承担了过多职责的控制器。
 MVC架构中的贫血模型会降低代码的可维护性,将导致以下问题:
- 业务逻辑分散:由于业务逻辑主要在控制器中实现,而不是封装在模型中,这导致业务逻辑分散在多个控制器中。当需要修改或扩展业务逻辑时,开发者需要找到并修改所有相关的控制器,这使得维护变得更加困难和耗时。
 - 代码复用性差:模型层如果只是简单的数据容器,而没有包含业务逻辑,那么这些模型就很难在不同的上下文中重用。每个新的应用场景都需要重新编写控制器逻辑,这增加了开发的工作量,并且可能导致重复代码的产生。
 - 违反单一职责原则:理想情况下,每个类应该只有一个引起变化的原因。但在贫血模型中,控制器既处理用户输入和交互,又处理业务逻辑,这违反了单一职责原则。当一个类承担了过多的职责时,它变得难以理解和修改。
 
代码示例
下面是一个示例,展示了一个基本的银行账户(BankAccount)领域对象,包含了存款(deposit)和取款(withdraw)的业务逻辑:
public class BankAccountService {public void deposit(BankAccount account, double amount) {if (amount <= 0) {throw new IllegalArgumentException("Deposit amount must be positive.");}double newBalance = account.getBalance() + amount;account.setBalance(newBalance);}public void withdraw(BankAccount account, double amount) {if (amount <= 0) {throw new IllegalArgumentException("Withdrawal amount must be positive.");}if (account.getBalance() < amount) {throw new IllegalStateException("Insufficient funds.");}double newBalance = account.getBalance() - amount;account.setBalance(newBalance);}
}
 
充血模型
在(DDD)中,充血模型(Rich Model)是一种设计哲学,它强调将业务逻辑和规则尽可能地集中在领域模型中,而不是分散在服务层或控制器中。这种设计方法与贫血模型(Anemic Model)形成对比,后者的领域模型通常只包含数据结构,而业务逻辑则被放置在其他地方,如控制器或服务层。
 充血模型的特点包括:
- 丰富的领域逻辑:领域模型包含与该领域相关的所有业务逻辑和行为,这使得模型具有丰富的功能和表现力。
 - 封装性:通过将业务逻辑封装在领域模型中,这些逻辑对外部是不可见的,只能通过模型提供的接口进行交互。
 - 领域专家的语言:领域模型使用领域专家的语言来表达概念和规则,这有助于确保软件设计与业务需求紧密匹配。
 - 可维护性和可扩展性:由于业务逻辑集中在领域模型中,当业务规则发生变化时,只需要修改相应的模型,而不需要在整个应用程序中寻找和修改逻辑。
 - 高度的内聚性:领域模型是围绕业务概念构建的,这使得相关的业务逻辑和数据高度内聚
 
充血模型的优点在于它能够更好地反映和处理复杂的业务需求,同时提高了代码的可读性、可维护性和可测试性。然而,它也可能需要更多的设计和抽象工作,特别是在项目的早期阶段。
代码示例
下面是一个简化的充血模型示例,展示了一个基本的银行账户(BankAccount)领域对象,包含了存款(deposit)和取款(withdraw)的业务逻辑:
// 银行账户领域对象 - 充血模型示例
public class BankAccount {private String accountId;private double balance;// 构造函数public BankAccount(String accountId, double initialBalance) {if (initialBalance < 0) {throw new IllegalArgumentException("Initial balance cannot be negative.");}this.accountId = accountId;this.balance = initialBalance;}// 存款业务逻辑public void deposit(double amount) {if (amount <= 0) {throw new IllegalArgumentException("Deposit amount must be positive.");}addAmountToBalance(amount);}// 取款业务逻辑,包含校验余额public void withdraw(double amount) {if (amount <= 0) {throw new IllegalArgumentException("Withdrawal amount must be positive.");}checkSufficientFunds(amount);subtractAmountFromBalance(amount);}// Getter方法,通常在充血模型中只用于展示,非业务逻辑的一部分public double getBalance() {return balance;}// 增加余额public void addAmountToBalance(double amount){return balance += amount;}// 扣减余额public void subtractAmountFromBalance(double amount){return balance -= amount;}// 检查余额public boolean checkSufficientFunds(double amount){if (balance < amount) {throw new IllegalStateException("Insufficient funds.");}return true;}} 

public class Main {public static void main(String[] args) {BankAccount account = new BankAccount("123456", 0);account.deposit(500); try {account.withdraw(300);} catch (IllegalStateException e) {System.out.println(e.getMessage());}}
}
 
BankAccount 类不仅包含了账户的属性(如 accountId 和 balance),还直接实现了业务操作(如存款和取款),并包含了相关的业务规则检查(比如不能存取负数金额,取款不能超过余额)。这就是充血模型的核心思想,即领域对象本身是富含业务逻辑的。
相关文章:
MVC和DDD的贫血和充血模型对比
文章目录 架构区别MVC三层架构DDD四层架构 贫血模型代码示例 充血模型代码示例 架构区别 MVC三层架构 MVC三层架构是软件工程中的一种设计模式,它将软件系统分为 模型(Model)、视图(View)和控制器(Contro…...
如何利用AI提高内容生产效率?
如何利用AI提高内容生产效率? 简介:探讨如何通过AI技术提升内容生产的效率和质量。 方向一:自动化内容生成 自动化内容生成是一种利用人工智能技术来自动创建文本、图像、音频等内容的方法。 以下是一些常见的自动化内容生成方式: 基于…...
C++ stack、queue以及deque
1、stack和queue常用接口 严格来说栈和队列的实现是容器适配器 1、常用接口: 栈:top、push、pop、size、emptystack - C Reference (cplusplus.com) 队列:top、push、pop、swap、size、emptyqueue - C Reference (cplusplus.com) 2、deque&a…...
科沃斯,「扫地茅」荣光恐难再现
作者 | 辰纹 来源 | 洞见新研社 科沃斯恐怕已经很难再回到被市场誉为“扫地茅”时的荣光了。 不久前,科沃斯发布2023年财报,报告期内营业收入155亿,同比仅增长1.16%,归母净利润6.12亿元,同比下降63.96%,直…...
双向BFS算法学习
双向BFS算法学习 推荐练习题 力扣“127”题:单词接龙 “752”题:打开轮盘锁 这里推荐一篇力扣题解 双向BFS 这里使用打开轮盘锁的题干进行举例: 你有一个带有四个圆形拨轮的转盘锁。每个拨轮都有10个数字: ‘0’, ‘1’, ‘2’,…...
C++从入门到精通---模版
文章目录 泛型编程函数模版模版参数的匹配原则类模版类模版的定义格式类模版的实例化 总结 泛型编程 泛型编程是一种编程范式,旨在实现通用性和灵活性。它允许在编写代码时使用参数化类型,而不是具体的类型,从而使代码更加灵活和可重用。 在…...
Unity数据持久化之Json
Json概述 Json是什么? 全称:JavaScript对象简谱(JavaScript Object Notation) Json是国际通用的一种轻量级的数据交换格式 主要在网络通讯中用于传输数据,或本地数据存储和读取 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率 我们一般使用Json文件来…...
LeetCode 35.搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: 输入: nums [1,3,5,6], target 5 输出: 2 示例 2: 输入…...
速来get!多微信聚合聊天功能大揭秘!
随着网络时代的发展,微信成为了职场中不可或缺的沟通工具,很多人都有着多个微信号,而要想高效管理这些账号,那就少不了工具的帮忙。 通过微信管理系统,可以轻松实现多个微信号聚合聊天,提高沟通效率。 1、…...
【跟我学RISC-V】(一)认识RISC-V指令集并搭建实验环境
目录 写在前面 一、RISC-V指令集简介 1、什么是ISA 2、有哪些ISA 3、CISC和RISC 4、什么是RISC-V 1. RISC 的起源 2. RISC-I 和 RISC-II 3. RISC 发展和商业化 4. RISC-V 的诞生 5、RISC-V生态的特点 6、RISC-V指令集的特点 1. 开源 2. 社区化 3. 设计简洁 4. 模…...
如何使用google.protobuf.Struct?
google.golang.org/protobuf/types/known/structpb 包提供了一种方式来创建和操作 google.protobuf.Struct 类型的数据。google.protobuf.Struct 是一种灵活的数据类型,可以表示任何结构化数据。 以下是如何使用 structpb 包的一些示例: 创建 Struct&a…...
Vue3 + TS + Element-Plus 封装的 Dialog 弹窗组件
弹窗组件中自定义了header 增加了全屏,svg-icon 没有的话可能会报错,换成自己的图标就可以 <template><el-dialog:dialogHeight"dialogHeight":title"dialogTitle"class"dialog min-w-70"v-model"dialogVi…...
大数据技术概述_4.大数据的应用领域
1.制造业的应用 制造业目前正在向信息化和自动化的方向发展。在产品的设计、生产和销售中,越来越多的企业使用计算机辅助设计(CAD)、计算机辅助制造(CAM)等软件,数控机床、传感器等设备,物料需求…...
ABB RobotStudio学习记录(一)新建工作站
RobotStudio新建工作站 最近遇到 虚拟示教器和 Rapid 代码不能控制 视图中机械臂的问题,其实是由于机械臂和工作站不匹配。以下是解决方法。 名称版本Robot Studio6.08 新建一个”空工作站“; 在目标位置新建一个目标文件夹 C:\solution\test࿰…...
雷达通信一体化(含WCSP2023会议论文集学习)
雷达通信一体化,又称雷达通信融合(RADCOM),是一种新兴的技术,它将雷达(通常用于探测和跟踪目标)和无线通信(用于传输信息)的功能结合在一起。这种融合技术的主要目标是提…...
特斯拉擎天柱机器人:工厂自动化的未来
随着技术的进步,工业自动化已经逐步进入了一个新的纪元。特斯拉最近公布的擎天柱机器人Optimus的演示,不仅仅展示了一个高科技机器人的能力,更是向我们揭示了未来工厂的可能性。 特斯拉擎天柱机器人的功能展示 马斯克在最新的演示中向我们展…...
【管理咨询宝藏93】大型制造集团数字化转型设计方案
【管理咨询宝藏93】大型制造集团数字化转型设计方案 【格式】PDF版本 【关键词】国际咨询公司、制造型企业转型、数字化转型 【核心观点】 - 235页大型制造型集团数字化转型方案设计!细节非常详尽,图表丰富! - 系统架构必须采用成熟、具有国…...
【数学建模】天然肠衣搭配问题
2011高教社杯全国大学生数学建模竞赛D题 天然肠衣(以下简称肠衣)制作加工是我国的一个传统产业,出口量占世界首位。肠衣经过清洗整理后被分割成长度不等的小段(原料),进入组装工序。传统的生产方式依靠人工…...
Dockerfile实践java项目
目的:用java项目测试dockerfil部署(前提是安装好了docker) 部署准备文件如下 1. java项目 java项目demo地址 https://gitee.com/xiaoqu_12/dockerfileDemo.git 或者百度网盘直接下载打包好的jar包 链接:https://pan.baidu.com/s/…...
【管理咨询宝藏96】企业数字化转型的中台战略培训方案
本报告首发于公号“管理咨询宝藏”,如需阅读完整版报告内容,请查阅公号“管理咨询宝藏”。 【管理咨询宝藏96】企业数字化转型的中台战略培训方案 【格式】PDF版本 【关键词】SRM采购、制造型企业转型、数字化转型 【核心观点】 - 数字化转型是指&…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...
EasyRTC音视频实时通话功能在WebRTC与智能硬件整合中的应用与优势
一、WebRTC与智能硬件整合趋势 随着物联网和实时通信需求的爆发式增长,WebRTC作为开源实时通信技术,为浏览器与移动应用提供免插件的音视频通信能力,在智能硬件领域的融合应用已成必然趋势。智能硬件不再局限于单一功能,对实时…...
