讲师中心 微信公众号
AI工具推荐 视频效率加速

C# DDD聚合根实现方法 C#如何设计和实现Aggregate Root

星涛小哥_4330

星涛小哥_4330

发布时间:2026-02-02 12:33:58

|

605人浏览过

|

来源于php中文网

原创

聚合根必须显式控制内部实体生命周期,通过行为方法封装状态变更、ID构造时生成且不可变、仓储仅操作聚合根、事件溯源下需从事件流重建状态。

c# ddd聚合根实现方法 c#如何设计和实现aggregate root

聚合根必须显式控制内部实体的生命周期

DDD 中的聚合根不是靠注释或文档约定出来的,而是通过代码强制约束:外部只能持有聚合根的引用,不能直接 new 或访问内部实体。常见错误是让仓储返回 OrderItem 实体,或者在应用层调用 order.Items.Add(...) 后再保存——这会绕过聚合根的不变量校验。

正确做法是只暴露方法而非集合属性:

public class Order : AggregateRoot
{
    private readonly List<OrderItem> _items = new();
    
    // ❌ 错误:返回可修改集合引用
    // public IReadOnlyList<OrderItem> Items => _items.AsReadOnly();
    
    // ✅ 正确:通过行为方法封装变更
    public void AddItem(ProductId productId, int quantity)
    {
        if (quantity <= 0) throw new ArgumentException("Quantity must be positive");
        if (_items.Count >= 100) throw new DomainException("Max 100 items per order");
        
        _items.Add(new OrderItem(productId, quantity));
        AddDomainEvent(new OrderItemAddedDomainEvent(Id, productId, quantity));
    }
}
  • 所有对内部状态的修改必须走聚合根定义的方法,确保每次变更都经过业务规则检查
  • 不要暴露 _items 的 setter 或可写集合接口(如 IList<t></t>)
  • 若需查询内部数据,返回不可变副本(IReadOnlyList<t></t>)或 DTO,而非原始引用

聚合根 ID 必须在构造时生成且不可变

聚合根的 ID 是其身份标识,也是仓储定位聚合的唯一依据。如果允许运行时修改 Id,会导致仓储找不到聚合、事件溯源错乱、并发冲突无法识别等问题。

典型错误包括:用默认构造函数 + 属性赋值、从数据库读取后重设 ID、使用 ORM 的延迟加载代理覆盖 ID 字段。

  • ✅ 构造函数中生成 ID:public Order(OrderId id) : base(id ?? OrderId.New()) { }
  • ✅ 使用只读属性:public OrderId Id { get; } // 不是 get; set;
  • ❌ 避免 EF Core 的 ValueConverter 或自定义 setter 干预 ID 赋值逻辑
  • ⚠️ 若用 EF Core,需配置 HasIndex(e => e.Id).IsUnique() 并禁用 ID 的 update 操作

仓储接口只能操作聚合根,不能暴露内部实体的 CRUD

仓储(Repository)是聚合根的「专属门面」,它的泛型参数必须是聚合根类型,比如 IRepository<order></order>。一旦出现 IRepository<orderitem></orderitem>,说明聚合边界设计失败,或者把技术存储细节泄露到了领域层。

GateCrash Forms
GateCrash Forms

以 CLI 为先的表单构建器,采用 BYOK 理念。通过JSON Schema 生成美观的 HTML 表单,使用您自己的 SMTP 服务器处理提交,存储响应...

下载

错误信号包括:

  • 应用服务里调用 _orderItemRepo.FindById(...)
  • 仓储方法返回 IQueryable<orderitem></orderitem> 或接受 OrderItem 作为参数
  • 在仓储实现中手动组装 Order 和它的 OrderItem 列表(应由聚合根自己重建)

正确做法是让仓储只负责整个聚合的加载与保存:

public interface IRepository<TAggregate> where TAggregate : AggregateRoot
{
    Task<TAggregate> GetByIdAsync(AggregateId id, CancellationToken ct = default);
    Task SaveAsync(TAggregate aggregate, CancellationToken ct = default);
}

// 应用服务中:
var order = await _orderRepo.GetByIdAsync(orderId);
order.AddItem(productId, 2); // 在内存中变更
await _orderRepo.SaveAsync(order); // 一次性持久化整个聚合

事件溯源场景下,聚合根需基于事件流重建状态

如果采用事件溯源(Event Sourcing),聚合根不能依赖数据库快照,而必须能从一组事件中完整重建自身。这意味着构造函数要支持「空 ID + 重放事件」模式,且所有状态变更必须由事件驱动。

  • 聚合根需提供静态工厂方法:public static Order Rehydrate(IEnumerable<idomainevent> events)</idomainevent>
  • 每个事件处理方法(如 When(OrderPlaced e))只做状态变更,不触发新业务逻辑
  • 避免在 When 方法中调用外部服务、发邮件、查数据库——这些属于应用层职责
  • EF Core 等 ORM 通常不适用于事件溯源;推荐用专用事件存储(如 EventStoreDB)或自建 append-only 表

最易被忽略的是:事件类本身必须是不可变的纯数据容器,且版本号、时间戳、聚合 ID 等元信息应在基础设施层注入,而非由聚合根构造。

热门AI工具

更多
PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3188

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

5089

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

3829

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

466

2026.01.19

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

391

2026.03.13

append用法
append用法

append是一个常用的命令行工具,用于将一个文件的内容追加到另一个文件的末尾。想了解更多append用法相关内容,可以阅读本专题下面的文章。

598

2023.10.25

python中append的用法
python中append的用法

在Python中,append()是列表对象的一个方法,用于向列表末尾添加一个元素。想了解更多append的更多内容,可以阅读本专题下面的文章。

1476

2023.11.14

python中append的含义
python中append的含义

本专题整合了python中append的相关内容,阅读专题下面的文章了解更多详细内容。

2744

2025.09.12

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

20

2026.10.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
uni-app从入门到实战教程
uni-app从入门到实战教程

共0课时 | 0人学习

uni-app x harmony开发指南
uni-app x harmony开发指南

共0课时 | 0人学习

uni-app鸿蒙运行和发行
uni-app鸿蒙运行和发行

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn