?? Implementing Domain Events in .NET: A Practical Guide
?? Elazar Lebedev
Founder | OSINT | Big Data | Python (NLTK, TensorFlow) | Data Mining | Multi-Language Processing | Network Analysis | ETL | Cryptographic Protocols | Geospatial Analysis | Intelligence Analysis | Cloud Computing
Hello, fellow developers! Today, let's dive into the world of Domain-Driven Design (DDD) and explore one of its powerful patterns: Domain Events. ??
Domain Events are objects that store data about actions and events in our system. They help us separate core business logic from peripheral operations, isolate errors, and improve extensibility.
Let's look at four ways to implement Domain Events, each building on the previous:
1?? Direct publication from a service
2?? Returning events from entity methods
3?? Injecting an event publisher into entities
4?? Publishing through entity tracking
We'll use a simple fruit selling system as our example. ??????
Here's a basic implementation of the first approach:
public class OrderService
{
private readonly IDomainEventPublisher _publisher;
public async Task<CreateOrderResponse> CreateOrder(CreateOrderDto request)
{
var newOrder = new Order();
// Business logic here...
var @event = new OrderCreatedEvent
{
OrderId = newOrder.Id,
// Other properties...
};
_publisher.AddEvent(@event);
return new CreateOrderResponse();
}
}
As we progress through the approaches, we gradually move the responsibility of event creation and publication away from the service and into the domain entities themselves.
领英推荐
In the final approach, our entities become event emitters:
public class Order : IDomainEventEmitter
{
private readonly List<IDomainEvent> _events = new();
public IReadOnlyCollection<IDomainEvent> Events => _events.AsReadOnly();
public static Order CreateNewOrder(OrderDetails details)
{
var order = new Order(details);
order._events.Add(new OrderCreatedEvent(order));
return order;
}
}
And our DbContext handles event publication:
public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
var entries = ChangeTracker.Entries<IDomainEventEmitter>().ToList();
foreach (var entity in entries.Select(entry => entry.Entity))
{
_eventPublisher.AddEventRange(entity.Events);
entity.ClearEvents();
}
return base.SaveChangesAsync(cancellationToken);
}
Now, let's look at a more complex scenario: an order processing system with multiple steps and events. ??
public class Order : IDomainEventEmitter
{
public OrderStatus Status { get; private set; }
private readonly List<IDomainEvent> _events = new();
public IReadOnlyCollection<IDomainEvent> Events => _events.AsReadOnly();
public void Process()
{
if (Status != OrderStatus.Created)
throw new InvalidOperationException("Order must be in Created status to process");
Status = OrderStatus.Processing;
_events.Add(new OrderProcessingStartedEvent(this));
}
public void Complete()
{
if (Status != OrderStatus.Processing)
throw new InvalidOperationException("Order must be in Processing status to complete");
Status = OrderStatus.Completed;
_events.Add(new OrderCompletedEvent(this));
}
}
public class OrderProcessingService
{
private readonly IOrderRepository _repository;
private readonly IPaymentService _paymentService;
private readonly IShippingService _shippingService;
public async Task ProcessOrder(Guid orderId)
{
var order = await _repository.GetByIdAsync(orderId);
order.Process();
await _paymentService.ProcessPayment(order);
await _shippingService.ArrangeShipping(order);
order.Complete();
await _repository.SaveAsync(order);
}
}
In this scenario, we have multiple stages of order processing, each triggering its own event. These events could be handled by different parts of the system to perform actions like sending notifications, updating inventory, or triggering external processes.
Remember, the key is to choose the approach that best fits your project's needs and complexity. Start simple and evolve as necessary. ??????
??What's your experience with Domain Events? Share in the comments below! ??
#SoftwareDesign #EventDrivenArchitecture #CSharp #DDD #dotNET #SoftwareArchitecture