当前位置: 首页 > news >正文

贫血模型的改进

贫血模型的改进

      • 引言
      • 贫血模型带来的问题
      • 如何改进——重新划分职责
        • 第一步:让实体“富有行为”
        • 第二步:明确Service的角色
      • Mapper层的本质与调用关系
        • 持久化Mapper(即通常理解的DAO/Repository实现):
      • 总结:改进后的分层调用关系

引言

开发中,domain里常常定义了一些只含有特定字段的实体类,而相应的业务处理全部放在service中。在《架构整洁之道》(以及《领域驱动设计》)的观点下,这种“只有字段的领域对象 + 包含所有逻辑的Service”的模式,被称为“贫血领域模型”。这通常意味着没有真正采用面向对象的架构,而只是在做“事务脚本”编程。

贫血模型带来的问题

在整洁架构中,业务逻辑属于领域层,而不属于应用层(Service)。

  • 违背了封装性: 实体应该保证自己的内部状态是合法的,并且通过方法来改变状态。如果实体只有getter/setter,那么任何Service都可以随意修改它,导致业务规则(如“订单金额不能为负数”)散落在各个Service中,难以维护。
  • 丧失了“告诉,不要问”原则: Service需要先询问实体的状态(order.getStatus() == PAID),然后做判断,再调用setter(order.setStatus(SHIPPED))。更好的做法是直接告诉实体:order.ship(),让实体自己判断状态是否允许发货。
  • 低内聚: 与某个实体相关的逻辑(如计算、验证)被分散在多个Service中,而不是集中在实体内部。

如何改进——重新划分职责

改进的核心思路是:将属于实体的逻辑放回实体,并严格区分应用层和领域层。

第一步:让实体“富有行为”

不要让User只是一个带id和name的struct或类。让它包含该对象固有的、通用的业务逻辑。

// 错误做法:贫血模型publicclassUser{privateStringname;privateStringstatus;// getters and setters...}// 正确做法:富领域模型publicclassUser{privateStringname;privateStringstatus;// 构造函数,确保对象创建时就合法publicUser(Stringname){if(name==null||name.length()<2){thrownewIllegalArgumentException("用户名过短");}this.name=name;this.status="ACTIVE";}// 行为方法:激活用户(实体自己控制状态流转)publicvoidactivate(){if("BLOCKED".equals(this.status)){thrownewIllegalStateException("已封禁用户无法激活");}this.status="ACTIVE";}// 业务方法:修改密码(不仅仅是setPassword,可能包含加密和校验)publicvoidchangePassword(StringoldPassword,StringnewPassword,PasswordEncoderencoder){// 这里可以调用encoder,但依赖是通过参数传入,避免实体依赖基础设施// ...}// 可以不提供setter,或者只提供受保护的setter给ORM框架}
第二步:明确Service的角色

通常将提到的Service细分为两层:

  1. 应用服务(Application Service)【即用例层】:
    • 职责: orchestrator(编排者)。它负责接收外部输入(DTO),验证权限,决定调用哪个领域对象,协调领域对象完成业务,然后调用基础设施(如Repository)保存结果。
    • 特点: 无业务逻辑,只有任务编排。它不应该写if/else判断业务规则,业务规则应该在领域对象内部。
  2. 领域服务(Domain Service):
    • 职责: 处理那些不适合放在某个具体实体中的逻辑。例如:两个账户之间的转账(涉及两个Account实体)、或者需要调用外部接口的复杂计算。
    • 特点: 属于领域层,操作的是领域对象。

Mapper层的本质与调用关系

在传统的三层架构(Controller-Service-Mapper)中,Mapper是持久化框架(如MyBatis)的一部分,通常由Service直接调用,负责将数据库记录映射成对象。但在整洁架构中,这个视角需要转变。

持久化Mapper(即通常理解的DAO/Repository实现):
  • 本质: 它属于基础设施层。
  • 调用者: 在整洁架构中,应用层或领域层不直接依赖Mapper接口。它们依赖的是Repository接口。
  • 机制(依赖倒置):
    • 在领域层定义UserRepository接口(这是一个抽象,声明了save(User user)等方法)。
    • 在基础设施层实现这个接口(MyBatisUserRepositoryImpl),这个实现内部会调用真正的 MyBatis Mapper 来进行CRUD。
    • 调用链: 应用层Service ->UserRepository接口(依赖注入) ->MyBatisUserRepositoryImpl-> MyBatis Mapper -> 数据库。
    • 图景:
      • Controller (请求进入)
      • -> Application Service (编排用例,调用Repository接口)
      • -> Repository 接口 (定义在Domain层)
      • -> Repository 实现 (在Infrastructure层,内部调用Mapper)
      • -> Mapper/ORM (真正读写数据库)

总结:改进后的分层调用关系

  1. Controller/Adapter: 接收HTTP请求,将DTO通过 DTO-Entity Mapper 转换成 领域对象(或直接传参给应用服务),调用 Application Service。
  2. Application Service (用例层):
    • 获取当前用户上下文,权限判断。
    • 通过 Repository接口 从数据库加载 领域对象(此时对象是活的,带有行为)。
    • 调用领域对象的业务方法(如order.completePayment(amount))。
    • 再次通过 Repository接口 保存领域对象。
  3. 领域层:定义领域模型和领域服务,执行核心业务逻辑,维护自身状态的正确性。定义Repository接口,获取和存储聚合根的契约。
  4. Infrastructure (含MyBatis Mapper/ORM):
    • 实现UserRepository接口。
    • 在实现类中,调用 MyBatis 的 Mapper 或 JPA 将内存中的领域对象状态同步回数据库。
    • 注意:如果使用JPA这类ORM,实体本身可能带有注解,这是为了映射表结构,此时JPA注解可以视为基础设施的一部分,但应尽量保持领域对象干净,可以用XML配置或代码生成的方式隔离。

通过这样分层,的领域层就变得独立且可测试了,不再依赖数据库和框架,让技术和业务分离,独自演化。


愿我都能在各自的领域里不断成长,勇敢追求梦想,同时也保持对世界的好奇与善意!

http://www.jsqmd.com/news/730497/

相关文章:

  • 人工智能之 RAG 知识详细解析
  • 基于PaddleOCR的自动化OCR技能开发:从原理到Copaw平台集成实践
  • VuePress光标点击特效插件:Canvas粒子动画实现与优化
  • 终极指南:如何用ViGEmBus在Windows上创建虚拟游戏手柄
  • 【Linux从入门到精通】第35篇:容器化技术预备——Docker安装与基本概念
  • 从“像素误差”到“结构感知”:SSIM如何重塑了我们对图像质量的认知?
  • Autovisor:当Python Playwright遇上智慧树,自动化学习不再是梦
  • 如何解决LenovoLegionToolkit启动异常:WMI接口故障终极指南
  • 大语言模型微调实战:从LoRA原理到ChatGPT定制化应用
  • nftables 规则的原子化更新
  • 中之网:构建“官网+短视频+AI大模型”全域营销矩阵,抢占电机行业智能搜索新蓝海
  • Excel高效使用技巧(五):效率倍增工具:宏/VBA入门与自动化场景实战
  • 别再让RS485模块偷电了!STM32低功耗项目实测与外围电路功耗优化指南
  • 2026年南京青少年心理咨询医院选择指南与服务解析 - 品牌排行榜
  • Bili2text:3步将B站视频转为文字稿,开启高效学习新篇章
  • ComfyUI-Manager终极指南:AI绘画插件一键管理,彻底告别安装烦恼
  • 2026年水果店加盟哪家靠谱?行业从业者经验分享 - 品牌排行榜
  • 终极Windows权限解锁指南:如何用RunAsTI获取TrustedInstaller系统最高权限
  • Excel插件:随机抽奖(抽签)
  • 2026年3月有名的冲孔加工生产厂家口碑推荐,防火软接/冲孔加工/消音冲孔板,冲孔加工生产厂家选哪家 - 品牌推荐师
  • 【R核心团队内部技术简报解密】:R 4.5 spatial stack重构原理、ABI兼容边界与2024 Q3必升关键提示
  • 2026年南京焦虑症心理咨询医院选择指南 - 品牌排行榜
  • 2026水果店加盟哪家好?从供应链到体验的全方位对比 - 品牌排行榜
  • 选购教师 D 类机构的技巧,师璞教师有优势吗? - mypinpai
  • Python高级应用系列(二十)Python高级特性全景总结与最佳实践
  • GitHub 热门项目 `modded-nanogpt` 实测:把“90 秒训练 124M”搬到 RTX 3090 后,先炸的不是显存,而是 Hopper 专用内核
  • 2026年3月叫号系统源头厂家推荐,叫号系统/医院排队叫号系统,叫号系统机构口碑推荐 - 品牌推荐师
  • 视觉计时器:解码视频中的物理时间密码
  • Krita-AI-Diffusion插件中文翻译功能的技术实现与架构解析
  • Dify 2026边缘节点安全加固白皮书:FIPS 140-3认证路径、TEE可信执行环境集成及国密SM4动态密钥轮转实现