Я настроил свои объекты модели предметной области так, чтобы они не зависели от какой-либо службы и логики инфраструктуры. Я также использую события предметной области, чтобы реагировать на некоторые изменения в моделях предметной области.
Теперь моя проблема заключается в том, как вызвать эти события из самих объектов модели предметной области. В настоящее время я использую для этого статический класс DomainEvents Уди Дахана (мне нужно, чтобы события обрабатывались именно тогда, когда они происходят, а не в последнее время). События используются для многих вещей, таких как ведение журнала, обновление данных в связанных службах и других объектах модели предметной области и БД, публикация сообщений в шине MassTransit и т. д.
Статический класс DomainEvents использует область действия Autofac, которую я добавляю в него в какой-то момент, чтобы найти экземпляр IMediatr и опубликовать события, например:
public static class DomainEvents
{
private static ILifetimeScope Scope;
public async static Task RaiseAsync<TDomainEvent>(TDomainEvent @event) where TDomainEvent : IDomainEvent
{
var mediator = Scope?.Resolve<IMediatorBus>();
if (mediator != null)
{
await mediator!.Publish(@event).ConfigureAwait(false);
}
else
{
Debug.WriteLine("Mediator not set for DomainEvents!");
}
}
public static void SetScope(ILifetimeScope scope)
{
Scope = scope;
}
}
Все это работает нормально в однопоточной среде, но метод DomainEvents.SetScope() является возможной проблемой гонок в многопоточной среде. Т.е. Когда я представлю MassTransit и создам потребителей сообщений, каждый потребитель сообщений установит текущую LifetimeScope в DomainEvents с помощью этого метода, и вот проблема: каждый потребитель перезапишет область времени жизни новой.
Почему я использую статический класс DomainEvents? Потому что я не хочу загрязнять свои объекты модели предметной области инфраструктурой. Я думал о том, чтобы сделать DomainEvents нестатичным (определить интерфейс), но затем мне нужно, чтобы они вводились в каждый объект модели предметной области, и я все еще думаю об этом, но, возможно, есть лучший способ.
Я хочу знать, есть ли лучший способ справиться с этим? Может быть, какие-то изменения в классе DomainEvents? Или, возможно, удалите статический класс DomainEvents и используйте для этого интерфейс или DomainService. Проблема в том, что мне не нравятся статические классы, но мне также не нравится помещать неспецифические для предметной области зависимости в объекты модели предметной области.
Пожалуйста помоги.
ОБНОВЛЕНИЕ
Чтобы лучше прояснить процесс и то, для чего я использую DomainEvents... У меня есть длительный процесс, который может занять от нескольких минут до нескольких часов/дней. Итак, процесс происходит следующим образом:
- Я получаю сообщение от MassTransit, т.е. ProcessStartMessage(processId)
- Получите ProcessData для (processId) из базы данных.
- Создайте модель предметной области в памяти ProcessTracker (singleton) и поместите в нее все данные, которые я загрузил из БД. (кэш в памяти)
- Я получаю другое сообщение от Masstransit т.е. ProcessStatusChanged(идентификатор процесса, данные).
- Перешлите данные этого сообщения в одноэлементный ProcessTracker в памяти для обработки.
- ProcessTracker обрабатывает данные.
Чтобы ProcessTracker мог обрабатывать эти данные, он создает множество объектов модели предметной области, каждый из которых отвечает за обработку некоторой части данных. (Обратите внимание, что больше НЕТ вызовов БД и гидратации объектов из БД, все это происходит в памяти, а также модель домена не сопоставляется с каким-либо объектом, она не подключена ни к какому объекту БД). В какой-то момент мне нужно зарегистрировать, что сделал объект модели предметной области в цепочке, закончилась или началась его работа, достигнута ли какая-то веха и т. д. Это делается путем вызова DomainEvents. Мне также нужно уведомить графический интерфейс об этих событиях, поэтому они также используются для отправки сообщений Masstransit.
То есть (псевдокод):
public class ProcessTracker
{
private Step _currentStep;
public void ProcessData(data)
{
_currentStep.ProcessData(data);
DomainEvents.Raise(new ProcesTrackerDataProcessed());
...
}
}
public class Step
{
public Phase _currentPhase;
public void ProcessData(data)
{
if (data.IsManual && _someOtherCondition())
{
DomainEvents.Raise(new StepDataEvent1());
...
}
if(data.CanTransition)
{
DomainEvents.Raise(new TransitionToNewPhase(this, data));
}
_currentPhase.DoSomeWork(data);
DomainEvents.Raise(new StepDataProcessed(this, data));
...
}
}
Что касается обновлений базы данных, они не являются транзакционными и не важны для процесса, а состояние объекта модели предметной области сохраняется только в памяти, в случае сбоя процесса процесс ДОЛЖЕН начинаться с самого начала (восстановления НЕТ).
Чтобы завершить процесс:
- Я получаю ProcessEnd от MassTransit
- Данные сообщения пересылаются в ProcessTracker.
- ProcessTracker обрабатывает данные и получает результат процесса.
- Результат процесса сохраняется в БД
- Другим участникам процесса отправляется сообщение, уведомляющее их о завершении процесса.