|
|
3 týždňov pred | |
|---|---|---|
| .. | ||
| 3 týždňov pred | ||
| ICommand.cs | 3 týždňov pred | |
| ICommandHandler.cs | 3 týždňov pred | |
| ICommandHandlerWithResponse.cs | 3 týždňov pred | |
| ICommandWithResponse.cs | 3 týždňov pred | |
| IQuery.cs | 3 týždňov pred | |
| IQueryHandler.cs | 3 týždňov pred | |
| IRequest.cs | 3 týždňov pred | |
| ISender.cs | 3 týždňov pred | |
| README.md | 3 týždňov pred | |
MediatR 의존성 없이 CQRS 의도를 명확히 표현하는 인터페이스입니다. (2026-07 MediatR 제거 — 라이선스 회피 + 순수 C# 우선)
호출부는 ISender / IMediator를 주입받아 Send(command/query, ct)로 디스패치하며,
실제 디스패치는 Application/Messaging/Sender.cs(리플렉션 + 캐싱)가 처리합니다.
IRequest // 반환값 없는 요청 공통 마커
IRequest<TResponse> // 반환값 있는 요청 공통 마커
ISender / IMediator // 디스패처 (Sender가 구현, IMediator : ISender)
ICommand // 반환값 없음 (: IRequest)
ICommand<TResponse> // 반환값 있음 (: IRequest<TResponse>)
ICommandHandler<TCommand> // Task Handle(...)
ICommandHandler<TCommand, TResponse> // Task<TResponse> Handle(...)
IQuery<TResponse> // 항상 반환값 있음 (: IRequest<TResponse>)
IQueryHandler<TQuery, TResponse> // Task<TResponse> Handle(...)
TResponse는 Handler가 실제로 반환하는 타입입니다.Result<T>래핑 여부는 각 Command/Query가 직접 선언합니다. 예:ICommand<Result<int>>→ Handler는Task<Result<int>>반환.
public sealed record DeleteMemberCommand(int MemberID) : ICommand;
public sealed class Handler(IAppDbContext db)
: ICommandHandler<DeleteMemberCommand>
{
public async Task Handle(DeleteMemberCommand request, CancellationToken ct)
{
await db.Member
.Where(x => x.ID == request.MemberID)
.ExecuteDeleteAsync(ct);
}
}
public sealed record CreateMemberCommand(string Email, string Name) : ICommand<int>;
public sealed class Handler(IAppDbContext db)
: ICommandHandler<CreateMemberCommand, int>
{
public async Task<int> Handle(CreateMemberCommand request, CancellationToken ct)
{
var member = Member.Create(request.Email);
await db.Member.AddAsync(member, ct);
await db.SaveChangesAsync(ct);
return member.ID;
}
}
public sealed record GetMemberQuery(int MemberID) : IQuery<MemberResponse>;
public sealed class Handler(IAppDbContext db)
: IQueryHandler<GetMemberQuery, MemberResponse>
{
public async Task<MemberResponse> Handle(GetMemberQuery request, CancellationToken ct)
{
// ...
}
}
// 둘 다 동일하게 동작합니다 (IMediator : ISender)
var result = await sender.Send(new GetMemberQuery(memberID), ct);
await mediator.Send(new DeleteMemberCommand(memberID), ct);
AddApplication()이 리플렉션 스캔으로 ICommandHandler<> / ICommandHandler<,> / IQueryHandler<,> 구현체를 모두 Scoped 등록하고, ISender / IMediator(= 단일 Sender)를 등록합니다.Sender는 요청의 런타임 타입으로 Handler를 찾아(타입별 1회 생성 후 캐시) 호출하며, 처리 시간 / 느린 요청(500ms) / 실패를 로깅합니다.ICommand / IQuery 상속으로 결정됩니다. 요청은 둘 중 정확히 하나만 구현해야 하며, 아니면 명확한 예외로 즉시 실패합니다.| 작업 유형 | 사용 인터페이스 | 예시 |
|---|---|---|
| 생성 (Create) | ICommand<TResponse> |
CreateMemberCommand |
| 수정 (Update) | ICommand |
UpdateMemberCommand |
| 삭제 (Delete) | ICommand |
DeleteMemberCommand |
| 조회 (Get/Search) | IQuery<TResponse> |
GetMemberQuery |
Application/Messaging/Sender.csApplication/DependencyInjection.cs