KIM-JINO5 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
..
Email cfebf095d1 first commit 3 долоо хоног өмнө
ICommand.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
ICommandHandler.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
ICommandHandlerWithResponse.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
ICommandWithResponse.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
IQuery.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
IQueryHandler.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
IRequest.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
ISender.cs 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө
README.md 07924cf4fd checkpoint: Phase 0~1 payment fixes + tests, before Phase B mass removal 3 долоо хоног өмнө

README.md

Messaging 추상 인터페이스 (사내 CQRS 디스패처)

MediatR 의존성 없이 CQRS 의도를 명확히 표현하는 인터페이스입니다. (2026-07 MediatR 제거 — 라이선스 회피 + 순수 C# 우선)

호출부는 ISender / IMediator를 주입받아 Send(command/query, ct)로 디스패치하며, 실제 디스패치는 Application/Messaging/Sender.cs(리플렉션 + 캐싱)가 처리합니다.

📚 제공 인터페이스

마커 / 디스패처

IRequest                 // 반환값 없는 요청 공통 마커
IRequest<TResponse>      // 반환값 있는 요청 공통 마커
ISender / IMediator      // 디스패처 (Sender가 구현, IMediator : ISender)

Command (쓰기 작업)

ICommand                                    // 반환값 없음 (: IRequest)
ICommand<TResponse>                         // 반환값 있음 (: IRequest<TResponse>)
ICommandHandler<TCommand>                   // Task Handle(...)
ICommandHandler<TCommand, TResponse>        // Task<TResponse> Handle(...)

Query (읽기 작업)

IQuery<TResponse>                           // 항상 반환값 있음 (: IRequest<TResponse>)
IQueryHandler<TQuery, TResponse>            // Task<TResponse> Handle(...)

TResponse는 Handler가 실제로 반환하는 타입입니다. Result<T> 래핑 여부는 각 Command/Query가 직접 선언합니다. 예: ICommand<Result<int>> → Handler는 Task<Result<int>> 반환.

🎯 사용 방법

1. 반환값이 없는 Command

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);
    }
}

2. 반환값이 있는 Command

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;
    }
}

3. Query

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)
    {
        // ...
    }
}

호출 (Endpoint / Razor Page)

// 둘 다 동일하게 동작합니다 (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) / 실패를 로깅합니다.
  • Handler 라우팅은 클래스 이름이 아니라 ICommand / IQuery 상속으로 결정됩니다. 요청은 둘 중 정확히 하나만 구현해야 하며, 아니면 명확한 예외로 즉시 실패합니다.

✅ 적용 규칙

작업 유형 사용 인터페이스 예시
생성 (Create) ICommand<TResponse> CreateMemberCommand
수정 (Update) ICommand UpdateMemberCommand
삭제 (Delete) ICommand DeleteMemberCommand
조회 (Get/Search) IQuery<TResponse> GetMemberQuery

🎓 참고