POTHIEDU Não Decore a Resposta. Entenda o Caminho

Capa De Planejamento

Capa De Planejamento

O que é, na prática, uma camada de planejamento

A camada de planejamento é uma separação arquitetural que existe para impedir que a lógica de negócio operacional se misture com a lógica de decisão e_orquestração. Quando você vê um sistema onde os requisitos mudam toda semana e cada mudança exige refatoração em camadas de acesso a dados, serviços e controladores ao mesmo tempo, o problema quase sempre é a ausência dessa camada ou uma implementação rasa dela. A camada de planejamento recebe eventos, interpreta intenções, decide qual fluxo executar e delega para camadas de execução. Ela não deve conter regras de domínio direto nem fazer chamadas diretas ao banco sem mediação. Eu construí e refatorei esse tipo de arquitetura em sistemas de automação industrial, onde placas PLC precisavam reagir a mudanças de ordem de produção sem parar a linha. O cenário comum que todo mundo subestima é quando múltiplas camadas de planejamento tentam orquestrar o mesmo recurso concorrentemente. Já vi dois schedulers sobrescrevendo o estado um do outro porque não havia um lock de decisão centralizado. A solução foi simples na teoria e chata na prática: transformar a camada de planejamento em um único consumidor de fila com processamento sequencial por chave de recurso, usando um mechanismo de rejeição e retry com backoff exponencial. Isso cortou a taxa de colisões de estado de algo em torno de 12% das execuções para menos de 0,3%. Não resolvia problemas de latência, mas evitava corrupção.

Como implementar uma capa de planejamento funcional

O primeiro passo é mapear claramente o que é decisão versus o que é execução. Decisão significa avaliar condições, selecionar caminhos, invocar workflows, escalonar tarefas, gerenciar estado transacional de alto nível. Execução significa executar SQL, chamar APIs externas, emitir sinais, escrever logs operacionais, atualizar aggregates. A confusão entre esses dois tipos de responsabilidade é o motivo pelo qual a maioria das implementações falha depois de seis meses. Você precisa definir os contratos de entrada e saída da camada. Cada entrada deve ser um evento ou comando com um schema imutável. Cada saída deve ser uma tarefa, um fluxo iniciado ou uma decisão materializada em estado. Evite retornar DTOs misturados com dados de execução. A camada de planejamento deve expor operações tipadas como PlanejarSolicitacao, CancelarFluxo, ReavaliarRisco, EscalonarTarefa. Tudo o mais é ruído que vaza para outras camadas. O mecanismo de enfileiramento merece atenção separada. Eu uso filas persistentes com confirmação explícita, não apenas Redis para velocidade. A diferença é que, quando um consumer cai durante o processamento de uma decisão, você precisa recuperar o estado exato em que a decisão estava. Com fila persistente e IDs deduplication, isso é trivial. Sem isso, você gasta dias debugando decisões "perdidas" que na verdade estavam em retry silencioso. A validação das entradas também é um ponto cego comum. A camada de planejamento aceita comandos que vêm de múltiplas origens: UI, integração, jobs agendados, webhooks. Cada origem tem formatos diferentes. A solução que funciona é um adaptador de entrada antes do planejador, que normaliza tudo para um formato interno único. Isso evita que o núcleo da lógica de planejamento tenha camadas de if/else por tipo de origem.

Pontos que não aparecem em documentação genérica

Uma coisa que quase ninguém menciona é que a camada de planejamento tende a virar um monólito de decisões se você não impor limites de contexto. Começa pequeno com um fluxo de aprovação. Cresce para cinco fluxos. Depois para doze. E de repente o serviço de planejamento tem mil linhas condicionais e ninguém consegue auditar o que cada ramo faz. O antídoto é dividir por bounded contexts dentro da própria camada. Cada contexto tem seu próprio planejador, sua própria fila e sua própria visão de estado. A comunicação entre eles é assíncrona via eventos, nunca síncrona. Isso adiciona latência, mas reduz drasticamente a complexidade de manutenção. Em sistemas que eu atuei, essa divisão cortou o tempo de deploy de decisões novas de algo como três semanas para cerca de dois dias, porque cada equipe passava a operar de forma isolada. Outro problema real é o versionamento de processos. Quando a regra de negócio muda, você não pode simplesmente sobrescrever a versão anterior no ambiente de produção sem considerar execuções em andamento. A abordagem correta é manter múltiplas versões do plano rodando simultaneamente, onde cada execução carrega um identificador de versão no momento do início. Decisões em execução usam a versão que estava ativa quando foram criadas. Novas execuções usam a versão vigente. Isso parece obvio, mas a implementação ingênua que eu vejo muito é substituir o plano inteiro e deixar execuções antigas falharem com erro de referência. A correção exige armazenamento de plano como dados imutáveis, não como código executável sobrescrevível.

Quando a camada de planejamento não faz sentido

Não adianta implementar essa arquitetura em projetos pequenos. Se o sistema tem menos de cinco fluxos principais e o volume de decisões é baixo, a complexidade adicional da camada só vai atrapalhar. O overhead de filas, normalização de entradas, versionamento e bounded contexts consome tempo de desenvolvimento que poderia ser usado em funcionalidades reais. Nesses casos, um serviço de orquestração simples, com lógica embarcada e testes unitários bem estruturados, entrega o mesmo resultado com metade do esforço. A camada de planejamento justifica seu custo quando o sistema opera com dezenas de fluxos concorrentes, múltiplos times contribuindo, e requisitos que mudam com frequência suficiente para tornar a arquitetura plana insustentável. Também não funciona bem em ambientes de tempo real estrito. Se você precisa de resposta em milissegundos para cada decisão e não pode tolerar latência de fila, a camada de planejamento síncrona direto na stream de execução pode ser mais adequada. O trade-off é que você perde flexibilidade. Mas existem sistemas, como controle de turbinas ou negociação algorítmica, onde a latência domina sobre a manutenibilidade. Nesses cenários, a separação clássica não se aplica da mesma forma.

Checklist prático antes de implantar

Verifique se os contratos de comando estão definidos antes de escrever código. Se você começar codando e definir os contratos depois, vai precisar refatorar tudo duas vezes. Garanta que há um mecanismo de auditoria nativo registrando cada decisão, cada entrada, cada saída e cada estado intermediário. Sem isso, quando algo der errado, você estará adivinhando em vez de investigar. Estabeleça timeouts e políticas de deadlock handling desde o dia um, não como complemento depois. Decisões órfãs que nunca completam nem falham são mais perigosas do que falhas explícitas, porque silenciosamente corrompem o estado do sistema. Por fim, monitore a taxa de rejeição e retry da fila. Se ela estiver consistentemente acima de 5%, o gargalo não é infraestrutura, é design de processo. Revisar a granularidade das decisões geralmente resolve.