Decisioni di Progettazione Architetturale
Questa pagina spiega il ragionamento alla base delle principali scelte tecnologiche e architetturali in PgArachne. Queste decisioni sono state prese per dare priorità a prestazioni, sicurezza e produttività degli sviluppatori, garantendo al contempo un’elevata compatibilità con i moderni agenti AI e modelli LLM.
1. JSON-RPC 2.0 vs. REST
PgArachne utilizza JSON-RPC 2.0 come protocollo di comunicazione primario al posto del tradizionale REST.
Perché JSON-RPC 2.0:
- Endpoint Singolo: Tutte le comunicazioni avvengono tramite
POST /{prefisso}/{database}/jsonrpc. Non è necessario progettare strutture URL complesse. - Chiamate Autocontenute: Ogni richiesta è un oggetto JSON completo (metodo + parametri + id), facile da generare e analizzare per gli LLM.
- Gestione degli Errori Standardizzata: I codici e i messaggi di errore fanno parte della specifica.
- Batching: Il protocollo supporta nativamente le richieste batch in un unico round-trip HTTP.
- Discovery: L’endpoint delle funzionalità fornisce una descrizione completa dell’API per gli agenti AI senza allucinazioni.
Perché non REST:
- Complessità per l’AI: La semantica REST è distribuita in più punti, rendendo difficile la costruzione di chiamate affidabili per gli agenti AI.
- Fuga dello Schema: Il CRUD sulle tabelle espone spesso la struttura interna. PgArachne espone deliberatamente funzioni.
- Mancanza di Standard: REST non offre uno standard universale per batch, wrapper di errore o scoperta automatizzata.
2. SSE (Server-Sent Events) vs. WebSockets
Per le notifiche in tempo reale, PgArachne implementa Server-Sent Events (SSE).
Perché SSE:
- HTTP Puro: SSE è HTTP standard, funziona attraverso proxy e CDN senza configurazioni speciali.
- Supporto Nativo del Browser: L’API
EventSourcegestisce la riconnessione automatica senza librerie aggiuntive. - Corrisponde alla Semantica NOTIFY:
NOTIFYdi PostgreSQL è unidirezionale, il che si adatta perfettamente a SSE. - Multiplexing: Su HTTP/2, centinaia di stream SSE condividono una singola connessione TCP.
- Semplicità Operativa: Le connessioni SSE appaiono come normali richieste HTTP nei log.
Perché non WebSockets:
- Bidirezionalità non Necessaria: Il client non invia mai dati tramite il canale di notifica.
- Problemi di Connettività: Spesso bloccati da firewall aziendali e alcuni bilanciatori di carico cloud.
- Maggiore Overhead: Handshake e frame ping/pong inutili per il semplice streaming di eventi.
3. Go vs. Alternative
PgArachne è scritto in Go per offrire il miglior equilibrio tra prestazioni e semplicità di distribuzione.
Perché Go:
- Binari Statici: Un unico file eseguibile senza dipendenze esterne.
- Concorrenza: Le goroutine gestiscono migliaia di connessioni simultanee in modo leggero.
- Libreria Standard Robusta: HTTP, TLS e JSON integrati, di livello produttivo.
- Cross-compilazione: Linux, macOS e Windows (amd64 e arm64) da qualsiasi macchina.
Perché non Node.js, PHP o Ruby:
- Runtime: Richiedono l’installazione di un ambiente specifico su ogni macchina di destinazione.
- Efficienza: Meno efficienti per migliaia di connessioni SSE inattive.
- Impronta di Memoria: Go utilizza molta meno memoria per connessione.
Perché non Rust:
- Velocità di Sviluppo: La sua complessità rallenta l’iterazione per uno strumento I/O dove Go è già sufficiente.
Perché non C/C++:
- Sicurezza: La gestione manuale della memoria aggiunge rischi senza guadagni rilevanti in un’applicazione gateway.
4. Funzioni PostgreSQL come Superficie API
PgArachne espone deliberatamente funzioni del database piuttosto che tabelle grezze.
Perché le funzioni:
- Incapsulamento: La logica di business reside con i dati nel database — un unico punto per audit, versionamento e sicurezza.
- Sicurezza Esplicita: Solo le funzioni con permessi
EXECUTEper un ruolo specifico sono accessibili. - Astrazione: Validazione, campi calcolati e operazioni complesse sono nascosti al client.
Perché no CRUD a livello di tabella:
- Accoppiamento Stretto: Esporre le tabelle lega l’API allo schema interno, rendendo difficile il refactoring.
- Frammentazione delle Regole di Business: La logica si divide tra vincoli del database e middleware.
5. Struttura URL: /{prefisso}/{database}/{endpoint}
PgArachne instrada tutti gli endpoint sotto un segmento di prefisso configurabile:
/db/{database}/jsonrpc, /db/{database}/sse, /db/{database}/mcp.
Il prefisso predefinito è db e può essere modificato tramite API_PREFIX.
Perché questa struttura:
- Routing tramite reverse proxy: Un’unica istanza PgArachne può servire più database. Un reverse proxy può instradare per prefisso o nome database senza ispezionare il corpo della richiesta, essenziale per il bilanciamento del carico.
- Scalabilità orizzontale: Con il nome del database nel percorso URL, è possibile eseguire più istanze PgArachne e instradare il traffico a istanze specifiche tramite regole di proxy standard, senza sessioni persistenti.
- Multiplexing di protocolli per database: Raggruppare
/jsonrpc,/ssee/mcpsotto lo stesso namespace consente di applicare autenticazione, rate limiting e controllo accessi per database a livello di proxy. - Prefisso configurabile: I deployment che usano già
/api/possono impostareAPI_PREFIX=api. I client legacy sono supportati tramite307 Temporary Redirect. - Osservabilità: I sistemi di log e metriche possono raggruppare il traffico per nome database direttamente dall’URL senza analizzare i corpi JSON.
Perché non una struttura piatta come /api/{database}:
- Ambiguità di protocollo: Un unico endpoint piatto non può distinguere il traffico JSON-RPC, SSE e MCP a livello di routing.
- Più difficile da estendere: L’aggiunta di nuovi protocolli richiederebbe comunque nuovi percorsi, quindi il namespace strutturato prepara il design per il futuro.
6. MCP come Strato di Traduzione, non come Protocollo di Database
PgArachne implementa il Model Context Protocol (MCP)
come un sottile strato di traduzione nel server Go. Le funzioni PostgreSQL non sanno mai di MCP —
rimangono semplici funzioni jsonb → json.
Perché tradurre MCP sul server:
- Nessuna modifica alle funzioni esistenti: Qualsiasi funzione già esposta via JSON-RPC è immediatamente disponibile come tool MCP. Nessuna modifica SQL.
- MCP è più che soli tool: Il protocollo include l’handshake di inizializzazione, ping, notifiche ed estensioni future — preoccupazioni di livello protocollo che appartengono a Go.
- La sicurezza rimane in un unico posto: Autenticazione, cambio di ruolo e validazione sono già implementati in Go. L’endpoint MCP riutilizza questa logica invariata.
- Più protocolli, un backend: La stessa funzione PostgreSQL può essere chiamata via JSON-RPC, MCP o SSE. Il database è agnostico al protocollo.
- SQL più semplice: Elaborare gli envelope MCP in PostgreSQL richiederebbe il parsing di strutture JSON complesse in PL/pgSQL, rendendo le funzioni più difficili da scrivere e mantenere.
Perché non portare MCP nel database:
- L’handshake MCP non necessita del database:
initializeepingsono messaggi di protocollo puri. Aprire una connessione per essi spreca risorse. - SQL è lo strumento sbagliato per la logica di protocollo: Codici di errore JSON-RPC, routing delle notifiche e gestione delle chiavi di idempotenza sono preoccupazioni middleware, non dati.