6 min de leitura

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 uma 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 EventSource gerencia a reconexão automática sem bibliotecas externas.
  • Corresponde à Semântica do NOTIFY: NOTIFY do PostgreSQL é unidirecional, ajustando-se perfeitamente ao SSE.
  • Multiplexação: Sobre o 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 leve.
  • 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: Sua complexidade retarda a iteração para uma ferramenta 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 puras.

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 EXECUTE para 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 reverse proxy: Uma única instância PgArachne pode servir múltiplos bancos de dados. Um reverse proxy pode rotear por prefixo ou nome do banco de dados sem inspecionar o corpo da requisição, essencial para balanceamento de carga.
  • Escalabilidade horizontal: Com o nome do banco de dados no caminho URL, é possível executar múltiplas instâncias 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, /sse e /mcp sob o mesmo namespace 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 configurar API_PREFIX=api. Clientes legados são suportados via 307 Temporary Redirect.
  • 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 sabem 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 está 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 em PostgreSQL exigiria analisar estruturas JSON complexas em PL/pgSQL, tornando as funções mais difíceis de escrever e manter.

Por que não levar MCP ao banco de dados:

  • O handshake MCP não precisa do banco de dados: initialize e ping são mensagens de protocolo puras. Abrir uma conexão para elas desperdiça recursos.
  • SQL é a ferramenta errada 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.