Skip to content

Guia de Desenvolvimento: Implementando Domain Events

A comunicação entre domínios é assíncrona e desacoplada, utilizando um barramento de eventos centralizado. Este guia fornece um passo a passo para implementar o fluxo de publicação e subscrição.

Fluxo de Trabalho (Workflow)

Para implementar um novo evento de domínio, siga esta sequência lógica:

Passo 1: Definir o Contrato (Contracts)

Todo evento deve ter um contrato claro em packages/contracts/src/events.ts.

  1. Adicione o nome do evento ao enum DomainEventNames:

    typescript
    export enum DomainEventNames {
      // ... outros eventos
      PATIENT_ARRIVED = 'PATIENT_ARRIVED'
    }
  2. Defina a interface do Payload no interface DomainEventPayloads:

    typescript
    export interface DomainEventPayloads {
      // ... outros payloads
      [DomainEventNames.PATIENT_ARRIVED]: {
        patientId: string;
        arrivalTime: string;
        priority: number;
      };
    }

Passo 2: Publicar o Evento (Publisher)

A publicação deve ocorrer no domínio onde a ação originou, geralmente após o sucesso de uma operação assíncrona.

  1. No Saga do módulo (packages/modules/[module]/store/sagas.ts):
    typescript
    import { domainEventsBus } from 'core/domain-events';
    import { DomainEventNames } from 'contracts/events';
    
    function* handleArrivalSuccess(data: any) {
      // Publicação Segura: O barramento valida o payload contra o contrato
      domainEventsBus.publish(DomainEventNames.PATIENT_ARRIVED, {
        patientId: data.id,
        arrivalTime: new Date().toISOString(),
        priority: data.priorityLevel
      });
    }

Passo 3: Assinar o Evento (Subscriber)

Qualquer domínio interessado no evento deve se inscrever via canal de eventos.

  1. No Saga do módulo interessado (packages/modules/[other-module]/store/sagas.ts):
    typescript
    import { take, put, call } from 'redux-saga/effects';
    import { domainEventsBus } from 'core/domain-events';
    import { DomainEventNames } from 'contracts/events';
    
    export function* watchArrivalEvents() {
      // 1. Cria o canal de comunicação com o barramento
      const channel = domainEventsBus.createChannel();
      
      while (true) {
        // 2. Aguarda o próximo evento disparado no ecossistema
        const event = yield take(channel);
        
        // 3. Filtra o evento de interesse
        if (event.name === DomainEventNames.PATIENT_ARRIVED) {
          // 4. Executa a ação reativa (ex: criar registro na fila)
          yield put(actions.addToQueue(event.payload));
        }
      }
    }

Regras e Boas Práticas (Checklist)

  • [ ] Payloads Enxutos: Passe apenas IDs. Se precisar de mais dados, o assinante deve buscar na API ou em sua própria store.
  • [ ] Nomes no Passado: Eventos descrevem fatos (CONSULTATION_FINISHED, não FINISH_CONSULTATION).
  • [ ] Idempotência: Garanta que se o mesmo evento chegar duas vezes, o sistema não corrompa os dados (ex: verificar se o paciente já está na fila antes de adicionar).
  • [ ] Tratamento de Erros: Um erro no Subscriber não deve "matar" o Saga do Publisher. Use blocos try/catch dentro do loop do Subscriber.

Exemplo de Falha vs Sucesso

❌ Errado: Chamar chatActions.createRoom() diretamente de dentro do domínio de Consultation. Isso cria acoplamento lateral.

✅ Correto: Consultation publica CONSULTATION_STARTED. O domínio de Chat ouve e decide por conta própria criar a sala.