--- applyTo: "src/Fengling.Member.Web/Application/Commands/**/*.cs" --- # 命令与命令处理器开发指南 ## 开发原则 ### 必须 - **命令定义**: - 使用 `record` 类型定义命令。 - 无返回值命令实现 `ICommand` 接口。 - 有返回值命令实现 `ICommand` 接口。 - 必须为每个命令创建验证器,继承 `AbstractValidator`。 - 命令处理器实现对应的 `ICommandHandler` 接口。 - 命令处理器中必须通过仓储存取聚合数据。 - 优先使用异步方法,所有仓储操作都应使用异步版本。 - 将 `CancellationToken` 传递给所有异步操作。 - 使用主构造函数注入所需的仓储或服务。 - **异常处理**: - 使用 `KnownException` 处理业务异常。 - 业务验证失败时抛出明确的错误信息。 ### 必须不要 - **事务管理**: - 不要手动调用 `SaveChanges`,框架会自动在命令处理完成后调用。 - 不要手动调用 `UpdateAsync`,如果实体是从仓储取出,则会自动跟踪变更。 - **仓储使用**: - 避免在命令处理器中进行复杂的查询操作(应使用查询端)。 - 仓储方法名不应是通用数据访问,而应体现业务意图。 - **重复引用**:无需重复添加 `GlobalUsings` 中已定义的 `using` 语句。 ## 文件命名规则 - 类文件应放置在 `src/Fengling.Member.Web/Application/Commands/{Module}s/` 目录下(以模块复数形式命名,避免命名空间与聚合类名冲突)。 - 命令文件名格式为 `{Action}{Entity}Command.cs`。 - 同一个命令及其对应的验证器和处理器定义在同一文件中。 - 不同的命令放在不同文件中。 ## 代码示例 **文件**: `src/Fengling.Member.Web/Application/Commands/Users/CreateUserCommand.cs` ```csharp using Fengling.Member.Domain.AggregatesModel.UserAggregate; using Fengling.Member.Infrastructure.Repositories; namespace Fengling.Member.Web.Application.Commands.Users; public record CreateUserCommand(string Name, string Email) : ICommand; public class CreateUserCommandValidator : AbstractValidator { public CreateUserCommandValidator() { RuleFor(x => x.Name) .NotEmpty() .WithMessage("用户名不能为空") .MaximumLength(50) .WithMessage("用户名不能超过50个字符"); RuleFor(x => x.Email) .NotEmpty() .WithMessage("邮箱不能为空") .EmailAddress() .WithMessage("邮箱格式不正确") .MaximumLength(100) .WithMessage("邮箱不能超过100个字符"); } } public class CreateUserCommandHandler(IUserRepository userRepository) : ICommandHandler { public async Task Handle(CreateUserCommand command, CancellationToken cancellationToken) { if (await userRepository.EmailExistsAsync(command.Email, cancellationToken)) { throw new KnownException("邮箱已存在"); } var user = new User(command.Name, command.Email); await userRepository.AddAsync(user, cancellationToken); return user.Id; } } ```