Instruction file imported from denis-peshkov/Cross.CQRS.EF (
.cursor/rules/101-backend-cqrs.mdc). Copyright stays with the author.
CQRS ΠΈ ΠΌΠΎΠ΄ΡΠ»ΠΈ
ΠΠΎΠ΄ΡΠ»ΠΈ
ΠΠΎΠ΄ ΠΎΡΠ³Π°Π½ΠΈΠ·ΠΎΠ²ΡΠ²Π°ΡΡ ΠΏΠΎ ΠΌΠΎΠ΄ΡΠ»ΡΠΌ Π² Web.Api/Modules/{ModuleName}/ (ΠΈΠ»ΠΈ ΡΠΊΠ²ΠΈΠ²Π°Π»Π΅Π½Ρ Modules/ Ρ library/sample).
Π‘ΡΡΡΠΊΡΡΡΠ° ΠΌΠΎΠ΄ΡΠ»Ρ:
Controllers/β ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΡ APIDtos/β Data Transfer ObjectsHandlers/β ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΈ ΠΊΠΎΠΌΠ°Π½Π΄ ΠΈ Π·Π°ΠΏΡΠΎΡΠΎΠ²
Modules/
ββ Vacancies/
ββ Controllers/
β ββ VacanciesController.cs
ββ Dtos/
β ββ SubmitResumeDto.cs
ββ Handlers/
ββ Create
β ββ SubmitResumeCommand.cs
β ββ SubmitResumeCommandHandler.cs
β ββ SubmitResumeCommandValidator.cs
ββ Read
ββ GetByIdQuery.cs
ββ GetByIdQueryHandler.cs
ββ GetByIdQueryValidator.cs
- ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΡ ΡΠΎΠ»ΡΠΊΠΎ Π΄Π»Ρ ΠΌΠ°ΡΡΡΡΡΠΈΠ·Π°ΡΠΈΠΈ, Π»ΠΎΠ³ΠΈΠΊΠ° Π² handlers
- CQRS Ρ MediatR
CQRS Ρ MediatR
- ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΠΊΠ»Π°ΡΡΡ Π΄Π»Ρ ΠΊΠΎΠΌΠ°Π½Π΄ (ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ Π΄Π°Π½Π½ΡΡ ) ΠΈ Π·Π°ΠΏΡΠΎΡΠΎΠ² (ΡΡΠ΅Π½ΠΈΠ΅)
- Π²ΡΡ Π±ΠΈΠ·Π½Π΅Ρ-Π»ΠΎΠ³ΠΈΠΊΠ° Π² ΠΎΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠ°Ρ , Π½Π΅ Π² ΠΊΠΎΠ½ΡΡΠΎΠ»Π»Π΅ΡΠ°Ρ
- Π²Π·Π°ΠΈΠΌΠΎΠ΄Π΅ΠΉΡΡΠ²ΠΈΠ΅ ΡΠ΅ΡΠ΅Π·
IMediator.Send()ΠΈIMediator.Send<T>()
public class SubmitResumeCommand : Command<bool>
{
public SubmitResumeDto Dto { get; set; }
}
public class SubmitResumeCommandHandler : CommandHandler<SubmitResumeCommand, bool>
{
protected override async Task<bool> HandleAsync(SubmitResumeCommand command, CancellationToken cancellationToken)
{
// Business logic
return true;
}
}
DTOs
- ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΠΊΠ»Π°ΡΡΡ Π΄Π»Ρ Π·Π°ΠΏΡΠΎΡΠΎΠ² ΠΈ ΠΎΡΠ²Π΅ΡΠΎΠ²
- FluentValidation Π΄Π»Ρ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ DTOs
- ΡΡΡΡΠΈΠΊΡ
Dto
public class SubmitResumeDto
{
public string Name { get; set; }
public string Email { get; set; }
public string Phone { get; set; }
public IFormFile Resume { get; set; }
}
Dependency Injection
- constructor injection
- ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ Π΄Π»Ρ Π²ΡΠ΅Ρ Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠ΅ΠΉ
ΠΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π²Π²ΠΎΠ΄Π°
Π‘Π΅ΡΠ²Π΅Ρ β ΠΈΡΡΠΎΡΠ½ΠΈΠΊ ΠΈΡΡΠΈΠ½Ρ Π΄Π»Ρ Π²Ρ ΠΎΠ΄ΡΡΠΈΡ ΠΊΠΎΠΌΠ°Π½Π΄ ΠΈ Π·Π°ΠΏΡΠΎΡΠΎΠ². ΠΠ»ΠΈΠ΅Π½Ρ ΠΌΠΎΠΆΠ΅Ρ Π΄ΡΠ±Π»ΠΈΡΠΎΠ²Π°ΡΡ Π΄Π»Ρ UX, ΡΠ΅ΡΠ²Π΅Ρ ΠΎΠ±ΡΠ·Π°Π½ Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ ΡΠ°ΠΌ.
| Π’ΠΈΠΏ ΠΏΠΎΠ»Ρ | ΠΡΠΊΠ΅Π½Π΄ |
|---|---|
Trim (Π² ΡΠΈΠ»ΡΡΡΠ΅; lower case β ΠΏΠΎ ΠΏΠΎΠ»ΠΈΡΠΈΠΊΠ΅ API Π² ΡΠΈΠ»ΡΡΡΠ΅ ΠΈΠ»ΠΈ Π΄ΠΎΠΌΠ΅Π½Π΅) |
|
| OTP / code | Trim |
| ΠΠΌΡ, ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΡ, provider | Trim |
| ΠΠ°ΡΠΎΠ»Ρ | Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΡΡΡ |
| Route id | N/A Π½Π° ΡΡΠΎΠ²Π½Π΅ InputNormalizer (Π΅ΡΠ»ΠΈ Π½ΡΠΆΠ½ΠΎ β Π² handler ΠΈΠ»ΠΈ Π²Π°Π»ΠΈΠ΄Π°ΡΠΎΡΠ΅) |
ΠΠ΄Π΅ Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·ΠΎΠ²Π°ΡΡ:
- ΠΡΠΈΠΌΠΈΡΠΈΠ²Ρ β
InputNormalizer(NormalizeTrimmedTextΠΈ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ ΠΏΠΎ ΠΌΠ΅ΡΠ΅ Π½Π°Π΄ΠΎΠ±Π½ΠΎΡΡΠΈ). Π’ΠΈΠΏΠΈΡΠ½ΡΠΉ ΠΏΡΡΡ Ρ ΠΎΡΡΠ°:server/Web.Api/Common/InputNormalizer.cs. - ΠΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ β
IRequestFilter<TCommand>/*CommandRequestFilterΡΡΠ΄ΠΎΠΌ Ρ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ; ΡΠ΅ΠΏΠΎΡΠΊΠ° Cross.CQRS:RequestFilterBehaviorΠ΄ΠΎValidationBehavior. - ΠΠ΅ Π² handler β Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ Π΄ΠΎ Π²Π°Π»ΠΈΠ΄Π°ΡΠΈΠΈ, ΡΡΠΎΠ±Ρ
NotEmpty/EmailAddressΠ²ΠΈΠ΄Π΅Π»ΠΈ ΡΠΆΠ΅ ΠΎΡΠΈΡΠ΅Π½Π½ΡΠ΅ ΡΡΡΠΎΠΊΠΈ.
ΠΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ:
- domain (
UserService,CodeService) ΠΌΠΎΠΆΠ΅Ρ Π΄ΡΠ±Π»ΠΈΡΠΎΠ²Π°ΡΡ Π½ΠΎΡΠΌΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΠΎΠ² ΠΊΠ°ΠΊ ΡΡΡΠ°Ρ ΠΎΠ²ΠΊΡ - ΠΏΠ°ΡΠΎΠ»ΠΈ Π²
InputNormalizerΠ½Π΅ ΡΡΠΎΠ³Π°ΡΡ β ΡΠ²Π½ΠΎ Π² ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠΈ ΠΊ ΠΊΠ»Π°ΡΡΡ
// *CommandRequestFilter.cs
dto.Email = InputNormalizer.NormalizeTrimmedText(dto.Email);
dto.Code = InputNormalizer.NormalizeTrimmedText(dto.Code);
// dto.Password β do not normalize