<< All versions

Skill v1.0.0

currentLLM-judged scan90/100
whuanle/moai/moai-cqrs-backend
──Details
PublishedOctober 22, 2026 at 04:34 PM
Content Hashsha256:c92497bbf19e6804...
Git SHAce7188a449dc
──Files
Files (1 file, 4.2 KB)
SKILL.md4.2 KBactive
SKILL.md · 74 lines · 4.2 KB

version: "1.0.0" name: moai-cqrs-backend description: Backend CQRS three-layer code standards for MoAI (.NET 9, Maomi modules, MediatR, EF Core). Use when adding/modifying Commands, Queries, Handlers, Controllers, validators, or business rules in src/*. 仅限 MoAI 项目。Use only for the MoAI project.


MoAI 后端 CQRS 三层规范(L2)

PROJECT SCOPE

只服务 MoAI 后端 src/。REQUIRED REFERENCE:改代码前必读 [docs/cqrs-conventions.md](../../../docs/cqrs-conventions.md)(唯一真源,含完整代码模板与命名规范)。 本 skill 只写执行要点与实踩坑。

WHEN

  • 新增/修改 Command、Query、Handler、Controller、校验
  • 涉及 src/<module>/MoAI.<Module>.{Shared,Core,Api} 任一层的改动

WHAT

按三层依赖(Api → Core → Shared)产出合规范的 CQRS 代码,构建 0 错。

HOW

0. 先找范本

rg 找同域代码读真实实现。最佳完整范本:user-management(src/account/ 的 UpdateUserIsAdminCommand 全链路)。

1. 目录与命名(细则见真源 §目录结构/命名规范)

项规范
Command{动作}{实体}Command.cs,命名空间 MoAI.{Domain}.Commands
QueryQuery{实体}{描述}Command.cs,命名空间 MoAI.{Domain}.Queries
Handler{Command/Query名}Handler.cs;Command 在 Handlers/,Query 在 Queries/
Response{Query名}Response.cs / {Query名}ResponseItem.cs,在 Queries/Responses/
复杂模块可按子领域建子目录(参照 MoAI.Plugin.Shared/Classify/...)

2. Shared 层要点

  • Command/Query 都继承 IModelValidator<T>(FluentValidation 静态 Validate),提前拦截无效请求
  • ⚠️ 路由参数时序坑:{id} 由 Controller 回填,自动验证发生在回填之前——Validate 里只能校验请求体字段,校验路由回填字段 = 接口恒 400(oauthconnect PUT 实踩)
  • 需要用户上下文的命令继承 IUserIdContext(ContextUserId/ContextUserType);用不到就别继承
  • ⚠️ IUserIdContext 的两个属性必须加 `[JsonIgnore]`:否则会作为查询参数/请求体字段进入 OpenAPI,进而出现在 Kiota 生成的客户端里(查询型命令尤其明显)。仓库先例:PagedParamter 对内部属性同样加 [JsonIgnore]。Handler 侧读 request.ContextUserId,不要注入 IUserContextProvider
  • 分页继承 PagedParamter(上限 1000);写命令响应统一 EmptyCommandResponse
  • 公开成员全部中文 XML 注释(StyleCop 强制)

3. Core 层要点

  • Handler 构造注入 DatabaseContext + 领域服务;禁止注入 IUserContextProvider/UserContext(用户信息只能经 Command 的 IUserIdContext 传入)
  • 实体审计属性(CreateUserId/CreateTime/UpdateUserId/UpdateTime/IsDeleted)框架自动注入,不要手动赋值;软删除字段 IsDeleted 类型是 long(0=未删),查询记得 IsDeleted == 0,勿写成 bool
  • 目标保护依赖 DB 事实:root 判定 = setting 表 key="root" 的 value
  • 业务异常:throw new BusinessException("中文消息.") { StatusCode = 400/403/404/409 },禁止裸 500
  • ⚠️ 写用户相关数据后必须 `RemoveUserStateAsync` 失效 Redis 用户态(禁用/降权即时生效依赖此)

4. Api 层要点

  • Controller 只做门禁 + 转发,无业务逻辑
  • 角色门禁在 Controller(EnsureAdminAsync/EnsureRootAsync 私有方法,查 GetUserStateAsync,非 403 即抛);目标保护在 Handler
  • 路由参数显式回填 + _userContextProvider.SetUserContext(cmd) 后再 _mediator.Send
  • 用户上下文只经 IUserContextProvider.GetUserContext(),不直接注入 UserContext

5. 模块注册

三层各有 {Domain}SharedModule/{Domain}CoreModule/{Domain}ApiModule(IModule,Core 用 [InjectModule<...>] 声明依赖,模板见真源 §模块注册)。

REFERENCE

正例:UpdateUserIsAdminCommand 全链路(Shared 定义 → Handler 目标保护+缓存失效 → Controller EnsureRoot+回填)。

LIMITS

  • 不含前端规范(L2-code-standards/moai-frontend-ui);审查清单(L3-fix-standards/moai-cqrs-review)
  • 不做迁移/DDL、部署
All versions