Постоянство и доменные события с постоянными невежественными объектами
Я изучал предметно-ориентированный дизайн в сочетании сдоменные события, Мне действительно нравится разделение проблем, которые эти события обеспечивают. Я столкнулся с проблемой с порядком сохранения объекта домена и создания событий домена. Я хотел бы вызывать события в доменных объектах, но я хочу, чтобы они оставались невежественными.
Я создал основнойShoppingCartService
, с этимCheckout
метод:
public void Checkout(IEnumerable<ShoppingCartItem> cart, Customer customer)
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
В этом примере конструкторOrder
подниметOrderCreated
событие, которое может быть обработано определенными обработчиками. Тем не менее, я не хочу, чтобы эти события возникали, когда сущность еще не сохранена или когда сохранение каким-либо образом завершается сбоем.
Чтобы решить эту проблему, я нашел несколько решений:
1. Поднять события в сервисе:
Вместо того, чтобы вызывать событие в доменном объекте, я мог бы вызывать события в службе. В этом случаеCheckout
метод подниметOrderCreated
событие. Одним из недостатков этого подхода является то, что при рассмотренииOrder
доменный объект, неясно, какие события и какими методами вызываются. Кроме того, разработчик должен помнить, чтобы поднять событие, когда заказ создается в другом месте. Это не правильно.
Другой вариант - поставить в очередь события домена и вызвать их при успешном сохранении. Это может быть достигнуто путемusing
утверждение например:
using (DomainEvents.QueueEvents<OrderCreated>())
{
var order = new Order(cart, customer);
_orderRepositorty.Add(order);
_unitOfWork.Commit();
}
QueueEvents<T>
Метод установил бы логическое значениеtrue
иDomainEvents.Raise<T>
Метод будет помещать событие в очередь, а не выполнять его напрямую. В распоряжение обратного вызоваQueueEvent<T>
события в очереди выполняются, что гарантирует, что сохранение уже произошло. Это кажется довольно сложным, и требуется, чтобы служба знала, какое событие вызывается в объекте домена. В приведенном мною примере он также поддерживает только один тип события, которое нужно вызвать, однако это можно обойти.
Я мог бы сохранить объект, используя событие домена. Это выглядит нормально, за исключением того факта, что обработчик событий, сохраняющий объект, должен выполняться первым, однако я где-то читал, что события домена не должны зависеть от определенного порядка выполнения. Возможно, это не так важно, и события домена могут каким-то образом знать, в каком порядке должны выполняться обработчики. Например: предположим, у меня есть интерфейс, определяющий событие доменаобработчикреализация будет выглядеть так:
public class NotifyCustomer : IDomainEventHandler<OrderCreated>
{
public void Handle(OrderCreated args)
{
// ...
}
}
Когда я хочу обработать сохранение в использовании обработчика событий, я бы создал другой обработчик, производный от того же интерфейса:
public class PersistOrder : IDomainEventHandler<OrderCreated>
{
public void Handle(OrderCreated args)
{
// ...
}
}
}
СейчасNotifyCustomer
поведение зависит от порядка, сохраняемого в базе данных, поэтомуPersistOrder
Обработчик событий должен выполняться первым. Допустимо ли, чтобы эти обработчики вводили свойство, например, которое указывает порядок их выполнения? Немного о реализацииDomainEvents.Raise<OrderCreated>()
метод:
foreach (var handler in Container.ResolveAll<IDomainEventHandler<OrderCreated>>().OrderBy(h => h.Order))
{
handler.Handle(args);
}
Теперь мой вопрос: есть ли у меня другие варианты? Я что-то пропустил? А что вы думаете о решениях, которые я предложил?