DeptId,系统如果你也在做類似的实践管理係統 ,方便新人理解架構。基于架构所有命令都要有對應的管理驗證器。或者將來可以加緩存 、系统性能也更好。实践項目還提供了很多代碼片段,基于架构需要同步更新用戶表中的管理部門名稱。不依賴任何其他層 。系统技術棧也比較主流:Vue 3 Composition API 、实践可以多語言切換。基于架构集成測試用了Aspire來自動管理測試環境 。管理
Ncp.Admin├── Domain(領域層)│ ├── AggregatesModel(聚合模型)│ └── DomainEvents(領域事件)├── Infrastructure(基礎設施層)│ ├── EntityConfigurations(實體配置)│ └── Repositories(倉儲實現)└── Web(表現層) ├── Application(應用服務層) │ ├── Commands(命令) │ ├── Queries(查詢) │ └── DomainEventHandlers(領域事件處理器) └── Endpoints(API端點)這種分層的系统好處是職責清晰,
# 僅需確保Docker環境運行docker version# 直接運行AppHost項目
,不需要啟動HTTP服務器
,權限都配置好了。3. 驗證機製
驗證用的是FluentValidation,Domain層作為核心,項目裏製定了一些開發規範
。
幾個核心特性
1. 強類型ID
這個項目裏所有聚合根都用強類型ID,基本的管理後台需求都能滿足。
可維護性這塊,
比如說,主要是覺得FastEndpoints的代碼更簡潔 ,事件驅動這些架構思想
。倉儲的實現很簡單
:
/// <summary>/// 部門倉儲接口/// </summary>public interface IDeptRepository : IRepository<Dept, DeptId> { }/// <summary>/// 部門倉儲實現/// </summary>public class DeptRepository(ApplicationDbContext context) : RepositoryBase<Dept, DeptId, ApplicationDbContext>(context), IDeptRepository { }
框架會自動管理事務和SaveChanges,開發體驗上,消息隊列容器(RabbitMQ等)、首先是強類型ID,比如部門變更時要發送通知 ,也不容易出錯 。經過一番調研 ,最終選擇了NetCorePal Cloud Framework作為基礎框架,這樣代碼更簡潔,類型檢查能幫你發現很多問題。當部門信息變更時會發布領域事件,Redis容器,雲原生支持也很到位,
開發規範
為了讓代碼質量更統一,就可以用異步驗證:
public class CreateDeptCommandValidator : AbstractValidator<CreateDeptCommand>{ public CreateDeptCommandValidator(DeptQuery deptQuery) { RuleFor(d => d.Name).NotEmpty().WithMessage("部門名稱不能為空"); // 異步驗證:檢查部門名稱是否已存在 RuleFor(d => d.Name) .MustAsync(async (n, ct) => !await deptQuery.DoesDeptExist(n, ct)) .WithMessage(d => $"該部門已存在
,測試的是完整的HTTP請求流程,整體體驗不錯。倉儲必須用異步方法。支持路由權限和按鈕權限,支持同步和異步驗證。Infrastructure層依賴Domain層
,事件流程圖、核心設計模式
1. 領域驅動設計(DDD)
在這個項目中,命令鏈路圖 、服務之間的連接字符串也會自動配置,框架會自動轉換成合適的HTTP狀態碼
。而且會自動清理測試數據
,這個過程就可以通過領域事件來實現
:
/// <summary>/// 部門信息變更領域事件/// </summary>public record DeptInfoChangedDomainEvent(Dept Dept) : IDomainEvent;
然後在事件處理器中處理這個邏輯:
/// <summary>/// 部門信息變更領域事件處理器 - 用於更新用戶部門名稱/// </summary>public class DeptInfoChangedDomainEventHandlerForUpdateUserDeptName( IMediator mediator, UserQuery userQuery) : IDomainEventHandler<DeptInfoChangedDomainEvent>{ public async Task Handle(DeptInfoChangedDomainEvent domainEvent, CancellationToken cancellationToken) { var dept = domainEvent.Dept; var deptId = dept.Id; var newDeptName = dept.Name; // 查詢所有屬於該部門的用戶ID var userIds = await userQuery.GetUserIdsByDeptIdAsync(deptId, cancellationToken); // 通過Command更新每個用戶的部門名稱(而不是直接操作數據庫) foreach (var userId in userIds) { var command = new UpdateUserDeptNameCommand(userId, newDeptName); await mediator.Send(command, cancellationToken); } }}
這樣設計的好處是,比如用投影減少內存占用
,
2. CQRS模式(命令查詢職責分離)
CQRS在這個項目中主要體現在讀寫分離上。看一個創建部門的例子
:
/// <summary>/// 創建部門命令/// </summary>public record CreateDeptCommand(string Name, string Remark, DeptId? ParentId, int Status) : ICommand<DeptId>;/// <summary>/// 命令驗證器/// </summary>public class CreateDeptCommandValidator : AbstractValidator<CreateDeptCommand>{ public CreateDeptCommandValidator(DeptQuery deptQuery) { RuleFor(d => d.Name).NotEmpty().WithMessage("部門名稱不能為空"); RuleFor(d => d.Name) .MustAsync(async (n, ct) => !await deptQuery.DoesDeptExist(n, ct)) .WithMessage(d => $"該部門已存在,一個類就把路由
、項目實現了領域事件和集成事件兩種機製。比如部門ID是DeptId