Я использовал шаблон События событий домена в течение некоторого времени - он позволяет нам инкапсулировать как можно больше поведения на нашем доменном уровне и обеспечивает хороший способ для другие части нашего приложения, чтобы подписаться на события домена.
В настоящее время мы используем статический класс, который наши объекты домена могут вызывать для повышения событий:
static class DomainEvents
{
public static IEventDispatcher Dispatcher { get; set; }
public static void Raise<TEvent>(TEvent e)
{
if (e != null)
{
Dispatcher.Dispatch(e);
}
}
}
Как вы можете видеть, это немного больше, чем прокладка для IEventDispatcher
, которая фактически выполняет работу по отправке или публикации событий.
Наша реализация диспетчера просто использует наш контейнер IoC (StructureMap) для размещения обработчиков событий для указанного типа события.
public void Dispatch<TEvent>(TEvent e)
{
foreach (var handler in container.GetAllInstances<IHandler<TEvent>>())
{
handler.Handle(e);
}
}
В большинстве случаев это нормально. Однако с этим подходом существует несколько проблем:
События должны отправляться только в том случае, если объект успешно сохраняется
Возьмем следующий класс:
public class Order
{
public string Id { get; private set; }
public decimal Amount { get; private set; }
public Order(decimal amount)
{
Amount = amount;
DomainEvents.Raise(new OrderRaisedEvent { OrderId = Id });
}
}
В конструкторе Order
поднимем OrderRaisedEvent
. На нашем прикладном уровне мы, скорее всего, создадим экземпляр заказа, добавим его в нашу базу данных "session", а затем зафиксируем/сохраним изменения:
var order = new Order(amount: 10);
session.Store(order);
session.SaveChanges();
Проблема заключается в том, что событие домена возникает до того, как мы успешно сохранили объект Order (совершая транзакцию). Если сбой не удалось, мы все равно отправили бы события.
Лучшим подходом было бы приостановить события до тех пор, пока объект не будет сохранен. Тем не менее, я не уверен, как лучше всего реализовать это, поддерживая строго типизированные обработчики событий.
События не должны создаваться до тех пор, пока объект не будет сохранен
Еще одна проблема, с которой я сталкиваюсь, заключается в том, что наши идентификаторы объектов не установлены/не назначены до сохранения объекта (RavenDB - session.Store
). Это означает, что в приведенном выше примере идентификатор заказа, переданный этому событию, на самом деле null
.
Так как я не уверен, как можно на самом деле генерировать идентификаторы RavenDB, одно решение может заключаться в том, чтобы задержать создание событий до тех пор, пока сущность не будет сохранена, но опять же я не так лучше ее реализую - возможно, в очереди на сбор Func<TEntity, TEvent>
?