Há duas formas de errar na arquitetura de conteúdo e a maioria dos projetos escolhe uma. De um lado estão os puristas do tudo-no-Git, para quem corrigir um erro de digitação significa um branch, um pull request e um deploy completo. Do outro estão os que decidem que o CMS deveria conter tudo — strings de interface, configuração de layout, design tokens, tudo isso — até o site não conseguir renderizar um botão sem uma ida e volta na rede. Construí meu portfólio e este blog sobre a mesma camada de conteúdo, e a única coisa que se sustentou foi uma fronteira rígida entre as duas.
No começo, tudo o que eu construía vivia em arquivos dentro do repositório — INI, depois JSON —, tudo versionado e inteiramente na minha mão. Era simples até deixar de ser. No dia em que você quer corrigir uma frase pelo celular, ou publicar um pensamento sem abrir um editor, o conteúdo só-em-arquivos vira uma parede. Forçar um deploy para mover uma vírgula é desperdiçar um deploy.
Então, a primeira vez que peguei um CMS headless, eu me corrigi demais e coloquei tudo dentro dele — rótulos de UI, ajustes de tema, estado de componentes —, em tabelas remotas ao lado do texto de verdade. Isso troca um modo de falha por um pior. Agora o chrome da interface chega pela rede, a internacionalização vira uma chamada de API, e um soluço do CMS pode derrubar uma página que não tem motivo para depender do CMS. A resposta nunca foi um lado ou o outro. É um contrato entre o que muda todo dia e o que define o sistema.
O CMS fica com o conteúdo narrativo e mutável, e com mais nada — as coisas que crescem e mudam sem ninguém precisar refatorar. Neste blog isso é cada post, resumo e tópico: escrevo e publico de uma tela de administração sem tocar no código-fonte. No portfólio são os estudos de caso, os marcos, as habilidades; terminar um contrato é uma edição de conteúdo, não um release. Também é o lar certo para os poucos ajustes globais que precisam mudar sem um deploy: um aviso, os metadados do autor, uma feature flag. Se eu poderia rascunhar, revisar ou apagar por capricho, pertence ao banco de dados.
Qualquer coisa que decide como a aplicação se comporta, ou que precisa que o compilador verifique, fica no código. O dicionário de interface é o caso mais claro: os dois sites são localizados em seis idiomas, mas os rótulos de navegação, os textos dos botões, os placeholders e os aria-labels vivem em JSON tipado no repositório, porque buscá-los de um CMS adicionaria latência para renderizar o quadro e arriscaria uma chave ausente no meio da hidratação. O mesmo vale para regras de roteamento, configuração de temas, tempos de animação, schemas de validação: eles querem checagem em tempo de compilação, não uma linha de banco de dados. E os assets estáticos: imagens, ícones e miniaturas pertencem ao projeto, onde o build pode otimizá-los e o cache pode confiar neles, sem uma ida e volta de mídia ao CMS no caminho de renderização.
Algumas coisas não cabem em nenhuma caixa. Comentários, likes, contadores de visualização, registros de rate-limit, sessões — de alta frequência, transacionais, exigindo integridade real — não pertencem a um CMS feito para redigir nem a arquivos reconstruídos a cada deploy. As threads de comentários precisam de respostas, checagens de autenticação, moderação. Um contador de likes achataria uma reconstrução estática e se arrastaria por uma API de escrita de CMS. Rate limits e logs de acesso precisam de um banco de dados de verdade fazendo trabalho de verdade em tempo real. Esse é um terceiro armazenamento, e fingir que ele é um dos outros dois é como você acaba com uma versão lenta e frágil dos dois.
Histórias vão no CMS. Estrutura vai no código. Tudo o que conta, incrementa ou limita vai em um banco de dados que não faz nenhuma dessas coisas.
Arquitetura é, na maior parte, o ato de traçar linhas e então se recusar a movê-las por conveniência. Conteúdo e código têm ciclos de vida diferentes; no momento em que você para de fingir o contrário, as ferramentas param de brigar com você. O CMS me deixa publicar de qualquer lugar. O repositório mantém as partes que precisam estar certas em tempo de build realmente certas em tempo de build. A fronteira entre os dois é a única razão pela qual qualquer um deles é agradável de usar.
A fundação está lançada; agora é hora de construir.