MediatR로 도메인 이벤트 게시
여러 핸들러에서 일부를 수행하려는 경우 핸들러에 코드가 있으면 코드가 부풀어 오르거나 중복될 수 있습니다.
MediatR 알림은 이러한 경우에 유용할 수 있습니다. 방법을 살펴보겠습니다!
설정
먼저 예제 설정을 생성해 보겠습니다. .NET 6을 사용하여 간단한 웹 API를 생성합니다.
~$ dotnet new webapi -o MediatrNotification
즐겨찾는 편집기에서 초기 상용구(
WeatherForecast.cs
및 Controllers/WheatherForecastController.cs
) 항목을 제거합니다.마지막으로 MediatR을 추가하고 초기화합니다.
~$ dotnet add package MediatR
~$ dotnet add package MediatR.Extensions.Microsoft.DependencyInjection
// Program.cs
+ using MediatR;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
+ builder.Services.AddMediatR(typeof(Program));
사용 사례
데모의 경우 누군가 꽃다발을 주문할 수 있는 단일 엔드포인트가 있습니다.
해당 예제를 기반으로 새 파일
PlaceBouquetOrderRequest.cs
에서 요청을 생성할 수 있습니다.// PlaceBouquetOrderRequest.cs
public class PlaceBouquetOrderRequest : IRequest<Guid>
{
public DateTime DueDate { get; init; }
public int FlowersCount { get; init; }
public string? Note { get; init; }
}
그 옆에 핸들러를 초기화합니다.
// PlaceBouquetOrderRequest.cs
public class PlaceBouquetOrderRequestHandler : IRequestHandler<PlaceBouquetOrderRequest, Guid>
{
public Task<Guid> Handle(PlaceBouquetOrderRequest request, CancellationToken cancellationToken)
{
var orderId = new Guid();
// Send the order to the merchant
return Task.FromResult(orderId);
}
}
마지막으로
Controllers/BouquetController.cs
에 연결된 컨트롤러를 만듭니다.// BouquetController.cs
[Route("api/[controller]")]
public class BouquetController : ControllerBase
{
private readonly IMediator _mediator;
public BouquetController(IMediator mediator)
=> _mediator = mediator;
[HttpPost("order")]
public async Task<IActionResult> PlaceBouquetOrder([FromBody] PlaceBouquetOrderRequest request)
{
var orderId = await _mediator.Send(request);
return Ok(orderId);
}
}
부작용 추가
우리의 앱은 훌륭하게 실행되고 있지만 이제 고객은 일정에 대한 개요를 볼 수 있도록 판매자의 캘린더에도 이벤트를 보내길 원합니다.
Please note that we won't perform any validation here since that's not the goal of this tutorial
PlaceBouquetOrderRequest.cs
로 돌아가서 추가 변경 사항을 추가해 보겠습니다.public class PlaceBouquetOrderRequestHandler : IRequestHandler<PlaceBouquetOrderRequest, Guid>
{
public Task<Guid> Handle(PlaceBouquetOrderRequest request, CancellationToken cancellationToken)
{
var orderId = new Guid();
// Send the order to the merchant
SendReminderToCalendarAt(request.DueDate);
return Task.FromResult(orderId);
}
private void SendReminderToCalendarAt(DateTime dueDate)
{
// Send a reminder to the merchant's calendar
}
}
문제
불행히도 거기에서 찾을 수 있는 몇 가지 문제가 있습니다.
PlaceBouquetOrderRequestHandler
는 이제 일정 미리 알림도 담당합니다. 그 범위가 원래 책임 범위를 벗어나고 있습니다SendReminder
논리는 다른 곳에서 재사용할 수 있으며 메서드를 복제하거나 전용 서비스로 추출해야 합니다. 그러나 서비스를 만들면 객체가 핸들러를 중심으로 설계된 코드 구조를 변경하게 될 수 있습니다. 해결책
잠시 생각해 보면 요청된 작업은 단순히 알림을 보내는 것보다 "주문이 완료되면 작업을 수행하는 것"에 관한 것입니다.
다행스럽게도 MediatR에는 이러한 이벤트를 나타내고 처리하는 개체가 있으며 이를 알림이라고 합니다.
우리의 사건을 해결하기 위해 하나를 만들어 봅시다!
새
BouquetOrderPlacedEvent.cs
에서 다음 이벤트를 생성합니다.// BouquetOrderPlacedEvent.cs
public class BouquetOrderPlacedEvent : INotification
{
public Guid OrderId { get; init; }
public DateTime DueDate { get; init; }
}
이제 이러한 종류의 알림을 처리할 수 있는 이벤트 핸들러를 만들 수 있습니다.
// BouquetOrderPlacedEvent.cs
public class BouquetOrderPlacedEventHandler : INotificationHandler<BouquetOrderPlacedEvent>
{
public Task Handle(BouquetOrderPlacedEvent notification, CancellationToken cancellationToken)
{
SendReminderToCalendarAt(notification.DueDate);
return Task.CompletedTask;
}
private void SendReminderToCalendarAt(DateTime dueDate)
{
// Send a reminder to the merchant's calendar
}
}
그리고 핸들러의 이전 논리를 이 이벤트의 방출로 대체합니다.
// PlaceBouquetOrderRequestHandler.cs
public class PlaceBouquetOrderRequestHandler : IRequestHandler<PlaceBouquetOrderRequest, Guid>
{
private readonly IPublisher _publisher;
public PlaceBouquetOrderRequestHandler(IPublisher publisher)
=> _publisher = publisher;
public Task<Guid> Handle(PlaceBouquetOrderRequest request, CancellationToken cancellationToken)
{
var orderId = new Guid();
// Send the order to the merchant
_publisher.Publish(new BouquetOrderPlacedEvent
{
OrderId = orderId,
DueDate = request.DueDate
});
return Task.FromResult(orderId);
}
}
더 나아가
새로운 종류의 주문을 처리할 계획이라면 이벤트를
OrderPlacedEvent
로 일반화하여 주문 종류에서 추상화할 수 있습니다.// BouquetOrderPlacedEvent.cs
public abstract class OrderPlacedEvent : INotification
{
public Guid OrderId { get; init; }
public DateTime DueDate { get; init; }
}
public class BouquetOrderPlacedEvent : OrderPlacedEvent { }
그런 다음 기본 클래스에서 파생된 모든 이벤트를 처리할 수 있도록 처리기를 일반화할 수 있습니다
OrderPlacedEvent
.public class OrderPlacedEventHandler<TOrderPlacedEvent> : INotificationHandler<TOrderPlacedEvent>
where TOrderPlacedEvent : OrderPlacedEvent
{
public Task Handle(TOrderPlacedEvent notification, CancellationToken cancellationToken)
{
SendReminderToCalendarAt(notification.DueDate);
return Task.CompletedTask;
}
private void SendReminderToCalendarAt(DateTime dueDate)
{
// Send a reminder to the merchant's calendar
}
}
Note that if we had just changed our handler's definition to
public class OrderPlacedEventHandler : INotificationHandler<OrderPlacedEvent>
, MediatR would not have correctly route the event to our handler. You can read more about it on this issue.
테이크 아웃
짜잔, 우리는 로직을 전용 핸들러로 옮겼고 나중에 다른 유형의 순서도 처리할 수 있습니다. 또한 처리기의 논리를 현재 사용 사례에 최대한 가깝게 유지했으며 타사 캘린더 서비스와의 통신을 실제로 구현했다면 처리기의 종속성을 줄였을 것입니다.
Reference
이 문제에 관하여(MediatR로 도메인 이벤트 게시), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/pbouillon/publishing-domain-events-with-mediatr-32mm텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)