Novo Portal Gazeta Machadense
Reconstrução do portal de notícias: um backend monolítico modular em Java servindo portal, admin e app.

A Gazeta Machadense precisava reconstruir seu ecossistema de notícias (~500 mil acessos/mês) com um backend único que servisse três frontends — portal, painel administrativo e app — sem cair na complexidade operacional de microsserviços, com um time pequeno (1–3 devs). Os desafios: manter os domínios isolados e coesos, ter busca rápida, atender dois públicos de autenticação (leitor e staff) e respeitar exigências legais do jornalismo, como retratação.
Três frontends (portal Next.js, admin React, app Flutter) consomem um único backend monolítico modular; PostgreSQL é a fonte da verdade, com Redis e Meilisearch ao lado, e integrações externas para mídia, pagamento, identidade e monitoramento.
- 01Monolito modular, não microsserviços (ADR-001)
Um deploy único dividido em 11 módulos de domínio que só dependem de um módulo shared — nenhum módulo de domínio depende de outro. Eles conversam por Spring Application Events (@TransactionalEventListener, após o commit) ou por interfaces publicadas em shared. Resultado: latência interna ~zero e transações ACID atravessando contextos, com a fronteira garantida pela própria estrutura de pacotes. Escolha consciente para um time de 1–3 devs.
- 02Hexagonal pragmático por módulo
Cada módulo (iam, content, media, publishing, ads, billing, newsletter, comments, analytics, search) segue sempre o mesmo corte: domain → repository → service → api → mapper. O módulo é a fronteira, sem ports/adapters cerimoniais — quem aprende um, aprende todos, e um módulo pode ser extraído como microsserviço no futuro.
- 03Borda comum em shared
Tudo que é transversal vive em shared: BaseEntity (ID em ULID, timestamps, auditoria), tratamento de erro em Problem+JSON (RFC 7807), envelope HTTP, paginação por cursor (base64 de {published_at, id}) e correlação de request (MDC/RequestId). Os módulos não reinventam o básico.
- 04Dois fluxos de autenticação por path (ADR-005)
Públicos diferentes, segurança diferente: /api/v1/public/* atende leitores via Firebase Auth (token opcional, perfil espelhado na tabela users); /api/v1/admin/* atende o staff via Spring Security 6 + JWT próprio, com RBAC granular e refresh tokens em Redis.
- 05Decisões com trade-off explícito (ADRs)
As escolhas pesadas viram ADRs, cada uma amarrada ao problema que resolve: PostgreSQL como fonte da verdade; cursor pagination em vez de offset (o feed cresce e offset duplica registros); virtual threads porque a carga é I/O-bound; e soft delete porque retratação jornalística tem risco legal.
Integrações externas
Além do núcleo, o portal integra Meilisearch para busca, Cloudflare (R2/Images/Stream) para mídia, Stripe + Connect para pagamento e split, Firebase Admin para a identidade dos leitores e Sentry para monitoramento de erros — cada um isolado atrás do seu módulo.
O resultado é um backend único, simples de operar para um time pequeno, mas com a disciplina de fronteira de um sistema modular: domínios isolados que conversam por eventos, busca rápida, dois fluxos de autenticação e a porta aberta para extrair qualquer módulo como microsserviço quando — e se — fizer sentido. Um só deploy serve portal, admin e app.