Decisões de Arquitetura
Decisões de Design Arquitetônico
Esta página explica o raciocínio por trás das principais escolhas tecnológicas e arquiteturais do PgArachne. Estas decisões foram tomadas para priorizar performance, segurança e produtividade do desenvolvedor, garantindo ao mesmo tempo alta compatibilidade com agentes de IA e modelos LLM modernos.
1. JSON-RPC 2.0 vs. REST
O PgArachne utiliza JSON-RPC 2.0 como seu protocolo de comunicação primário em vez do tradicional REST.
Por que JSON-RPC 2.0:
- Endpoint único: Toda a comunicação ocorre via
POST /{prefixo}/{banco_de_dados}/jsonrpc. Não há necessidade de projetar estruturas de URL complexas. - Chamadas autocontidas: Cada requisição é um objeto JSON completo (método + parâmetros + id), fácil de gerar e processar para LLMs.
- Tratamento de erros padronizado: Códigos e mensagens de erro fazem parte da especificação.
- Loteamento (Batching): O protocolo suporta nativamente requisições em lote em uma única viagem de ida e volta HTTP.
- Descoberta (Discovery): O endpoint de capacidades fornece uma descrição completa da API para agentes de IA sem alucinações.
Por que não REST:
- Complexidade para IA: A semântica REST está dispersa em vários lugares, dificultando a construção de chamadas confiáveis para agentes de IA.
- Exposição do esquema: O CRUD sobre tabelas muitas vezes expõe a estrutura interna. O PgArachne expõe funções deliberadamente.
- Ausência de padrões: O REST não oferece padrão universal para lotes, envelopes de erro ou descoberta automatizada.
2. SSE (Server-Sent Events) vs. WebSockets
Para notificações em tempo real, o PgArachne implementa Server-Sent Events (SSE).
Por que SSE:
- HTTP puro: O SSE é HTTP padrão, funcionando através de proxies e CDNs sem configurações especiais.
- Suporte nativo do navegador: A API
EventSourcegerencia a reconexão automática sem bibliotecas externas. - Corresponde à semântica do NOTIFY: O
NOTIFYdo PostgreSQL é unidirecional, adequando-se perfeitamente ao SSE. - Multiplexação: Sobre HTTP/2, centenas de fluxos SSE compartilham uma única conexão TCP.
- Simplicidade operacional: As conexões SSE aparecem como requisições HTTP normais nos logs.
Por que não WebSockets:
- Bidirecionalidade desnecessária: O cliente nunca precisa enviar dados pelo canal de notificação.
- Problemas de conectividade: Frequentemente bloqueados por firewalls corporativos e alguns balanceadores de carga em nuvem.
- Maior sobrecarga: Handshakes e frames de ping/pong desnecessários para o simples streaming de eventos.
3. Go vs. Alternativas
O PgArachne é escrito em Go para oferecer o melhor equilíbrio entre performance e simplicidade de implantação.
Por que Go:
- Binários estáticos: Um único arquivo executável sem dependências externas.
- Concorrência: As goroutines gerenciam milhares de conexões simultâneas de forma eficiente.
- Biblioteca padrão robusta: HTTP, TLS e JSON integrados, de nível de produção.
- Compilação cruzada: Linux, macOS e Windows (amd64 e arm64) a partir de qualquer máquina.
Por que não Node.js, PHP ou Ruby:
- Runtimes: Requerem a instalação de um ambiente específico em cada máquina de destino.
- Eficiência: Menos eficientes para manter milhares de conexões SSE inativas.
- Consumo de memória: O Go utiliza significativamente menos memória por conexão.
Por que não Rust:
- Velocidade de desenvolvimento: A sua complexidade retarda a iteração para uma ferramenta de I/O onde o Go já é suficiente.
Por que não C/C++:
- Segurança: O gerenciamento manual de memória adiciona riscos sem ganhos relevantes em uma aplicação de gateway.
4. Funções PostgreSQL como Superfície da API
O PgArachne expõe deliberadamente funções do banco de dados em vez de tabelas diretamente.
Por que funções:
- Encapsulamento: A lógica de negócio reside com os dados no banco de dados — um único lugar para auditoria, versionamento e segurança.
- Segurança explícita: Apenas as funções com permissões
EXECUTEpara um papel específico são acessíveis. - Abstração: Validação, campos calculados e operações complexas ficam ocultos para o cliente.
Por que não CRUD em nível de tabela:
- Acoplamento forte: Expor tabelas vincula a API ao esquema interno, dificultando refatorações.
- Fragmentação de regras de negócio: A lógica se divide entre restrições do banco de dados e middleware.
5. Estrutura de URL: /{prefixo}/{banco_de_dados}/{endpoint}
O PgArachne roteia todos os endpoints sob um segmento de prefixo configurável:
/db/{banco_de_dados}/jsonrpc, /db/{banco_de_dados}/sse, /db/{banco_de_dados}/mcp.
O prefixo padrão é db e pode ser alterado via API_PREFIX.
Por que esta estrutura:
- Roteamento por proxy reverso: Uma única instância do PgArachne pode servir múltiplos bancos de dados. Um proxy reverso pode rotear por prefixo ou nome do banco de dados sem inspecionar o corpo da requisição, o que é essencial para balanceamento de carga.
- Escalabilidade horizontal: Com o nome do banco de dados no caminho da URL, é possível executar múltiplas instâncias do PgArachne e direcionar o tráfego por banco de dados usando regras de proxy padrão, sem sessões persistentes.
- Multiplexação de protocolos por banco de dados: Agrupar
/jsonrpc,/ssee/mcpsob o mesmo namespace/{prefixo}/{banco_de_dados}/permite aplicar autenticação, rate limiting e controle de acesso por banco de dados no nível do proxy. - Prefixo configurável: Deployments que já usam
/api/podem configurarAPI_PREFIX=api. - Observabilidade: Sistemas de log e métricas podem agrupar o tráfego por nome de banco de dados diretamente da URL sem analisar corpos JSON.
Por que não uma estrutura plana como /api/{banco_de_dados}:
- Ambiguidade de protocolo: Um único endpoint plano não consegue distinguir o tráfego JSON-RPC, SSE e MCP no nível de roteamento.
- Mais difícil de estender: Adicionar novos protocolos exigiria de qualquer forma novas rotas; portanto, o namespace estruturado prepara o design para o futuro.
6. MCP como Camada de Tradução, não como Protocolo de Banco de Dados
O PgArachne implementa o Model Context Protocol (MCP)
como uma fina camada de tradução no servidor Go. As funções PostgreSQL nunca têm conhecimento do MCP —
permanecem simples funções jsonb → json.
Por que traduzir MCP no servidor:
- Sem alterações nas funções existentes: Qualquer função já exposta via JSON-RPC fica instantaneamente disponível como tool MCP. Sem alterações SQL.
- MCP é mais do que apenas tools: O protocolo inclui o handshake de inicialização, ping, notificações e extensões futuras — preocupações de nível de protocolo que pertencem ao Go.
- A segurança permanece em um único lugar: Autenticação, troca de papel e validação já estão implementados em Go. O endpoint MCP reutiliza essa lógica sem alterações.
- Múltiplos protocolos, um backend: A mesma função PostgreSQL pode ser chamada via JSON-RPC, MCP ou SSE. O banco de dados é agnóstico ao protocolo.
- SQL mais simples: Processar envelopes MCP (
initialize,tools/list, tratamento de notificações) em PostgreSQL exigiria analisar estruturas JSON complexas em PL/pgSQL, tornando as funções mais difíceis de escrever, testar e manter.
Por que não levar MCP ao banco de dados:
- O handshake MCP não requer o banco de dados:
initializeepingsão mensagens de protocolo puras. Abrir uma conexão para elas desperdiça recursos. - SQL é a ferramenta inadequada para lógica de protocolo: Códigos de erro JSON-RPC, roteamento de notificações e gerenciamento de chaves de idempotência são preocupações de middleware, não de dados.