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

.NET Core充血模型

文章目录

  • 前言
  • 一、核心概念
  • 二、实现步骤
    • 1.创建领域对象(含行为)
    • 2.工厂方法(控制创建)
    • 3.值对象封装业务概念
    • 4.仓储接口(持久化抽象)
    • 5.应用服务(协调领域对象)
  • 三、关键实践
    • 1.禁用公共setter
    • 2.领域事件处理
    • 3.EF Core配置
    • 4.业务规则验证
  • 四、对比贫血模型
  • 五、常见陷阱
  • 总结


前言

充血模型领域驱动设计DDD)中的一种重要模式,它将业务逻辑封装在实体内部,使实体成为包含行为和数据的完整领域对象。

一、核心概念

  • 充血模型:实体类不仅包含数据,还包含业务逻辑。
  • 贫血模型:实体类仅包含数据,业务逻辑由服务层处理。
  • 领域驱动设计DDD):强调领域模型是系统的核心,通过聚合根、值对象等模式构建复杂业务系统。

二、实现步骤

1.创建领域对象(含行为)

  1. 代码如下(示例):
    public class Order
    {// 内部状态(私有set)public int Id { get; private set; }public OrderStatus Status { get; private set; } = OrderStatus.Pending;public decimal TotalAmount { get; private set; }private readonly List<OrderItem> _items = new();public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly();// 核心业务行为public void AddItem(Product product, int quantity){// 业务规则校验if (Status != OrderStatus.Pending)throw new InvalidOperationException("无法修改已发货订单");if (quantity <= 0)throw new ArgumentException("数量必须为正数");// 领域逻辑计算var item = new OrderItem(product, quantity);_items.Add(item);UpdateTotalAmount();}public void Ship(){if (Status != OrderStatus.Pending)throw new InvalidOperationException("订单已发货");if (_items.Count == 0)throw new InvalidOperationException("无法发货空订单");Status = OrderStatus.Shipped;}private void UpdateTotalAmount() => TotalAmount = _items.Sum(i => i.Subtotal);
    }public class OrderItem
    {internal OrderItem(Product product, int quantity){Product = product;Quantity = quantity;}public Product Product { get; }public int Quantity { get; }public decimal Subtotal => Product.Price * Quantity;
    }
    

2.工厂方法(控制创建)

  1. 代码如下(示例):
    public class Order
    {// 私有构造函数,防止外部直接实例化private Order() { }// 工厂方法创建订单public static Order Create(string customerName){if (string.IsNullOrWhiteSpace(customerName))throw new ArgumentNullException(nameof(customerName), "客户名称不能为空");return new Order{Id = Guid.NewGuid(),OrderDate = DateTime.UtcNow,CustomerName = customerName,Status = OrderStatus.Created};}
    }
    

3.值对象封装业务概念

  1. 示例代码
    public record Address
    {public string Street { get; init; }public string City { get; init; }// 业务行为public bool IsDomestic() => Country == "USA";
    }
    

4.仓储接口(持久化抽象)

  1. 示例代码
    public interface IOrderRepository
    {Order GetById(int id);void Save(Order order);
    }// 实现(EF Core)
    public class OrderRepository : IOrderRepository
    {private readonly MyDBContext _context;public OrderRepository(MyDBContext context) => _context = context;public Order GetById(int id) => _context.Orders.Include(o => o.Items).FirstOrDefault(o => o.Id == id);public void Save(Order order){if (order.Id == 0)_context.Orders.Add(order);_context.SaveChanges();}
    }
    

5.应用服务(协调领域对象)

  1. 代码示例:

    public class OrderService
    {private readonly IOrderRepository _orderRepository;public OrderService(IOrderRepository orderRepository) => _orderRepository = orderRepository;public void ProcessOrder(int orderId){var order = _orderRepository.GetById(orderId);order.Ship(); // 调用领域行为_orderRepository.Save(order);}
    }
    

三、关键实践

1.禁用公共setter

  • 使用私有set或init-only属性

    public decimal TotalAmount { get; private set; }
    

2.领域事件处理

  • 实现领域事件解耦业务逻辑
    public class Order
    {public event Action<Order> Shipped;public void Ship(){// ... 发货逻辑Shipped?.Invoke(this);}
    }
    

3.EF Core配置

  • 处理私有字段映射
builder.Property(r => r.TotalAmount).HasField("totalAmount");

4.业务规则验证

  • 在方法内部实现原子校验

    public void AddItem(Product product, int quantity)
    {if (product.IsDiscontinued)throw new DomainException("无法添加已停产的产品");// ...
    }
    

四、对比贫血模型

充血模型贫血模型
order.Ship()orderService.Ship(order)
业务逻辑内聚在领域对象业务逻辑分散在Service类
对象自洽保证一致性需额外校验对象状态
符合面向对象设计过程式编程风格

五、常见陷阱

  • 领域服务过度膨胀 → 将核心逻辑移回实体

  • 暴露内部状态 → 通过方法控制状态变更

  • 忽略聚合根边界 → 通过根实体修改子实体

  • 依赖基础设施 → 领域对象不直接访问数据库


总结

领域对象方法应仅使用自己的属性/参数进行计算,避免依赖外部服务(可通过方法参数注入依赖)。
通过充血模型,可显著提升代码可维护性和业务表达能力,特别适合复杂业务系统的领域驱动设计(DDD)实现。

http://www.lryc.cn/news/574563.html

相关文章:

  • go语言 *和
  • 配置自己的NTP 服务器做时间同步
  • HTML炫酷烟花
  • 知乎-AI大模型全栈工程师课程1~12期(已完结)
  • 通义灵码2.5智能体模式实战———集成高德MCP 10分钟生成周边服务地图应用
  • 同城信息发布 app 交流互动系统框架设计
  • WPF 几种绑定 (笔记)
  • maven:迁移到 Maven Central 后 pom.xml的配置步骤
  • pdf转图片(png,jpg)的python脚本
  • 发布 npm 包完整指南(含账号注册、发布撤销与注意事项)
  • 【云计算】云测试
  • 成交量流动策略
  • Unity3D仿星露谷物语开发70之背景音乐
  • 软件测试报告机构如何保障软件质量与安全性?作用有哪些?
  • 使用 PyFluent 自动化 CFD
  • 用 Python 打造立体数据世界:3D 堆叠条形图绘制全解析
  • 【Pandas】pandas DataFrame update
  • 华为云Flexus+DeepSeek征文 | 华为云MaaS平台上的智能客服Agent开发:多渠道融合应用案例
  • 《C++初阶之类和对象》【初始化列表 + 自定义类型转换 + static成员】
  • 在 centos7部署kubephere
  • TortoiseSVN 安装教程
  • prometheus+grafana+MySQL监控
  • 云原生周刊:Argo CD v3.1 正式发布
  • 工程优化——WebSocket、WSS(WebSocket Secure)和SSE(Server-Sent Events)通信对比
  • Jenkins+Jmeter+Ant接口持续集成
  • 【人工智能agent】--dify实现文档内容的自动抽取
  • 论文阅读:2025 arxiv Qwen3 Technical Report
  • 【论文阅读 | CVPRW 2023 |CSSA :基于通道切换和空间注意力的多模态目标检测】
  • 【AI时代速通QT】第三节:Linux环境中安装QT并做测试调试
  • Starrocks 低基数全局字典优化