领域驱动:贫血模型和充血模型

2023-10-08 21:59

本文主要是介绍领域驱动:贫血模型和充血模型,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

什么是领域驱动

领域模型是通过识别领域对象与行为来连接现实主体与操作之间的映射关系。对象行为的组织原则更体现面向对象对象设计思想,通过聚合,解耦抽象等方式达到系统的可复用,可维护,可扩展能力。

MVC

MVC 三层架构中 M 表示 model ,V 表示的是 View, C 表示的是 Controller, 也就是分成了三层: 数据层, 表示层,逻辑层。

  1. 模型: 负责存储系统的中心数据
  2. 视图:将数据显示给用户
  3. 控制器: 处理用户输入的信息,负责读取视图数据,控制用户输入,并向模型写入数据。

后端项目一般是分成三层: Repository 层,Service 层,Controller 层,其中,Repository 负责数据访问,Service 负责处理业务逻辑, Controller 负责暴露接口。

什么是贫血模型

贫血模型领域对象很简单,只有所有属性的 get/set 方式,与少量的属性值转换,不包含任何业务逻辑, 不关心数据对象的持久化,只用来做数据对象的承载和传递介质。

领域对象

@Entity
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class User {@Idprivate String userId;private String userName;private String password;private boolean isLock;
}

真正的业务逻辑在领域服务中负责实现,此服务中引入持久化仓库, 在业务逻辑之后持久化到仓库中,并可发布领域事件。

Service 层, 领域服务, 业务处理,使用领域对象进行数据承载。

public interface UserService {void create(User user);void edit(User user);void changePassword(String userId, String newPassword);void lock(String userId);void unlock(String userId);}@Service
public class UserServiceImpl implements UserService {@Autowiredprivate UserRepository repo;@Overridepublic void edit(User user) {User dbUser = repo.findById(user.getUserId()).get();dbUser.setUserName(user.getUserName());repo.save(dbUser);// 发布领域事件 ...}@Overridepublic void lock(String userId) {User dbUser = repo.findById(userId).get();dbUser.setLock(true);repo.save(dbUser);// 发布领域事件 ...}// ... 省略完整代码
}

再看个贫血模型的具体例子:

// Controller+VO(View Object) //
public class UserController {private UserService userService; //通过构造函数或者IOC框架注入public UserVo getUserById(Long userId) {UserBo userBo = userService.getUserById(userId);UserVo userVo = [...convert userBo to userVo...];return userVo;}
}public class UserVo {//省略其他属性、get/set/construct方法private Long id;private String name;private String cellphone;
}// Service+BO(Business Object) //
public class UserService {private UserRepository userRepository; //通过构造函数或者IOC框架注入public UserBo getUserById(Long userId) {UserEntity userEntity = userRepository.getUserById(userId);UserBo userBo = [...convert userEntity to userBo...];return userBo;}
}public class UserBo {//省略其他属性、get/set/construct方法private Long id;private String name;private String cellphone;
}// Repository+Entity //
public class UserRepository {public UserEntity getUserById(Long userId) { //... }
}public class UserEntity {//省略其他属性、get/set/construct方法private Long id;private String name;private String cellphone;
}

UserEntity 和 UserRepository 负责数据访问, UserBo 和 UserService 负责处理业务逻辑, UserVo 和 UserController 属于接口层。

这样 UserBo 中只包含属性数据,不包含业务逻辑的累,叫做贫血模式

传统的贫血模型其实很受欢迎, 贫血模型是面向过程的编程风格。

  • 开发系统业务比较简单,只要实现 CRUD 操作即可, 不需要费力设计充血模型, 其次贫血模型足够应对这种简单的业务操作。
  • 充血模型的设计比贫血模型更加有难度,充血模型是面向对象的编程风格,一开始就要想好,对数据要暴露哪些操作,定义哪些业务逻辑, 而不是和贫血模型一样,只要设计好模型,后续有什么功能需求,Service 就定义什么操作。
  • 思维固化,转型有成本,基于贫血模型开发多年,一下要转换成充血模型,领域驱动,有一定的学习成本。

充血模型

充血模型包含领域对象作用此对象的相关行为, 领域对象不仅好汉属性,还包含业务处理逻辑,同时也包含对象的持久化操作。

@Entity
@Data
@Builder
@AllArgsConstructor
public class User implements UserService {@Idprivate String userId;private String userName;private String password;private boolean isLock;// 持久化仓库@Transientprivate UserRepository repo;// 是否是持久化对象@Transientprivate boolean isRepository;@PostLoadpublic void per() {isRepository = true;}public User() {}public User(UserRepository repo) {this.repo = repo;}@Overridepublic void create(User user) {repo.save(user);}@Overridepublic void edit(User user) {if (!isRepository) {throw new RuntimeException("用户不存在");}userName = user.userName;repo.save(this);// 发布领域事件 ...}@Overridepublic void lock() {if (!isRepository) {throw new RuntimeException("用户不存在");}isLock = true;repo.save(this);// 发布领域事件 ...}}

领域模型的优点

  • 对象自治度很高,表达能力很强,非常适合复杂的业务逻辑的实现,可靠复用程度比较高。更符合面向对象的思想。
  • 当前充血模型不够存粹,将业务逻辑和持久化操作放在一块,有一定的耦合度。

在领域对象行为比较复杂的情况下, 需要多个行为共享对象状态时,充血模型表现更强。

充血模型2

上面的充血模型,不仅包含业务逻辑,还包含数据持久化,为解决业务逻辑不存粹的问题,可以将持久化操作移出去。

@Entity
@Data
@Builder
@AllArgsConstructor
public class User implements UserService {@Idprivate String userId;private String userName;private String password;private boolean isLock;// 是否是持久化对象@Transientprivate boolean isRepository;@Overridepublic void create(User user) {user.userId = UUID.randomUUID().toString();}@Overridepublic void edit(User user) {userName = user.userName;}@Overridepublic void lock() {isLock = true;}}@Service
public class UserManager {@Autowiredprivate UserRepository repo;public User findOne(String userId){return repo.findById(userId).get();}public void edit(User u) {User user = findOne(u.getUserId());user.edit(u);repo.save(user);// 发布领域事件 ...}public void lock(String userId) {User user = findOne(userId);user.lock();repo.save(user);// 发布领域事件 ...}
}
  • 去掉了持久化的入侵,业务更加存粹

什么场景选择充血模型

基于充血模型的 DDD 开发模式,更适合复杂的系统开发,比如包含各种利息计算模型,还款模型的复杂业务的金融系统。

领域驱动设计主要是用来指导如何解偶业务系统,划分业务模块, 定义业务领域模型及其交互。他的发展主要是由于微服务,微服务不仅仅要关注服务治理,监控,调用链追踪, API 网关等问题,还有个重要的问题,就是对公司业务进行拆分,形成微服务。对领域驱动设计关键在于对业务的熟悉理解程度,领域驱动设计的思想是: 轻 Service ,重 Domain。

我们平时开发时,大部分是 SQL 驱动,接到一个后端开发需求时,首先是去看接口如何对应到数据库中,要访问哪些表,哪些库,然后思考 SQL 如何写。这样会看到,类似的SQL 到处都是,通常为一个 需求写一个 SQL。

对于领域驱动,我们首先需要理清所有业务,包括定义模型包含的属性和方法,领域模型相当于可复用业务的中间层,新的需求需要基于之前定义好的业务来完成。

欢迎关注公众号:程序员开发者社区

在这里插入图片描述

参考资料
  • https://zhuanlan.zhihu.com/p/147879821
  • https://juejin.cn/post/6856635975666401288

这篇关于领域驱动:贫血模型和充血模型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/168263

相关文章

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

如何在本地部署 DeepSeek Janus Pro 文生图大模型

《如何在本地部署DeepSeekJanusPro文生图大模型》DeepSeekJanusPro模型在本地成功部署,支持图片理解和文生图功能,通过Gradio界面进行交互,展示了其强大的多模态处... 目录什么是 Janus Pro1. 安装 conda2. 创建 python 虚拟环境3. 克隆 janus

本地私有化部署DeepSeek模型的详细教程

《本地私有化部署DeepSeek模型的详细教程》DeepSeek模型是一种强大的语言模型,本地私有化部署可以让用户在自己的环境中安全、高效地使用该模型,避免数据传输到外部带来的安全风险,同时也能根据自... 目录一、引言二、环境准备(一)硬件要求(二)软件要求(三)创建虚拟环境三、安装依赖库四、获取 Dee

DeepSeek模型本地部署的详细教程

《DeepSeek模型本地部署的详细教程》DeepSeek作为一款开源且性能强大的大语言模型,提供了灵活的本地部署方案,让用户能够在本地环境中高效运行模型,同时保护数据隐私,在本地成功部署DeepSe... 目录一、环境准备(一)硬件需求(二)软件依赖二、安装Ollama三、下载并部署DeepSeek模型选

Golang的CSP模型简介(最新推荐)

《Golang的CSP模型简介(最新推荐)》Golang采用了CSP(CommunicatingSequentialProcesses,通信顺序进程)并发模型,通过goroutine和channe... 目录前言一、介绍1. 什么是 CSP 模型2. Goroutine3. Channel4. Channe

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G