fengling-member-service/.github/instructions/command.instructions.md

90 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
applyTo: "src/Fengling.Member.Web/Application/Commands/**/*.cs"
---
# 命令与命令处理器开发指南
## 开发原则
### 必须
- **命令定义**
- 使用 `record` 类型定义命令。
- 无返回值命令实现 `ICommand` 接口。
- 有返回值命令实现 `ICommand<TResponse>` 接口。
- 必须为每个命令创建验证器,继承 `AbstractValidator<TCommand>`
- 命令处理器实现对应的 `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<UserId>;
public class CreateUserCommandValidator : AbstractValidator<CreateUserCommand>
{
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<CreateUserCommand, UserId>
{
public async Task<UserId> 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;
}
}
```