Сегодня я узнал 06/05/2022
- Доменные события
- Когда использовать
- Компоненты
- Динамика
- Реализация
Доменные события
Суть события домена заключается в том, что вы используете его для захвата вещей, которые могут вызвать изменение состояния разрабатываемого вами приложения. Эти объекты событий обрабатываются, чтобы вызвать изменения в системе, и сохраняются для создания журнала аудита (AuditLog).
Фаулер, Мартин
То есть когда в системе что-то происходит, я могу на основе этого события выполнить операцию и сохранить событие для будущего случая аудита.
Каждое событие должно быть представлено в виде действия, представленного в прошлом, пример UserCreated
Когда использовать
Обычно событие домена используется, когда мы хотим уведомить другие ограниченные контексты об изменении состояния.
пример: в системе электронной коммерции при создании заказа мы можем выдать событие OrderPlaced
, и тогда выставляющий счет boundedContext будет слушать это событие и выставлять счет для этого заказа
Компоненты
- СобытиеВыбирается дата и время и содержит информацию о том, что произошло в эту дату и время Например: продукт с названием X изменился на Y
- ОбработчикВыполняет обработку при вызове события Например: после создания пользователя и отправки письма часть отправки письма является обработчиком
- Диспетчер событийОтветственность за хранение и выполнение обработчиков события при его возникновении
Динамический
- Создание диспетчера событий
- Создайте «Событие
- Создайте обработчик для события
- Зарегистрируйте событие вместе с обработчиком в «Диспетчере событий
И когда мы передадим notify
диспетчеру Event dispatcher
, передающему событие, будут выполнены все handlers
.
Реализация
сначала создадим наши интерфейсы, которые будут реализованы диспетчером, событиями и обработчиками
export interface EventInterface<T=any> {
dateTimeOcurred: Date;
eventData: T;
}
export interface EventHandlerInterface<T extends EventInterface=EventInterface>{
handle(event: T): void;
}
export interface EventDispatcherInterface{
notify(event: EventInterface): void;
register(eventName: string, eventHandler: EventHandlerInterface): void;
unregister(eventName: string, eventHandler: EventHandlerInterface): void;
unregisterAll(): void;
}
а затем давайте создадим наш класс диспетчера событий
export class EventDispatcher implements EventDispatcherInterface{
private eventHandlers: { [eventName: string]: EventHandlerInterface[] } = {};
get getEventsHandler(): { [eventName: string]: EventHandlerInterface[] }{
return this.eventHandlers;
}
notify(event: EventInterface): void {
const eventName = event.constructor.name;
if(this.eventHandlers[eventName]){
this.eventHandlers[eventName].forEach(eventHandler => {
eventHandler.handle(event);
})
}
}
register(eventName: string, eventHandler: EventHandlerInterface<EventInterface>): void {
if (!this.eventHandlers[eventName]) {
this.eventHandlers[eventName] = [];
}
this.eventHandlers[eventName].push(eventHandler);
}
unregister(eventName: string, eventHandler: EventHandlerInterface<EventInterface>): void {
if (!this.eventHandlers[eventName]) {
throw new Error(`${eventName} event does not exist.`);
}
const index = this.eventHandlers[eventName].indexOf(eventHandler);
if (index !== -1){
this.eventHandlers[eventName].splice(index, 1);
}else{
throw new Error(`${eventName} event handler not registered.`);
}
}
unregisterAll(): void {
this.eventHandlers = {};
}
}
В этом классе у нас будет объект с обработчиками событий, и мы будем проверять, существует ли уже событие, на которое нужно зарегистрировать обработчик, если оно уже существует, мы просто добавим обработчик к событию, если не существует, мы создадим событие, а затем добавим обработчик. Также в этом классе есть метод notify, который получает событие и проходит через все обработчики, связанные с ним, выполняя метод handle
А затем давайте создадим наше событие
export class ProductCreatedEvent implements EventInterface{
dateTimeOcurred: Date;
eventData: any;
constructor(eventData: any){
this.dateTimeOcurred = new Date();
this.eventData = eventData;
}
}
и наш обработчик для этого события, который в данном случае будет отправлять электронное письмо при создании продукта
export class SendEmailWhenProductIsCreatedHandler implements EventHandlerInterface<ProductCreatedEvent>{
handle(event: ProductCreatedEvent): void {
console.log("Sending email to user...")
}
}
и для того, чтобы это работало, нам нужна следующая реализация
const eventDispatcher = new EventDispatcher();
const eventHandler = new SendEmailWhenProductIsCreatedHandler();
const spyEventHandler = jest.spyOn(eventHandler, "handle");
eventDispatcher.register("ProductCreatedEvent", eventHandler)
const productCreatedEvent = new ProductCreatedEvent({
name: "Product 1",
description: "Product 1 description",
price: 100
});
eventDispatcher.notify(productCreatedEvent)
поэтому каждый раз, когда создается новый продукт, нам просто нужно вызывать функцию notify, которая выполняет все обработчики, связанные с полученным событием.
Я все еще изучаю DDD, и если вы хотите увидеть код лучше, вы можете проверить этот репозиторий