GH GambleHub

Read Models e projeções

O Read Model é uma tabela/índice/vista especialmente projetada para leituras rápidas sob um cenário de alimentos específico. A projeção é um processo que converte eventos/alterações de origem em atualizações do Read Model (normalmente idempotent upsert). Com o CQRS, isso permite descarregar o núcleo OLTP e estabilizar p95/p99 leituras, controlando o «frescor».

Ideias principais:
  • Denormalizar sob pedido, não «padrão universal».
  • Atualizar de forma escalonada e idimpotente.
  • Controlar estaleness e ordem claramente.

1) Quando usar o Read Models (e quando não)

Adequado:
  • Leituras pesadas frequentes (joinas/agregações/arrumações) com atrasos de atualização válidos.
  • Dashboards, catálogos, lendings, top-N, fidas pessoais, listas de busca.
  • Divisão de carga: núcleo write - rigoroso, plano read - rápido e escalável.
Não é adequado:
  • Operações que exigem invariantes rigorosos «por cada gravação» (dinheiro, exclusividade). É strong path.

2) Circuito arquitetônico (padrão verbal)

1. Origem de alterações: eventos de domínio (event surcing) ou CDC do OLTP.
2. Linha de montagem: parser → agregação/desnormalização → idempotent upsert.
3. Read Store: BD/índice otimizado sob consulta (RDBMS, colinvertebrados, busca).
4. API/cliente: SELECT/GET rápido, com os atributos «as _ of/freshness».

3) Design do Read Model

Comece com a consulta: quais campos, filtros, ordens, paginação, top-N?
Mantenha os dados já combinados (nomes, somas, estatais).

Chaves:
  • Particionamento por 'tenant _ id', data, região.
  • Primary key: chave de negócio + baquete de tempo (por exemplo, '(tenant _ id, entity _ id)' ou '(tenant _ id, bucket _ minuto)').
  • Índices: por where/order by frequente.
  • TTL/retenshn: para vitrines temporárias (por exemplo, 90 dias).

4) Fluxo de atualizações e idempotação

Idempotent upsert é a base da estabilidade das projeções.

Pseudo:
sql
-- Projection table
CREATE TABLE read_orders (
tenant_id  TEXT,
order_id  UUID,
status   TEXT,
total    NUMERIC(12,2),
customer  JSONB,
updated_at TIMESTAMP,
PRIMARY KEY (tenant_id, order_id)
);

-- Idempotent update by event
INSERT INTO read_orders(tenant_id, order_id, status, total, customer, updated_at)
VALUES (:tenant,:id,:status,:total,:customer,:ts)
ON CONFLICT (tenant_id, order_id) DO UPDATE
SET status = EXCLUDED. status,
total = EXCLUDED. total,
customer = COALESCE(EXCLUDED. customer, read_orders. customer),
updated_at = GREATEST(EXCLUDED. updated_at, read_orders. updated_at);
Regras:
  • Cada mensagem traz uma versão/hora; aceitamos apenas «fresco ou igual» (idempotency).
  • Para aparelhos (contadores, somas) - Armazene o state e use atualizações de switch (ou abordagens CRDT).

5) Origem de alterações: eventos vs CDC

Eventos (event surcing): rica semântica, fácil de construir diferentes projeções; a evolução dos circuitos é importante.
CDC (replicação lógica): basta ligar ao banco de dados existente; vai precisar de um mapping de DML→sobyty e filtragem de updates de ruído.

Ambas as opções exigem:
  • Garantias de entrega (at-least-once) e DLQ para mensagens «venenosas».
  • Ordem da chave (partition key = 'tenant _ id: entity _ id').

6) Ordem, causalidade e «frescura»

Ordem da chave: os eventos de um único objeto devem vir em sequência; use a partilha e versões.
Causalidade (sessão/causal): Para que o autor veja suas alterações (RYW), envie versões watermark nas solicitações.
Recente (bounded staleness): devolva 'as _ of '/' X-Data-Freshness' e mantenha o SLO (por exemplo, p95 ≤ 60 c).

7) Unidades incorporadas e top-N

Exemplo de baquetes de venda de minutos:
sql
CREATE TABLE read_sales_minute (
tenant_id TEXT,
bucket  TIMESTAMP, -- toStartOfMinute revenue  NUMERIC(14,2),
orders  INT,
PRIMARY KEY (tenant_id, bucket)
);

-- Update by Event
INSERT INTO read_sales_minute(tenant_id, bucket, revenue, orders)
VALUES (:tenant,:bucket,:amount, 1)
ON CONFLICT (tenant_id, bucket) DO UPDATE
SET revenue = read_sales_minute. revenue + EXCLUDED. revenue,
orders = read_sales_minute. orders + 1;
Para Top N:
  • Suporte a vitrine classificada (por exemplo, por 'revenue DESC') e atualize apenas as posições alteradas (heap/skiplist/limited place).
  • Guarde a «janela» do top (por exemplo, 100-1000 linhas por segmento).

8) Projeções de busca e geo

Pesquisa (ES/Openserch): Documento denormalizado, transformações pipeline, versão do documento = versão da fonte.
Geo: Guarde 'POINT/LAT, LON', instale previamente os tales/quadrotros.

9) Multi-tenante e regiões

'tenant _ id' é obrigatório nas chaves de projeção e evento.
Fairness: Limite throughput projeções per tenant (WFQ/DRR) para evitar que o barulho freie os outros.
Residency: a projeção vive na mesma região do núcleo write; vitrines inter-regionais - equipamentos/resumos.

10) Observabilidade e SLO

Métricas:
  • 'project _ lag _ ms' (istochnik→vitrina), 'freshness _ age _ ms' (desde o último delta).
  • thughput updates, número de erros, DLQ-rate, redrive-sucess.
  • Tamanho das vitrines, p95/p99 latência de leitura.
Tracing/logs:
  • Теги: `tenant_id`, `entity_id`, `event_id`, `version`, `projection_name`, `attempt`.
  • Anotações: soluções merge, omissões de versões ultrapassadas.

11) Playbooks (runbooks)

1. Crescimento da laje: verificar o conector/corretor, aumentar as partições, priorizar vitrines-chave.
2. Muitos erros de padrão: congelar redrave, migrar circuitos (backfill), reiniciar com a nova versão do mapper.
3. DLQ repetido: reduzir batch, ativar o processador «shadow», aumentar a idempotidade.
4. Incoerência da vitrine: execute rebuild vitrines do diário/fonte por janela (seletivo por tenant/partition).
5. Chaves quentes: limitar a concorrência da chave, adicionar filas locais, levar a unidade para uma vitrine separada.

12) Contagem completa (rebuild) e backfill

Abordagem:
  • Parar o consumo (ou mudar para a nova versão da vitrine).
  • Repasse em lotes (por partituras/datas/tenantes).
  • Activar o suingue de duas fases: primeiro preencher 'read' v2 ', depois alternar atômico para a leitura.

13) Evolução dos circuitos (versionização)

'schema _ version' em eventos/documentos.
A projeção pode ler várias versões, migrar para trás.
Para grandes alterações, a nova vitrine v2 e o tráfego canário.

14) Segurança e acesso

Herda RLS/LCA da origem; Não façam uma vitrine mais acessível do que os dados originais.
Disfarce o PII em projeções que não são necessárias para UX/analistas.
Auditoria de Redryvs/Redefinições/Redefinições manuais.

15) Modelo de configuração

yaml projections:
read_orders:
source: kafka. orders. events partition_key: "{tenant_id}:{order_id}"
idempotency: version_ts upsert:
table: read_orders conflict_keys: [tenant_id, order_id]
freshness_slo_ms: 60000 dlq:
topic: orders. events. dlq redrive:
batch: 500 rate_limit_per_sec: 50 read_sales_minute:
source: cdc. orders partition_key: "{tenant_id}:{bucket_minute}"
aggregate: increment retention_days: 90 limits:
per_tenant_parallelism: 4 per_key_serial: true observability:
metrics: [projection_lag_ms, dlq_rate, redrive_success, read_p95_ms]

16) Erros típicos

«Uma vitrine para todos os casos» → updates pesados e p99 ruins.
Falta de idempotação → duplas/corridas nas unidades.
Dual-write diretamente para a vitrine e OLTP → divergências.
Visibilidade zero de frescura → conflito de expectativas com o produto.
Rebuild sem um suingue de duas fases → «buracos» nas respostas.
Não há partilha/índice → aumento de custo e latência.

17) Receitas rápidas

Catálogo/pesquisa: vitrine documental + upsert incorporativo, lag ≤ 5-15 c, índices abaixo dos filtros.
Dashboards: tanques de minutos/relógio, equipamentos 'SUM/COUNT', p95 frescura ≤ 60 c.
Fita pessoal: projeção por usuário + causal/RYW para o autor, fallback para o dinheiro.
SaaS global: Vitrines regionais, equipamentos cruzados-regionais; fairness per tenant.

18) Folha de cheque antes de vender

  • A vitrine foi projetada para um pedido específico; há índices e partituras.
  • Origem das alterações selecionada (eventos/CDC); garantias de entrega e ordem de chave.
  • Upsert Idempotent com versões/hora; protecção contra eventos antigos.
  • O SLO de frescura é definido e dado nas respostas ('as _ of/freshness').
  • O DLQ e o redrave seguro estão configurados; playbook em rebuild/backfill.
  • Restrições à concorrência (per-key serial) e fairness per tenant.
  • Métricas de laje/erro/latency, alertas em p95/p99 e crescimento do DLQ.
  • Versionização de circuitos e estratégia de migração (v2 + switch).
  • As políticas de acesso/PII foram herdadas e testadas.

Conclusão

O Read Models e Projeções é um acelerador de leitura de engenharia que paga um pequeno preço de «frescura» e infraestrutura de streaming para obter milissegundos previsíveis e descarregar o núcleo de gravações. Projete as vitrines sob demanda, torne os updates idimpotentes, mede a liga e claramente prometa frescura - e suas APIs permanecerão rápidas, mesmo com o aumento da carga de trabalho, dados e geografia.

Contact

Entrar em contacto

Contacte-nos para qualquer questão ou necessidade de apoio.Estamos sempre prontos para ajudar!

Iniciar integração

O Email é obrigatório. Telegram ou WhatsApp — opcionais.

O seu nome opcional
Email opcional
Assunto opcional
Mensagem opcional
Telegram opcional
@
Se indicar Telegram — responderemos também por lá.
WhatsApp opcional
Formato: +indicativo e número (ex.: +351XXXXXXXXX).

Ao clicar, concorda com o tratamento dos seus dados.