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.
Adicione o nome do evento ao
enum DomainEventNames:typescriptexport enum DomainEventNames { // ... outros eventos PATIENT_ARRIVED = 'PATIENT_ARRIVED' }Defina a interface do Payload no
interface DomainEventPayloads:typescriptexport 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.
- No Saga do módulo (
packages/modules/[module]/store/sagas.ts):typescriptimport { 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.
- No Saga do módulo interessado (
packages/modules/[other-module]/store/sagas.ts):typescriptimport { 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ãoFINISH_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/catchdentro 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.