COMO ESCALAMOS UM SISTEMA C# QUE LEVAVA 1H PARA PROCESSAR 1.000 OPERAÇÕES E REDUZIMOS PARA 10 MINUTOS
Recentemente eu e minha equipe entregamos um projeto desafiador em que revisitamos um sistema legado escrito em C#. Na versão antiga, todo o backend estava encapsulado num monólito .NET Framework, com algumas rotas expostas via WCF e hospedado atrás de um load balancer que distribuía tráfego para um grupo fixo de três instâncias EC2, sem qualquer mecanismo de auto scaling. Para completar, toda a leitura e escrita ocorriam no mesmo processo e os dados eram persistidos num SQL Server compartilhado — um cenário perfeito para bloqueios de banco de dados e filas de espera intermináveis sempre que múltiplas operações acessavam as mesmas tabelas.
Decidimos redesenhar completamente esse fluxo crítico. Em vez de processar tudo de uma vez, extraímos a etapa de ingestão para containers Docker, que rodam em clusters independentes e escalam horizontalmente com facilidade. Cada container agora envia lotes de registros diretamente para uma fila SQS, garantindo buffer, retries automáticos e desacoplamento total entre quem produz os dados e quem os consome.
No lado de consumo, migramos o processamento pesado para funções AWS Lambda acionadas pela própria fila SQS. Essas lambdas cuidam de todas as regras de negócio, transformações e enriquecimento de dados, aproveitando a elasticidade serverless para disparar dezenas ou centenas de instâncias em paralelo, conforme a demanda. O modelo “pague pelo que usar” também trouxe uma redução significativa de custos operacionais.
Após o processamento, os resultados são persistidos em um banco relacional no Amazon RDS. Aqui, aproveitamos transações ACID para manter a integridade e eliminamos a antiga disputa por locks, já que cada execução Lambda escreve em instâncias otimizadas para carga alta, sem interferir umas nas outras. Essa separação clara entre leitura, processamento e armazenamento foi fundamental para quebrar os gargalos históricos.
Conclusão? O impacto foi impressionante: o tempo total para processar 1.000 operações caiu de aproximadamente uma hora para apenas dez minutos. Além do ganho de performance, conquistamos maior resiliência — se um worker falha, a mensagem permanece na fila até ser reprocessada — e flexibilidade para evoluir cada componente sem afetar o restante da aplicação.
Essa migração reforçou uma lição que talvez seja a mais importante: arquitetura bem pensada faz tanta diferença quanto a tecnologia em si. Independente de escolher C#, Java, Go ou Node.js, é o desenho desacoplado — filas, containers, serverless e bancos dedicados — que garante throughput alto, latência previsível e manutenção simplificada.