广告
您当前的位置: 首页 >  技术 >  编程开发

领域建模实践:解密贫血模型(Anemic Model)与充血模型(Rich Model)的本质对立

作者:CoderWang 时间:2026-07-05 阅读数:3人阅读

在主流的企业级 Java 开发中,开发人员习惯了使用传统的“三层架构”(Controller-Service-DAO)。在这种传统的开发模式中,所谓的领域实体(如 UserOrder)通常只包含私有属性以及一堆自动生成的 Getter 和 Setter 方法,没有任何实质性的业务逻辑。所有的业务逻辑、校验规则都被塞进了臃肿的 Service 类中。

这种**“数据与行为分离”**的建模方案,在领域驱动设计(DDD)中被称为**贫血模型(Anemic Domain Model)**。而 DDD 所极力推崇的是将数据和紧密关联的行为强内聚在一起的**充血模型(Rich Domain Model)**。

本文将系统拆解贫血模型与充血模型的概念边界、面向对象设计差异以及充血模型的工程落地实践。

一、 核心范式对比:贫血模型 vs. 充血模型

我们通过多维指标对比直观呈现两种建模思想的对立本质:

特征维度贫血模型 (Anemic Model)充血模型 (Rich Model)
业务表达力低。对象仅用于承载数据库行数据,业务语义隐式地散落于 Service 层的复杂代码中。**极高**。对象的方法直观表达商业业务概念(如 order.pay() 替代 order.setStatus(PAY))。
面向对象本质**退化为面向过程**。Service 是过程(Processor),Entity 是结构体(Struct)。**真正的面向对象**。对象是具备行为、能够自治、守卫自身约束的业务主体。
高并发守卫差。由于状态暴露在 Setter 中,外部可以任意修改,核心不变性(Invariants)极易被破坏。**强守卫**。聚合根封闭属性,通过公共方法统一状态修改入口,内置规则拦截。
可测试性差。测试 Service 时必须 Mock 极其复杂的 DAO 依赖与外部 RPC 客户端。**极高**。充血模型是无状态、纯内存运行的业务实体,直接实例化即可进行单测。
---

二、 数据与行为的物理流转关系对比

我们通过物理调用拓扑清晰呈现两种开发范式的流量流转差异:

1. 贫血模型调用流

[ 客户端 ] ──> [ Service 业务包 ]
                    │  (Service 读取 Entity 属性, 在 Service 内部计算, 再 set 回去)
                    ├─ [ Entity (纯属性 DTO) ]
                    │
                    ▼
           [ DAO / Repository 保存 ]

2. 充血模型调用流

[ 客户端 ] ──> [ Service 编排包 ]
                    │  (Service 从 Repo 读取聚合根,直接调用聚合根的行为方法)
                    ▼
           [ 聚合根.executeBusiness() (内存完成状态修改与不变性保护) ]
                    │
                    ▼
           [ Repository.save(聚合根) ]
---

三、 代码实战:在基础设施层落地充血聚合设计

以下是一个严格符合 DDD 规范的“账户余额(Account)”充血聚合设计,强制捍卫“账户余额不能为负数”的不变性约束:

package com.company.domain.aggregate;

import java.math.BigDecimal;

/**
 * 账户聚合根:充血模型实现
 */
public class Account {
    private final String accountId;
    private BigDecimal balance; // 余额封闭,杜绝 Setter 暴露给外部

    public Account(String accountId, BigDecimal initialBalance) {
        if (initialBalance.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("初始余额不能为负数!");
        }
        this.accountId = accountId;
        this.balance = initialBalance;
    }

    /**
     * 业务行为:扣款(内置不变性守卫)
     */
    public void withdraw(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("扣款金额必须大于零!");
        }
        
        // 执行核心不变性校验
        BigDecimal newBalance = this.balance.subtract(amount);
        if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalStateException("账户余额不足,扣款失败!");
        }
        
        this.balance = newBalance; // 状态安全更新
    }

    public String getAccountId() { return accountId; }
    public BigDecimal getBalance() { return balance; }
}
---

四、 总结

贫血模型与充血模型的博弈,不仅是 DDD 战术设计的选择,更是面向对象程序设计范式的本源复归。

它通过**坚决取缔失血的 Setter 通道,将数据状态更新与核心业务校验紧紧包裹于聚合根的领域动作中,从底层消除了“数据脏变”的安全隐患**;并通过**将过程业务逻辑封装为可复用的内聚对象,让软件模型天然对应真实的商业概念**。掌握这套充血实体设计与工程整合红线,是重构失控的面条 Service 代码、防范软件系统熵增演进的核心看家本领!

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。

评论交流 (0)

正在加载评论...
头像

CoderWang

当你还撑不起你的梦想时,就要去奋斗。如果缘分安排我们相遇,请不要让她擦肩和过。我们一起奋斗!

微信