MediatR을 사용하여 .NET Core에서 CQRS 구현

개요



MS docs에는 CQRS 및 DDD를 적용한 구현 패턴에 대한 기사가 있습니다.
htps : // / cs. mic로소 ft. 코 m / 쟈 jp / t t t / r 치 테 c 얽힌 / 미 c 로세 r ㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜㅜ



거기에서는 MediatR이라는 라이브러리를 이용해 CQRS의 구현을 실시하고 있습니다.
이번에는 자기 이해의 의미도 담아 도메인 모델과 Web API 구현 샘플을 작성해 보겠습니다.

또한 CQRS와 DDD에 대한 자세한 해설은 전문 여러분에게 맡깁니다.
(※) 필자도 공부중의 몸입니다. . .

코드 샘플



샘플 코드를 Github에 올리고 있습니다.
htps : // 기주 b. 코 m / 쵸시 유키 / t t-t-e-mea tr-mp ぇ

참고



도메인 모델의 코드는, 이하를 참고로 했습니다.
htps : // 기주 b. 코 m / rs ぃ b / 흠뻑 pdd ぇ r pprt

환경


  • Windows10
  • Visual Studio 2019
  • .NET Core 3.0
  • MediatR

  • 해설



    샘플 코드의 프로젝트 구성은 다음과 같습니다.


    프로젝트 이름
    설명


    DotNetCoreMediatrSample.Domain
    도메인 모델

    DotNetCoreMediatrSample.Infrastructure.InMemory
    메모리 내 리포지토리 구현

    DotNetCoreMediatrSample.Infrastructure.InMemory.Test
    메모리 내 리포지토리 테스트

    DotNetCoreMediatrSample.Web
    웹 API


    이번에는 User 엔터티를 검색하는 쿼리와 User 엔터티를 만드는 명령을 구현합니다.
    IRequest 를 구현한 클래스를 각각 작성합니다.

    GetUserQuery.cs
    
    public class GetUserQuery : IRequest<UserModel>
    {
        public GetUserQuery(string id)
        {
            Id = id;
        }
    
        public string Id { get; }
    }
    
    

    CreateUserCommand.cs
    
    public class CreateUserCommand : IRequest
    {
        public CreateUserCommand(string userName, string firstName, string familyName)
        {
            UserName = userName;
            FirstName = firstName;
            FamilyName = familyName;
        }
    
        public string UserName { get; }
        public string FirstName { get; }
        public string FamilyName { get; }
    }
    
    

    MediatR은 쿼리 명령 메시지에 해당하는 작업을 수행하는 Handler를 구현해야합니다.
    IRequestHandler 를 구현하는 형태로 작성합니다.
    이번 구현 샘플에는 포함되어 있지 않지만 IRequest 대신 INotification을 이용하면 하나의 메시지에 대해 여러 Handler를 등록할 수 있습니다.

    GetUserHandler.cs
    
    public class GetUserHandler : IRequestHandler<GetUserQuery, UserModel>
    {
        private readonly IUserRepository _repository;
    
        public GetUserHandler(IUserRepository repository)
        {
            _repository = repository;
        }
    
        public Task<UserModel> Handle(GetUserQuery request, CancellationToken cancellationToken)
        {
            var user = _repository.Find(new UserId(request.Id));
            var model = user == null ? null : new UserModel(user);
            return Task.FromResult(model);
        }
    }
    
    
    

    위의 내용을 ASP.NET Core 웹 API에 통합해 봅니다.
    MediatR의 Handler를 DI 컨테이너에 등록합니다. 전용의 확장 메소드가 있기 때문에, 이것을 이용합니다.
    또한 메모리 내 리포지토리를 함께 DI 컨테이너에 등록합니다.
    실용적인 CQRS에서는 쿼리와 명령으로 대상 데이터 스토어를 구분하는 경우가 있지만 이번에는 동일한 리포지토리입니다.

    Startup.cs
    
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMediatR(typeof(GetUserHandler).Assembly);
        services.AddMediatR(typeof(CreateUserHandler).Assembly);
        services.AddControllers();
    
        services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Web API", Version = "v1"}); });
    
        services.AddTransient<IUserRepository, InMemoryUserRepository>();
        services.AddTransient<IUserFactory, UserFactory>();
    }
    
    

    Web API의 컨트롤러에서 IMediator를 생성자 주입합니다.
    IMediator.Send를 사용하여 쿼리 및 명령을 실행합니다.

    UsersController.cs
    
    public UsersController(IMediator mediator)
    {
        _mediator = mediator;
    }
    
    [HttpGet("{id}")]
    public async Task<ActionResult<UserViewModel>> Get(string id)
    {
        var user = await _mediator.Send(new GetUserQuery(id));
        if (user == null) return BadRequest("データが存在しません");
    
        return new UserViewModel
        {
            Id = user.Id,
            UserName = user.UserName,
            FirstName = user.Name.FirstName,
            FamilyName = user.Name.FamilyName
        };
    }
    
    

    동작 확인은 Swagger UI에서 수행합니다.


    MediatR을 사용하면 비교적 쉽게 도메인 로직과 Web API 종속성을 느슨하게 결합 할 수 있습니다.
    ASP.NET Core를 사용할 때 선택의 하나로 고려해야 할 가치가 있습니까?

    좋은 웹페이지 즐겨찾기