Add CQRS Command
When the user asks to add a new command, scaffold the following files in the correct microservice. Ask which service if ambiguous.
If the command involves non-trivial FluentValidation rules (async validators, cross-field validation, collection rules) or WolverineFx middleware, use context7 to look up the current docs before writing.
Arguments
{Name}-- Command name in PascalCase (e.g.,CreateOrder,AssignRole){Service}-- Target microservice. Ask if ambiguous.
Configuration
Read cloudstack.json from the project root at the start of execution. Extract:
NAMESPACE=project.namespace(default: detect from*.slnname or first*.csprojroot namespace)SERVICES=backend.services[](default: discover fromsrc/*/directories containing.Application/subfolders)SOLUTION=backend.solutionPath(default: find*.slninsrc/)
If cloudstack.json does not exist, auto-detect by scanning the project structure.
Prerequisites
- The target microservice must exist under
src/ - An Endpoints file should exist at
Host/Endpoints/(if not, create one) - If the command operates on an entity that doesn't exist yet, run
/add-entityfirst
1. Command Record (Application/Commands/{CommandName}.cs)
namespace {Namespace}.{Service}.Application.Commands;
public record {Name}Command({parameters});
2. Response Record (same file or Application/Contracts/)
public record {Name}Response({response fields});
3. Handler (Application/Commands/{Name}Handler.cs)
namespace {Namespace}.{Service}.Application.Commands;
public class {Name}Handler
{
// Constructor-inject repositories and services
public async Task<Result<{Name}Response>> Handle({Name}Command command, CancellationToken cancellationToken)
{
// 1. Validate business rules
// 2. Create/modify domain entities via factory methods or aggregate methods
// 3. Persist via repository
// 4. Return Result.Success or Result.Failure with SCREAMING_SNAKE error code
}
}
4. Validator (Application/Validators/{Name}Validator.cs)
namespace {Namespace}.{Service}.Application.Validators;
public class {Name}Validator : AbstractValidator<{Name}Command>
{
public {Name}Validator()
{
// RuleFor(x => x.Field).NotEmpty().WithErrorCode("ERROR_CODE");
}
}
5. Endpoint (add to existing Host/Endpoints/{Domain}Endpoints.cs)
group.MapPost("/{route}", {Name})
.WithName("{Name}")
.WithSummary("...")
.WithDescription("...")
.Produces<{Name}Response>(StatusCodes.Status201Created)
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status409Conflict);
private static async Task<IResult> {Name}(
[FromBody] {Name}Request request,
[FromServices] {Name}Handler handler,
CancellationToken cancellationToken)
{
var command = new {Name}Command(...);
var result = await handler.Handle(command, cancellationToken);
return result.ToHttpResult();
}
6. DI Registration
Register the handler in the service's AddInfrastructure or Program.cs:
services.AddScoped<{Name}Handler>();
Checklist
- Command record is immutable
- Handler returns
Result<T>, no thrown exceptions for business logic - Error codes are SCREAMING_SNAKE_CASE
- Validator uses FluentValidation
- Endpoint has OpenAPI metadata (WithName, WithSummary, Produces)
- Handler registered in DI
- CancellationToken propagated through all async calls
Output
After scaffolding, report:
## Scaffolded: {Name}Command
Files created/modified:
- `src/{Service}/{Service}.Application/Commands/{Name}Command.cs`
- `src/{Service}/{Service}.Application/Commands/{Name}Handler.cs`
- `src/{Service}/{Service}.Application/Validators/{Name}Validator.cs`
- `src/{Service}/{Service}.Host/Endpoints/{Domain}Endpoints.cs` (modified)
- `src/{Service}/{Service}.Host/Program.cs` or DI registration (modified)
Next: Run `/run-tests` to verify, or `/add-query` if you also need a read endpoint.
Error Handling
- Endpoints file doesn't exist: Create a new
{Domain}Endpoints.cswith the route group boilerplate. - DI registration file not found: Add
services.AddScoped<{Name}Handler>()directly toProgram.cs. - Namespace conflicts: Check existing commands in the folder before creating -- prompt the user if a similar command already exists.
Related Skills
/add-entityif the command creates a new entity type/add-event-handlerto react to domain events raised by this command/add-queryif you also need a read endpoint for the same resource