Dirk Richter
Software-Entwicklung und Architektur
Alternative zu MediatR - Brighter and Darker
Die Nuget Packet Automapper und MediatR sind in der aktuellsten Version keine freien Produkte mehr.
Es wird ein kleines Beispiel gezeigt, wie die Nuget Pakete Brighter und Darker ale Alternative verwendet werden können. Mit Brighter und Darker können Usecases (Commands und Queries) in Onion Architecture klar getrennt, einheitlich und testbar implementiert werden – als moderner Ersatz für MediatR in ASP.NET Core.
Inhaltsverzeichnis
Einleitung
Verwendung Brighter/ Darker in der Onion Architecture
Onion Architecture fördert die Entkopplung von Schnittstellen (z.B. Controller) und Business Logic. Mit Brighter (für Commands/Events) und Darker (für Queries) werden beide Arten von Usecases konsequent als Handler im Application Layer abgebildet.
Controller injizieren nur einen CommandProcessor und einen QueryProcessor, unabhängig von der konkreten Business Logik. Das Interface Layer bleibt schlank und unabhängig.
Commands und Queries sind als Handler einfach testbar. Middleware (z.B. Caching, Logging, Validation) kann zentral integriert werden.
Neue Entwickler finden ein konsistentes Muster für alle Usecases, Command- und Query-Handler sind klar getrennt und strukturiert.
Die Architektur entspricht modernen Standards für skalierbare, wartbare .NET Anwendungen.
Beispiel: Brighter/Darker als Ersatz für MediatR in ASP.NET Core
MediatR-Implementierung (klassisch)
1// Command
2public class PlaceOrderCommand : IRequest { ... }
3
4// Command Handler
5public class PlaceOrderHandler : IRequestHandler<PlaceOrderCommand>
6{
7 public async Task<Unit> Handle(PlaceOrderCommand command, CancellationToken cancellationToken)
8 {
9 // Business Logic
10 return Unit.Value;
11 }
12}
13
14// Query
15public class GetOrderQuery : IRequest<OrderDto> { ... }
16
17// Query Handler
18public class GetOrderQueryHandler : IRequestHandler<GetOrderQuery, OrderDto>
19{
20 public async Task<OrderDto> Handle(GetOrderQuery query, CancellationToken cancellationToken)
21 {
22 // Business Logic
23 return new OrderDto { ... };
24 }
25}
26
27// Controller
28public class OrderController : ControllerBase
29{
30 private readonly IMediator _mediator;
31 public OrderController(IMediator mediator) => _mediator = mediator;
32
33 [HttpPost]
34 public async Task<IActionResult> PlaceOrder([FromBody] PlaceOrderCommand command)
35 {
36 await _mediator.Send(command);
37 return Ok();
38 }
39
40 [HttpGet("{orderId}")]
41 public async Task<ActionResult<OrderDto>> GetOrder(int orderId)
42 {
43 var order = await _mediator.Send(new GetOrderQuery { OrderId = orderId });
44 return order is null ? NotFound() : Ok(order);
45 }
46}
Brighter/Darker-Implementierung (Onion Architecture)
1// Command
2public class PlaceOrderCommand : IRequest { ... }
3
4// Command Handler (Brighter)
5using Paramore.Brighter;
6public class PlaceOrderHandler : RequestHandlerAsync<PlaceOrderCommand>
7{
8 public override async Task<PlaceOrderCommand> HandleAsync(PlaceOrderCommand command, CancellationToken cancellationToken = default)
9 {
10 // Business Logic
11 return command;
12 }
13}
14
15// Query
16public class GetOrderQuery : IQuery<OrderDto> { ... }
17
18// Query Handler (Darker)
19using Darker;
20public class GetOrderQueryHandler : QueryHandler<GetOrderQuery, OrderDto>
21{
22 public override async Task<OrderDto> ExecuteAsync(GetOrderQuery query, CancellationToken cancellationToken = default)
23 {
24 // Business Logic
25 return new OrderDto { ... };
26 }
27}
28
29// Controller
30public class OrderController : ControllerBase
31{
32 private readonly IAmCommandProcessor _commandProcessor;
33 private readonly IQueryProcessor _queryProcessor;
34
35 public OrderController(IAmCommandProcessor commandProcessor, IQueryProcessor queryProcessor)
36 {
37 _commandProcessor = commandProcessor;
38 _queryProcessor = queryProcessor;
39 }
40
41 [HttpPost]
42 public async Task<IActionResult> PlaceOrder([FromBody] PlaceOrderCommand command)
43 {
44 await _commandProcessor.SendAsync(command);
45 return Ok();
46 }
47
48 [HttpGet("{orderId}")]
49 public async Task<ActionResult<OrderDto>> GetOrder(int orderId)
50 {
51 var order = await _queryProcessor.ExecuteAsync(new GetOrderQuery { OrderId = orderId });
52 return order is null ? NotFound() : Ok(order);
53 }
54}
Fazit