DDD Domain events
Tags: todayilearned, braziliandevs, javascript, beginners
Published: 2022-05-06 22:15:34

### Today I Learned 06/05/2022

- [Eventos de domínio](#eventos-de-domínio)
- [Quando utilizar](#quando-utilizar)
- [Componentes](#componentes)
- [Dinâmica](#dinâmica)
- [Implementação](#implementação)

---

## Eventos de domínio
> A essência de um evento de domínio é que você o usa para capturar coisas que podem desencadear uma mudança no estado do aplicativo que você está desenvolvendo. Esses objetos de evento são processados para causar alterações no sistema e armazenados parar fornecer um AuditLog
[Fowler, Martin](https://www.martinfowler.com/eaaDev/DomainEvent.html#:~:text=The%20essence%20of%20a%20Domain,inputs%20into%20a%20single%20source.)

Ou seja quando algo acontece no sistema eu posso baseado nesse evento executar uma operação e armazenar o evento para um caso de auditoria no futuro

Todo evento deve ser representado em uma ação representada no passado, exemplo UserCreated

---

## Quando utilizar

Normalmente um domain event deve ser utilizado quando queremos notificar outros Bounded Contexts de uma mudança de estado

exemplo: Um sistema de E-commerce quando um pedido é criado, podemos emitir um evento de `OrderPlaced` e então o boundedContext de emissão de notas fiscais vai escutar esse evento e emitir a nota para aquele pedido

---

## Componentes

- Event
Tem uma data e hora, e contem o que aconteceu naquela data e hora
Ex: produto com nome X mudou para Y
- Handler
Executa o processamento quando o evento é chamado
Ex: depois de criar um usuário eviar um email, a parte de enviar email é um handler
- Event Dispatcher
Responsável por armazenar e executar os handlers de um evento quando ele é disparado

---

## Dinâmica

- Criar um "Event Dispatcher"
- Criar um "Evento"
- Criar um "Handler" para o "Evento"
- Registrar o Evento, juntamente com o Handler no "Event Dispatcher"

E ai quando dermos um `notify` no `Event dispatcher` passando o evento, todos os `handlers` serão executados

---

## Implementação

primeiro vamos criar as nossas interfaces que serão implementadas pelo dispatcher, pelos eventos e pelos handlers

```js
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;
}
```

e ai vamos criar a nossa classe de event dispatcher

```js
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 = {};
  }

}
```

Nessa classe vamos ter um objeto com os event handlers e vamos verificar se o evento ao qual o handler quer ser registrado já existe, se já existir vamos somente adicionar o handler ao evento, se não existir vamos criar o evento e adicionar o handler em seguida. Também nessa classe temos o método notify, que recebe o evento e percorre todos os handlers associados a ele executando o método handle


E ai vamos criar o nosso evento 

```js
export class ProductCreatedEvent implements EventInterface{
  dateTimeOcurred: Date;
  eventData: any;

  constructor(eventData: any){
    this.dateTimeOcurred = new Date();
    this.eventData = eventData;
  }
}
```

e o nosso handler para esse evento que nesse caso será o de enviar email quando o produto for criado

```js
export class SendEmailWhenProductIsCreatedHandler implements EventHandlerInterface<ProductCreatedEvent>{
  handle(event: ProductCreatedEvent): void {
    console.log("Sending email to user...")
  }
}
```

e para fazermos isso funcionar basta a seguinte implementação

```js
    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)
```

então toda vez que um novo produto for criado basta a chamarmos a função notify que executa todos os handlers associados ao evento que ela recebeu

> Ainda estou aprendendo sobre DDD e se quiser ver melhor o código pode dar uma olhada nesse [Repositório](https://github.com/Yansb/full-cycle-course/tree/master/domain-driven-design)