Analytics e Múltiplos Eventos
Papel do Analytics no ecossistema
Modelando o serviço de Analytics como consumidor de eventos de domínio.
Nesta aula você vai
- Entender por que Analytics não deve consultar bancos de outros serviços
- Definir contratos de evento que alimentam o read model
- Criar pipeline inicial para processar order.created payment.approved e notification.sent
Papel do Analytics no ecossistema
Objetivos
- Separar claramente modelo transacional e modelo analítico no monorepo.
- Consumir eventos de
order-service,payment-serviceenotification-service. - Construir a base para métricas quase em tempo real sem acoplamento de banco.
Pré-requisitos
- Docker, Docker Compose e Kafka em execução.
- Node.js 20+ no
analytics-service. - Serviços do monorepo funcionando: customer (Java/Spring), order (Python/FastAPI), payment (Go/Gin), notification (Ruby/Sinatra).
Conceito
No e-commerce distribuído, analytics-service não pode fazer SELECT nos bancos dos outros contextos. A fonte oficial para leitura analítica é o fluxo de eventos. Nesta matéria, o analytics escuta:
order.createdpayment.approvednotification.sent
Com isso, você monta um read model próprio, versionável e otimizado para consultas.
Estrutura de arquivos
services/analytics-service/
├── src/
│ ├── app.js
│ ├── consumers/
│ │ └── analytics-consumer.js
│ ├── handlers/
│ │ ├── order-created-handler.js
│ │ ├── payment-approved-handler.js
│ │ └── notification-sent-handler.js
│ └── store/
│ └── metrics-store.js
└── package.json
Passo a passo
- Crie o store em memória (simples para laboratório) em
services/analytics-service/src/store/metrics-store.js:
const state = {
totals: {
orders: 0,
paymentsApproved: 0,
notificationsSent: 0,
},
revenueCents: 0,
lastUpdatedAt: null,
};
export function incrementOrders() {
state.totals.orders += 1;
state.lastUpdatedAt = new Date().toISOString();
}
export function incrementPaymentsApproved(amountCents) {
state.totals.paymentsApproved += 1;
state.revenueCents += amountCents;
state.lastUpdatedAt = new Date().toISOString();
}
export function incrementNotificationsSent() {
state.totals.notificationsSent += 1;
state.lastUpdatedAt = new Date().toISOString();
}
export function getMetricsSnapshot() {
return structuredClone(state);
}
- Crie handlers por domínio:
// services/analytics-service/src/handlers/order-created-handler.js
import { incrementOrders } from "../store/metrics-store.js";
export async function handleOrderCreated(event) {
if (!event?.payload?.orderId) return;
incrementOrders();
}
// services/analytics-service/src/handlers/payment-approved-handler.js
import { incrementPaymentsApproved } from "../store/metrics-store.js";
export async function handlePaymentApproved(event) {
const amountCents = Number(event?.payload?.amountCents ?? 0);
incrementPaymentsApproved(amountCents);
}
// services/analytics-service/src/handlers/notification-sent-handler.js
import { incrementNotificationsSent } from "../store/metrics-store.js";
export async function handleNotificationSent(event) {
if (!event?.payload?.channel) return;
incrementNotificationsSent();
}
- Registre as rotas de tipo de evento em
services/analytics-service/src/consumers/analytics-consumer.js:
import { handleOrderCreated } from "../handlers/order-created-handler.js";
import { handlePaymentApproved } from "../handlers/payment-approved-handler.js";
import { handleNotificationSent } from "../handlers/notification-sent-handler.js";
export const handlersByType = {
"order.created": handleOrderCreated,
"payment.approved": handlePaymentApproved,
"notification.sent": handleNotificationSent,
};
Como testar
Suba o ambiente:
docker compose up -d kafka redis analytics-service notification-service payment-service order-service
Publique eventos de teste no tópico principal (via API dos serviços ou producer utilitário) e valide:
curl -s http://localhost:8085/health
Se o analytics-service subir sem erro e os handlers forem chamados, você já tem a fundação para as próximas duas aulas.
Dicas de projeto
- Separe handler por evento para evitar
switchgigante. - Não use o payload cru em toda a aplicação; normalize primeiro.
- Deixe o read model idempotente desde o começo (vamos reforçar na aula 2).
Erros comuns
- Ler banco do
payment-servicepara calcular receita. - Não versionar contrato de evento.
- Acoplar nome de tópico a regra de negócio.
Resumo
Você definiu o papel do analytics-service no ecossistema e criou a base de consumo de eventos order.created, payment.approved e notification.sent, sem dependência dos bancos transacionais.