Pular para o conteúdo
TDSoft

Do Monolito aos Microserviços

Escrito por: Fernando Matte Fagundes (CTO tdsoft)

Equipe TDSoft11 min de leitura

Do monolito aos microsservicos

O Contexto: uma plataforma em crescimento

A TDS Media Technology nasceu com um objetivo claro: modernizar a gestão comercial de emissoras de TV, rádio e broadcasting no Brasil. Nosso produto principal, um SaaS voltado ao mercado de mídia, evoluiu ao longo dos anos para se tornar uma referência no setor, atendendo grandes grupos de comunicação.

Por anos, toda essa plataforma viveu em um único projeto Spring MVC. Um monolito cuidadosamente construído, com módulos bem definidos, que atendia desde o agendamento de comerciais até a gestão de contratos, faturamento, programação e relatórios.

Funcionava. E funcionava bem. Mas o mercado de software não perdoa quem fica parado.

CONTEXTO TÉCNICO INICIAL

Stack monolítica: Java + Spring MVC · JSP · JPA/Hibernate · MySQL · jQuery · Deploy manual em servidor único · Sem orquestração de containers · CI/CD rudimentar com Ant/Maven

O Monolito: base sólida, limitações reais

É importante dizer com clareza: o monolito não era um problema de arquitetura ruim. Era o modelo correto para o estágio de crescimento em que a empresa se encontrava. Centralização significava simplicidade operacional, transações ACID naturais, deploy simples e onboarding rápido de novos desenvolvedores.

O que o monolito nos deu

  • Velocidade de entrega inicial — um único repositório, uma única base de código. Qualquer desenvolvedor conseguia modificar qualquer parte do sistema sem overhead de comunicação entre serviços.
  • Consistência de dados nativa — todas as operações de negócio compartilhavam o mesmo banco, garantindo integridade referencial e transações distribuídas sem qualquer esforço adicional.
  • Debugging simplificado — uma única stack trace, um único log, um único processo. Encontrar e corrigir bugs era direto ao ponto.

Quando os sinais de alerta apareceram

Com o crescimento da base de clientes e o aumento de funcionalidades, os primeiros sintomas surgiram de forma gradual — e depois, inevitável.

Acoplamento excessivo

Uma mudança no módulo de Faturamento exigia regressão em Programação, Contratos e CRM. O medo de mexer era real.

Deploy monolítico

Uma correção emergencial em um módulo exigia o redeploy de toda a aplicação. Downtime para mudanças pontuais.

Escalabilidade limitada

Não era possível escalar horizontalmente apenas o módulo de geração de relatórios (CPU-intensivo) sem escalar tudo.

Tecnologia engessada

Toda a plataforma era forçada a usar as mesmas versões de Spring, Java e dependências — independente das necessidades de cada domínio.

O monolito não falhou conosco. Fomos nós que crescemos além do que ele foi projetado para suportar.

— Reflexão da equipe de engenharia · TDS Media

A Decisão: por que microsserviços

A escolha pela arquitetura de microsserviços não foi impulsiva. Levamos meses analisando modelos alternativos: monolito modular, arquitetura em camadas redesenhada e decomposição baseada em domínios de negócio.

A conclusão foi que o modelo de microsserviços atendia melhor nossas duas necessidades críticas: permitir que novas funcionalidades fossem desenvolvidas com maior flexibilidade pela área de negócio, e dar à área de tecnologia os meios para atender essa demanda com velocidade e qualidade.

ANTES · MONOLITO                   DEPOIS · MICROSSERVIÇOS
────────────────────────────────   ────────────────────────────────────
▸ Spring MVC (JSP)                 ▸ Spring Boot · REST/RabbitMQ
▸ Deploy manual · servidor único   ▸ Docker · Rancher / Kubernetes
▸ Banco único MySQL                ▸ BD por domínio · MySQL + PostgreSQL
▸ Sem API Gateway                  ▸ Kong API Gateway
▸ Sessão HTTP stateful             ▸ JWT stateless
▸ Logs locais por instância        ▸ Graylog + Elasticsearch
▸ CI/CD manual                     ▸ Jenkins + Blue Ocean
▸ Escalabilidade vertical          ▸ Escalabilidade horizontal

Os Desafios da Migração

Se houvesse uma única frase para resumir o que aprendemos sobre migração de monolito para microsserviços, seria esta: os problemas técnicos são menores do que os problemas de cultura e processo. Mas os técnicos também existem — e são significativos.

1. Decomposição de Domínios de Negócio

A primeira pergunta — e mais difícil — foi: onde cortar? Em um monolito bem construído, os módulos frequentemente compartilham entidades, lógica e até tabelas de banco. Separar esses nós de forma cirúrgica, sem quebrar funcionalidades em produção, exigiu meses de análise de domínio com toda a equipe.

Adotamos os princípios do Domain-Driven Design (DDD) para mapear Bounded Contexts. Cada contexto delimitado se tornaria, eventualmente, um microsserviço. O processo revelou acoplamentos que não eram visíveis no código, mas viviam nos dados.

ARMADILHA COMUM

Evite a tentação de criar microsserviços muito granulares no início. Um serviço por entidade (ServicoDeContrato, ServicoDeFatura) frequentemente gera mais complexidade do que valor. Comece com serviços maiores e divida quando a necessidade for clara.

2. Comunicação Entre Serviços

No monolito, uma chamada de método entre módulos é síncrona, local e imediata. Em microsserviços, essa mesma operação cruza uma rede, pode falhar, pode demorar, pode estar indisponível. Toda a lógica de negócio que assumia confiabilidade absoluta precisou ser revisitada.

Adotamos uma estratégia híbrida: REST síncrono para operações que exigem resposta imediata (consultas, validações) e RabbitMQ assíncrono para operações de processamento, notificações e integrações que toleram latência.

// Padrão de circuit breaker com Resilience4j
// — proteção contra falha em cascata entre serviços
@CircuitBreaker(name = "programacao-service", fallbackMethod = "fallbackProgramacao")
@Retry(name = "programacao-service")
public ProgramacaoDTO buscarProgramacao(String broadcasterId) {
    return programacaoClient.findByBroadcaster(broadcasterId);
}

private ProgramacaoDTO fallbackProgramacao(String id, Exception ex) {
    log.warn("Circuit aberto para broadcaster {}. Usando cache.", id);
    return cacheService.getLastKnownProgramacao(id);
}

3. Gestão de Dados Distribuídos

Este foi, sem dúvida, o desafio mais custoso. No monolito, uma operação de negócio complexa — como fechar um contrato de publicidade e provisionar horários na grade — era uma única transação ACID. Em microsserviços, essa mesma operação envolve múltiplos serviços e múltiplos bancos de dados.

Get Fernando Matté Fagundes’s stories in your inbox

Join Medium for free to get updates from this writer.

Subscribe

Remember me for faster sign in

A solução foi a implementação do padrão SAGA com orquestração via RabbitMQ. Cada etapa do processo de negócio publica um evento. Se uma etapa falha, eventos de compensação são disparados para reverter as etapas anteriores.

COMPLEXIDADE SUBESTIMADA

• Transações distribuídas não têm atomicidade garantida — a consistência eventual é um paradigma mental diferente

• Detectar e tratar mensagens duplicadas (idempotência) se torna obrigatório

• A migração de dados do banco único para bancos por serviço exigiu scripts complexos e janelas de manutenção cuidadosas

4. Observabilidade Distribuída

No monolito, debugar um problema era relativamente simples: uma requisição, uma stack trace, um arquivo de log. Em microsserviços, uma única operação do usuário pode percorrer 5, 8, 10 serviços diferentes. Sem observabilidade adequada, o sistema se torna uma caixa preta distribuída.

Construímos três camadas de visibilidade:

CAMADA 1 · LOGS

Graylog + Elasticsearch

Centralização de logs de todos os serviços com correlação por traceId. Permite filtrar todos os logs de uma operação específica independente de quantos serviços ela cruzou.

CAMADA 2 · TRACING

Spring Cloud Sleuth + Zipkin

Rastreamento distribuído de ponta a ponta. Cada serviço propaga automaticamente o traceId e spanId, permitindo visualizar o caminho completo de uma requisição e identificar gargalos de latência.

CAMADA 3 · MÉTRICAS

Weave Scope + Spring Boot Actuator

Monitoramento em tempo real dos containers e saúde dos serviços. O Rancher Health Check integrado ao endpoint /actuator/health permite autorrecuperação de serviços com falha.

5. API Gateway e Segurança

Com dezenas de serviços expostos, centralizar as preocupações transversais — autenticação, rate limiting, logging de acesso, versionamento — tornou-se essencial. Adotamos o Kong como API Gateway por sua extensibilidade via plugins e por ser open source.

O modelo de segurança foi redesenhado para o paradigma distribuído: o Gateway autentica e emite um JWT (RFC 7519). Cada serviço, de forma independente, valida o token e extrai as informações de identidade e permissão. Não há mais sessão centralizada — cada requisição é autossuficiente.

DECISÃO DE ARQUITETURA

O JWT carrega no payload os dados de tenant, roles e permissions do usuário. Isso elimina chamadas ao serviço de autenticação a cada requisição, reduzindo latência e criando resiliência: mesmo que o serviço de autenticação esteja temporariamente indisponível, requisições com tokens válidos continuam funcionando.

6. Deployment e Orquestração

Gerenciar um monolito em produção é relativamente simples. Gerenciar 15, 20, 30 serviços independentes — cada um com sua própria versão, suas próprias variáveis de ambiente, seus próprios requisitos de recursos — é uma disciplina completamente diferente.

A adoção do Docker como padrão de empacotamento foi a decisão correta: cada serviço se torna uma imagem reprodutível, agnóstica de ambiente. O Rancher (atualmente evoluído para RKE2/Kubernetes) assumiu a orquestração, garantindo disponibilidade, balanceamento de carga e autorrecuperação de serviços.

O pipeline de CI/CD com Jenkins + Blue Ocean automatizou todo o fluxo: push no repositório Git dispara o build, testes, geração da imagem Docker, push no Registry e deploy orquestrado no Rancher. O que antes era um deploy manual arriscado se tornou um processo reprodutível e auditável.

7. A Dimensão Humana e Cultural

Nenhuma mudança técnica é apenas técnica. Migrar para microsserviços exigiu uma mudança profunda na forma como a equipe pensa sobre responsabilidade, ownership e colaboração.

Desenvolvedores acostumados a ter acesso a todo o codebase precisaram aprender a trabalhar com contratos de API como fronteiras de equipe. A cultura de “funciona na minha máquina” precisou evoluir para “funciona em qualquer container, em qualquer ambiente”.

INVESTIMENTO SUBESTIMADO

O custo de treinamento, de redefinição de processos e de resistência cultural à mudança foi significativamente maior do que qualquer desafio técnico. Reserve tempo e energia para essa dimensão — ela decide o sucesso da migração.

Os Benefícios Conquistados

Passado o período de turbulência da migração, os ganhos se tornaram tangíveis e mensuráveis. Não são promessas de arquitetura — são resultados observados no dia a dia da operação.

  • Deploy independente e seguro — um time pode fazer deploy do serviço de Programação sem nenhum impacto no serviço de Faturamento. Deploys mais frequentes, menos risco, janelas de manutenção eliminadas para mudanças pontuais.
  • Escalabilidade seletiva — módulos com alta demanda computacional — como geração de relatórios e processamento de mídia — podem ser escalados horizontalmente de forma isolada, sem custo de escalar toda a plataforma.
  • Isolamento de falhas — uma falha em um serviço periférico não derruba toda a plataforma. Com circuit breakers e fallbacks implementados, o sistema degrada de forma controlada e elegante.
  • Liberdade tecnológica por domínio — novos serviços podem adotar tecnologias adequadas ao seu domínio. Serviços com requisitos de baixa latência usam Redis. Serviços analíticos usam PostgreSQL. Não há mais um denominador comum forçado.
  • Velocidade de entrega de novas funcionalidades — times menores, com contexto delimitado, entregam funcionalidades com mais velocidade e menos risco de regressão em domínios não relacionados.
  • Observabilidade de nível de produção — a combinação de Graylog, Zipkin e métricas de container nos dá uma visão do sistema que o monolito jamais poderia oferecer. Sabemos exatamente onde cada milissegundo é gasto.

Lições Aprendidas

Se você está considerando uma migração similar, estas são as lições que pagamos para aprender:

  1. Não migre tudo de uma vez — nunca. A estratégia Strangler Fig funciona: extraia serviços periféricos primeiro, valide os padrões, ganhe confiança. O núcleo do monolito pode ser o último a sair. Tentativas de big-bang rewrite têm histórico consistente de falha.
  2. Invista em observabilidade antes de precisar dela. Não coloque serviços em produção sem logging centralizado, distributed tracing e health checks. Em microsserviços, você está cego sem observabilidade — e os problemas aparecem nas piores horas.
  3. Defina contratos de API como cidadãos de primeira classe. Trate as interfaces entre serviços com o mesmo rigor que você trata código de produção. Versione-as, documente-as, teste-as com consumer-driven contracts. A API é o produto que um serviço entrega para os outros.
  4. A consistência eventual não é opcional — é um paradigma. Aceitar e projetar para consistência eventual desde o início é fundamental. Tentar forçar semântica de transação ACID em sistemas distribuídos gera complexidade sem garantias.
  5. Automatize o pipeline desde o primeiro serviço. Com um serviço, um deploy manual é gerenciável. Com dez, é caos. Com vinte, é impossível. O investimento no pipeline de CI/CD se paga no segundo ou terceiro serviço.
  6. Cuide das pessoas antes do código. A mudança de paradigma mental de um desenvolvedor de monolito para microsserviços é significativa. Invista em treinamento, pair programming, documentação e na criação de uma cultura de ownership por domínio.

Conclusão

A migração de um monolito para microsserviços não é uma receita de bolo. É uma jornada — longa, por vezes frustrante, frequentemente esclarecedora. Para a TDS Media Technology, foi a decisão certa no momento certo.

Hoje, nossa plataforma atende centenas de empresas de mídia com uma arquitetura que consegue crescer, evoluir e se adaptar sem que o passado seja um peso. Novos módulos são adicionados sem medo de regressão global. Novas tecnologias são adotadas onde fazem sentido. Times diferentes trabalham em paralelo com autonomia real.

O monolito foi o alicerce. Os microsserviços são o edifício que estamos construindo sobre ele. E a lição mais importante é que nenhuma arquitetura é permanente — o que importa é a capacidade de evoluir.

Gostou deste artigo?

Receba os próximos no seu e-mail. Sem spam: você sai quando quiser.

← Voltar para o blog