Αρχιτεκτονικές Αποφάσεις
Αποφάσεις Αρχιτεκτονικού Σχεδιασμού
Αυτή η σελίδα εξηγεί τη λογική πίσω από τις βασικές αρχιτεκτονικές και τεχνολογικές επιλογές στο PgArachne. Αυτές οι αποφάσεις ελήφθησαν με προτεραιότητα στην απόδοση, την ασφάλεια και την παραγωγικότητα των developers, εξασφαλίζοντας παράλληλα ότι το σύστημα παραμένει πλήρως συμβατό με σύγχρονους AI και LLM agents.
1. Γιατί PostgreSQL
Το PgArachne είναι σκόπιμα χτισμένο αποκλειστικά πάνω στην PostgreSQL και δεν επιχειρεί να είναι ανεξάρτητο από τη βάση δεδομένων. Το μεγαλύτερο μέρος όσων προσφέρει το gateway δεν είναι custom λογική σε Go, αλλά άμεση αξιοποίηση των δυνατοτήτων της PostgreSQL.
Γιατί PostgreSQL:
- Ενσωματωμένο μοντέλο δικαιωμάτων: Οι ρόλοι, τα
GRANT/REVOKEκαι τα δικαιώματαEXECUTEσε functions αποτελούν μέρος της βάσης δεδομένων. Έτσι το PgArachne δεν χρειάζεται δικό του επίπεδο εξουσιοδότησης — απλώς αλλάζει ρόλο μεSET LOCAL ROLEκαι τα υπόλοιπα τα αναλαμβάνει η PostgreSQL. Οι κανόνες πρόσβασης ισχύουν ίδιοι για το API, τοpsqlκαι κάθε άλλο εργαλείο. - Row-Level Security: Οι πολιτικές σε επίπεδο γραμμής αξιολογούνται με βάση τον ρόλο εκ μέρους του οποίου εκτελείται η κλήση. Η απομόνωση δεδομένων μεταξύ χρηστών ή tenants βρίσκεται στη βάση δεδομένων, όχι στον κώδικα του gateway.
- Native JSON (
jsonb): Το συμβόλαιοfunction(jsonb) → jsonταιριάζει ακριβώς με το σώμα του JSON-RPC request και response. Δεν χρειάζεται αντιστοίχιση παραμέτρων σε τύπους ούτε δημιουργία envelopes — το JSON διέρχεται από το gateway αναλλοίωτο. - Συναλλακτικές εγγυήσεις: Κάθε κλήση εκτελείται σε μία συναλλαγή με πλήρεις εγγυήσεις ACID. Μια function μπορεί να γράψει ατομικά σε πολλούς πίνακες και, σε περίπτωση σφάλματος, όλα αναιρούνται, χωρίς το gateway να χρειάζεται να γνωρίζει οτιδήποτε σχετικό.
- Ισχυρό προγραμματιστικό μοντέλο: Η PL/pgSQL, οι SQL functions και άλλες διαδικαστικές γλώσσες επιτρέπουν τη διατύπωση της business logic εκεί όπου βρίσκονται τα δεδομένα, χωρίς μεταφορά ενδιάμεσων αποτελεσμάτων μέσω δικτύου.
LISTEN/NOTIFY: Οι ειδοποιήσεις πραγματικού χρόνου (απόφαση 5) βασίζονται απευθείας στον μηχανισμό της PostgreSQL. Δεν απαιτείται εξωτερικός message broker.- Introspection του system catalog: Η λίστα των καλέσιμων μεθόδων, οι περιγραφές τους και τα schemas των παραμέτρων διαβάζονται από το
pg_procκαι τα σχόλια των functions. Από μία και μόνη πηγή αλήθειας προκύπτουν τοcapabilities, το MCPtools/listκαι η προδιαγραφή OpenAPI, και δεν μπορούν να αποκλίνουν από την πραγματικότητα. - Οικοσύστημα επεκτάσεων: Οι PostGIS, pgvector, TimescaleDB,
pg_trgmκαι άλλες επεκτάσεις είναι άμεσα διαθέσιμες ως κανονικές SQL functions, άρα και ως μέθοδοι API και εργαλεία για AI agents — χωρίς ούτε μία γραμμή κώδικα Go. - Ανοιχτότητα και ωριμότητα: Ελεύθερη άδεια χωρίς vendor lock-in, δεκαετίες δοκιμασμένης λειτουργίας, ενεργή ανάπτυξη και διαθεσιμότητα σε όλους τους μεγάλους παρόχους cloud, αλλά και ως αυτόνομα διαχειριζόμενο instance.
Γιατί όχι άλλη βάση δεδομένων ή επίπεδο ανεξάρτητο από τη βάση δεδομένων:
- Ελάχιστος κοινός παρονομαστής: Η υποστήριξη πολλών βάσεων δεδομένων θα σήμαινε παραίτηση ακριβώς από τις δυνατότητες πάνω στις οποίες στηρίζεται το PgArachne — το μοντέλο ασφάλειας με ρόλους, το
jsonb, τοLISTEN/NOTIFYκαι το introspection των functions. Θα έμενε ένα λεπτό και λιγότερο ασφαλές επίπεδο πάνω από γενική SQL. - MySQL/MariaDB: Δεν διαθέτουν native Row-Level Security ούτε ισοδύναμο του
LISTEN/NOTIFY, και η υποστήριξή τους για JSON και διαδικαστική λογική είναι πιο περιορισμένη. - SQL Server και Oracle: Η ιδιοκτησιακή αδειοδότηση και το λειτουργικό κόστος έρχονται σε αντίθεση με τον στόχο της εύκολης, δωρεάν εγκατάστασης με ένα μόνο binary.
- Βάσεις NoSQL (document, key-value, column-family): Η κλιμάκωση και η ευελιξία του schema, για τις οποίες επιλέγεται συνήθως η NoSQL, δεν αποτελούν το βασικό όφελος για το PgArachne. Την αναγκαία αποθήκευση εγγράφων καλύπτει το
jsonb, με indexing και querying, δίπλα στα σχεσιακά δεδομένα και στην ίδια συναλλαγή. Αντίθετα λείπουν όσα στηρίζουν το PgArachne: server-side functions που καλούνται με λεπτομερές δικαίωμαEXECUTEγια συγκεκριμένο ρόλο, Row-Level Security, introspection των functions από τον κατάλογο και ενιαία γλώσσα ερωτημάτων. Χωρίς αυτά το gateway θα έπρεπε να χειρίζεται μόνο του την εξουσιοδότηση, την επικύρωση και την περιγραφή του API — και θα έπαυε να είναι λεπτό επίπεδο. - SQLite: Είναι embedded βάση δεδομένων χωρίς μοντέλο ρόλων και δικαιωμάτων σε επίπεδο server, πάνω στο οποίο είναι χτισμένη ολόκληρη η ασφάλεια του PgArachne.
Το αποτέλεσμα είναι ότι το PgArachne παραμένει μικρό: αναθέτει στην PostgreSQL την ασφάλεια, τις συναλλαγές, τις ειδοποιήσεις και την ανακάλυψη του API, και το ίδιο φροντίζει μόνο για τη μετάφραση πρωτοκόλλων και την authentication.
2. PostgreSQL Functions ως Επιφάνεια API
Το PgArachne εκθέτει σκόπιμα functions της βάσης δεδομένων αντί για raw πίνακες.
Γιατί functions:
- Encapsulation: Η business logic βρίσκεται συνεντοπισμένη με τα δεδομένα στη βάση δεδομένων — ένα μόνο σημείο για audit, versioning και ασφάλεια.
- Ρητή Ασφάλεια: Μόνο functions στις οποίες έχουν ρητά χορηγηθεί δικαιώματα
EXECUTEσε συγκεκριμένο ρόλο είναι προσβάσιμες μέσω του API. - Abstraction: Η επικύρωση εισόδου, τα υπολογιζόμενα πεδία και οι σύνθετες λειτουργίες πολλών πινάκων είναι κρυμμένες από τον client, παρέχοντας ένα καθαρό interface.
Γιατί όχι CRUD σε επίπεδο πίνακα:
- Στενή Σύζευξη: Η απευθείας έκθεση πινάκων συνδέει στενά το API σας με το εσωτερικό σχήμα της βάσης δεδομένων, καθιστώντας δύσκολο το refactoring της βάσης χωρίς να «σπάσουν» οι clients.
- Κατακερματισμός Επιχειρησιακών Κανόνων: Η business logic καταλήγει μοιρασμένη ανάμεσα σε constraints της βάσης δεδομένων και σε όποιο middleware χρησιμοποιείται για το φιλτράρισμα HTTP requests.
3. Go έναντι Εναλλακτικών
Το PgArachne είναι γραμμένο σε Go για να προσφέρει την καλύτερη ισορροπία μεταξύ απόδοσης και απλότητας deployment.
Γιατί Go:
- Static Binaries: Μεταγλωττίζεται σε ένα μόνο binary χωρίς εξωτερικές εξαρτήσεις. Το deployment είναι τόσο απλό όσο η αντιγραφή του αρχείου στον server.
- Concurrency: Οι goroutines της Go κάνουν τη διαχείριση χιλιάδων ταυτόχρονων συνδέσεων SSE και βάσης δεδομένων ελαφριά και απλή.
- Ισχυρή Standard Library: Οι ενσωματωμένες βιβλιοθήκες για HTTP, TLS και JSON είναι production-grade και δεν απαιτούν «node_modules» ή εξωτερικά runtimes.
- Cross-Compilation: Στοχεύει εύκολα Linux, macOS και Windows (amd64 και arm64) από οποιοδήποτε μηχάνημα ανάπτυξης.
Γιατί όχι Node.js, PHP, ή Ruby:
- Runtimes: Απαιτούν την εγκατάσταση ενός συγκεκριμένου runtime environment σε κάθε μηχάνημα-στόχο.
- Αποδοτικότητα: Ο single-threaded loop της Node ή το μοντέλο process-per-request της PHP είναι λιγότερο αποδοτικά για τη διατήρηση χιλιάδων ανενεργών συνδέσεων SSE.
- Αποτύπωμα Μνήμης: Η Go χρησιμοποιεί σημαντικά λιγότερη μνήμη ανά σύνδεση από τις scripted γλώσσες.
Γιατί όχι Rust:
- Ταχύτητα Ανάπτυξης: Ενώ η Rust προσφέρει εξαιρετική απόδοση, η πολυπλοκότητά της (borrow checker) επιβραδύνει την επαναληπτική ανάπτυξη για ένα εργαλείο περιορισμένο από I/O, όπου η απόδοση της Go είναι ήδη υπεραρκετή.
Γιατί όχι C/C++:
- Ασφάλεια: Η χειροκίνητη διαχείριση μνήμης προσθέτει σημαντικούς κινδύνους ασφαλείας (buffer overflows) χωρίς ουσιαστικό κέρδος απόδοσης σε μια εφαρμογή gateway.
4. JSON-RPC 2.0 έναντι REST
Το PgArachne χρησιμοποιεί το JSON-RPC 2.0 ως το κύριο πρωτόκολλο επικοινωνίας του, αντί για το παραδοσιακό REST.
Γιατί JSON-RPC 2.0:
- Ένα Μόνο Endpoint: Όλη η επικοινωνία γίνεται μέσω
POST /{prefix}/{database}/jsonrpc. Δεν υπάρχει ανάγκη σχεδιασμού πολύπλοκων δομών URL ή συζήτησης σχετικά με τη σημασιολογία των HTTP verbs. - Αυτοτελείς Κλήσεις: Κάθε request είναι ένα πλήρες JSON object (method + params + id). Αυτή η μορφή παράγεται και αναλύεται εύκολα από LLMs και AI agents με υψηλή αξιοπιστία.
- Τυποποιημένη Διαχείριση Σφαλμάτων: Οι κωδικοί και τα μηνύματα σφάλματος αποτελούν μέρος της προδιαγραφής, εξαλείφοντας την ανάγκη να «εφευρίσκετε» συμβάσεις HTTP status codes για επιχειρησιακά σφάλματα.
- Batching: Το πρωτόκολλο υποστηρίζει εγγενώς batch requests, επιτρέποντας πολλαπλές λειτουργίες (π.χ. αρκετές κλήσεις functions) σε ένα μόνο HTTP round-trip χωρίς επιπλέον δουλειά.
- Discovery: Το capabilities endpoint παρέχει μια πλήρη περιγραφή του API σε μορφή που οι AI agents μπορούν να καταναλώσουν για να κατανοήσουν τα διαθέσιμα εργαλεία χωρίς hallucinations.
Γιατί όχι REST:
- Πολυπλοκότητα για AI: Η σημασιολογία του REST (GET/POST/PATCH/DELETE + URL params + body) είναι διασκορπισμένη σε πολλά σημεία, καθιστώντας δυσκολότερη τη σύνθεση κλήσεων με αξιοπιστία από τους AI agents.
- Διαρροή Σχήματος: Το CRUD-over-tables (όπως το PostgREST) συχνά αφήνει να διαρρεύσει η εσωτερική δομή της βάσης δεδομένων απευθείας στο API. Το PgArachne σκόπιμα εκθέτει functions, κρατώντας τη business logic ενσωματωμένη στην SQL.
- Έλλειψη Προτύπων: Το REST δεν προσφέρει καθολικό πρότυπο για batch operations, cross-platform error envelopes, ή αυτοματοποιημένη ανακάλυψη API.
5. SSE (Server-Sent Events) έναντι WebSockets
Για ειδοποιήσεις πραγματικού χρόνου, το PgArachne υλοποιεί Server-Sent Events (SSE).
Γιατί SSE:
- Απλό HTTP: Το SSE είναι τυπικό HTTP. Λειτουργεί μέσα από proxies, load balancers και CDNs χωρίς ειδική διαμόρφωση «protocol upgrade».
- Native Υποστήριξη Browser: Το API
EventSourceείναι ενσωματωμένο σε όλους τους σύγχρονους browsers και διαχειρίζεται αυτόματη επανασύνδεση χωρίς καμία client library. - Ταιριάζει με τη Σημασιολογία του NOTIFY: Το
NOTIFYτης PostgreSQL είναι μονόδρομο (από τον server στον client), το οποίο ταιριάζει απόλυτα με το SSE. - Multiplexing: Πάνω από HTTP/2, εκατοντάδες SSE streams μπορούν να μοιραστούν μία μόνο σύνδεση TCP, κάνοντάς το εξαιρετικά αποδοτικό.
- Λειτουργική Απλότητα: Οι συνδέσεις SSE εμφανίζονται ως κανονικά HTTP requests σε logs και εργαλεία monitoring, καθιστώντας ευκολότερο το debugging και το rate-limiting.
Γιατί όχι WebSockets:
- Άσκοπη Διπλή Κατεύθυνση: Καθώς ο client δεν έχει ποτέ ανάγκη να στείλει δεδομένα πίσω μέσω του καναλιού ειδοποιήσεων, η πολυπλοκότητα των WebSockets δεν προσφέρει κανένα όφελος.
- Προβλήματα Συνδεσιμότητας: Τα WebSockets συχνά μπλοκάρονται ή κλείνονται πρόωρα από εταιρικά firewalls και ορισμένα cloud load balancers.
- Υψηλότερο Overhead: Προσθέτει πολυπλοκότητα πρωτοκόλλου (handshakes, ping/pong frames) που δεν απαιτείται για απλό event streaming.
6. Δομή URL: /{prefix}/{database}/{endpoint}
Το PgArachne δρομολογεί όλα τα endpoints κάτω από ένα ρυθμιζόμενο τμήμα προθέματος:
/db/{database}/jsonrpc, /db/{database}/file, /db/{database}/sse, /db/{database}/mcp.
Το πρόθεμα έχει προεπιλογή db και μπορεί να αλλάξει μέσω API_PREFIX.
Γιατί αυτή η δομή:
- Δρομολόγηση μέσω reverse proxy: Ένα μόνο instance PgArachne μπορεί να εξυπηρετήσει πολλαπλές βάσεις δεδομένων. Ένας reverse proxy (Nginx, Caddy, Traefik) μπορεί να δρομολογήσει βάσει προθέματος ή ονόματος βάσης δεδομένων χωρίς να επιθεωρεί το σώμα του request, κάτι κρίσιμο για load balancing και κανόνες δρομολόγησης βάσει path.
- Οριζόντια Κλιμακωσιμότητα: Με το όνομα της βάσης δεδομένων στο URL path, μπορείτε να τρέξετε πολλαπλά instances PgArachne και να δρομολογήσετε traffic σε συγκεκριμένα instances ανά βάση δεδομένων χρησιμοποιώντας τυπικούς κανόνες proxy — χωρίς sticky sessions ή επιθεώρηση body.
- Protocol multiplexing ανά βάση δεδομένων: Η ομαδοποίηση των
/jsonrpc,/file,/sseκαι/mcpκάτω από τον ίδιο namespace/{prefix}/{database}/κάνει φυσιολογική την εφαρμογή authentication, rate limiting και access control ανά βάση δεδομένων στο επίπεδο του proxy. - Ρυθμιζόμενο πρόθεμα: Deployments που χρησιμοποιούν ήδη το
/api/ως πρόθεμα στην υποδομή τους μπορούν να ορίσουνAPI_PREFIX=apiγια να ταιριάξουν με τις συμβάσεις τους. - Παρατηρησιμότητα: Τα εργαλεία συγκέντρωσης logs και συστήματα μετρικών μπορούν να ομαδοποιήσουν και να φιλτράρουν το traffic βάσει ονόματος βάσης δεδομένων απευθείας από το URL, χωρίς να αναλύουν JSON bodies.
Γιατί όχι μια πλατή δομή όπως /api/{database}:
- Ασάφεια Πρωτοκόλλου: Ένα μόνο πλατό endpoint δεν μπορεί να διακρίνει μεταξύ traffic JSON-RPC, SSE και MCP στο επίπεδο δρομολόγησης — αυτή η απόφαση καταλήγει στη λογική της εφαρμογής ή στην επιθεώρηση headers.
- Δυσκολότερη Επέκταση: Η προσθήκη νέων πρωτοκόλλων (π.χ. GraphQL, gRPC-gateway) απαιτεί ούτως ή άλλως την εισαγωγή νέων top-level paths, οπότε ο δομημένος namespace καθιστά τον σχεδιασμό ανθεκτικό στο μέλλον.
7. MCP ως Translation Layer, Όχι ως Πρωτόκολλο Βάσης Δεδομένων
Το PgArachne υλοποιεί το Model Context Protocol (MCP)
ως ένα λεπτό translation layer στον Go server. Οι PostgreSQL functions δεν έχουν ποτέ επίγνωση του MCP — παραμένουν
απλές functions jsonb → json.
Γιατί το MCP μεταφράζεται στον server:
- Μηδενικές αλλαγές σε υπάρχουσες functions: Κάθε function που είναι ήδη εκτεθειμένη μέσω JSON-RPC γίνεται άμεσα διαθέσιμη ως εργαλείο MCP. Καμία αλλαγή SQL, καμία επανεγκατάσταση αντικειμένων της βάσης δεδομένων.
- Το MCP είναι πολύ περισσότερο από εργαλεία: Το πρωτόκολλο περιλαμβάνει μια μέθοδο ανακάλυψης (
server/discover), μεταδεδομένα έκδοσης πρωτοκόλλου και δυνατοτήτων σε κάθε αίτημα, notifications, και επεκτάσεις (resources, prompts) πέρα από απλές κλήσεις εργαλείων. Αυτά είναι ζητήματα επιπέδου πρωτοκόλλου που ανήκουν στη Go, όχι σε SQL functions. - Η ασφάλεια παραμένει σε ένα σημείο: Η authentication, η αλλαγή ρόλων και η επικύρωση εισόδου είναι ήδη υλοποιημένες στη Go. Το endpoint MCP επαναχρησιμοποιεί αυτή τη λογική αμετάβλητη.
- Πολλαπλά πρωτόκολλα, ένα backend: Η ίδια PostgreSQL function μπορεί να κληθεί μέσω JSON-RPC (από κανονικό client), MCP (από Claude Desktop ή Cursor), ή SSE (για event subscriptions). Η βάση δεδομένων είναι protocol-agnostic.
- Απλούστερη SQL: Η επεξεργασία envelopes MCP (
server/discover,tools/list, χειρισμός notifications) εντός PostgreSQL functions θα απαιτούσε ανάλυση σύνθετων δομών JSON σε PL/pgSQL, καθιστώντας τις functions δυσκολότερες στη γραφή, τη δοκιμή και τη συντήρηση.
Γιατί όχι να μεταφερθεί το MCP στη βάση δεδομένων:
- Η επικύρωση transport του MCP δεν χρειάζεται βάση δεδομένων: Το
server/discoverκαι οι έλεγχοι έκδοσης πρωτοκόλλου/headers σε κάθε αίτημα είναι καθαρά μηνύματα πρωτοκόλλου. Το άνοιγμα σύνδεσης βάσης δεδομένων για αυτά σπαταλά πόρους και προσθέτει latency. - Η SQL είναι λάθος εργαλείο για λογική πρωτοκόλλου: Οι κωδικοί σφάλματος JSON-RPC 2.0, η δρομολόγηση notifications και η διαχείριση idempotency keys είναι ζητήματα middleware, όχι ζητήματα δεδομένων.
8. Εξαγωγή OpenAPI: Εικονικά Paths, Όχι Νέο Πρωτόκολλο
Το GET /{prefix}/{database}/openapi.json (ή .yaml) παράγει ένα έγγραφο OpenAPI 3.1
που περιγράφει κάθε μέθοδο που μπορεί να εκτελέσει ο αυθεντικοποιημένος client. Η πραγματική κλήση εξακολουθεί
να γίνεται αποκλειστικά μέσω του ενιαίου endpoint POST /{prefix}/{database}/jsonrpc — η προδιαγραφή
απλώς παραθέτει επιπλέον κάθε μέθοδο κάτω από το δικό της path, όπως το
/{prefix}/{database}/rpc/api.hello_world, αλλά αυτό το path υπάρχει μόνο ως τεκμηρίωση και δεν
αποτελεί πραγματικό HTTP route που μπορείτε να καλέσετε απευθείας.
Γιατί εικονικά paths ανά μέθοδο:
- Συμβατότητα με tooling: Τα Swagger UI, Postman, Insomnia, και οι περισσότεροι OpenAPI code generators αναμένουν μία operation ανά καταχώριση
paths. Ένα μόνο JSON-RPC path δεν μπορεί διαφορετικά να αναπαραστήσει N διαφορετικές υπογραφές μεθόδων με τρόπο που να κατανοούν αυτά τα εργαλεία. - Καμία αλλαγή backend δεν απαιτείται: Η προσθήκη πραγματικού route ανά μέθοδο θα σήμαινε έναν δεύτερο τρόπο κλήσης κάθε function, με τη δική του επιφάνεια auth, error-handling και versioning που θα έπρεπε να συντηρείται σε πλήρη συγχρονισμό με το JSON-RPC. Η παραγωγή των paths αποκλειστικά από την
capabilities()διατηρεί την επιφάνεια του πρωτοκόλλου ακριβώς όπως περιγράφεται στην απόφαση 4, ικανοποιώντας παράλληλα τα εργαλεία που χρειάζονται schemas ανά operation. - Αυτοτεκμηριούμενο: Η περιγραφή κάθε εικονικής operation αναφέρει ρητά το ακριβές JSON-RPC request (method + params) που απαιτείται για την πραγματική κλήση της, οπότε τίποτα δεν χάνεται από το ότι δεν υπάρχει πραγματικό route.
Γιατί η προδιαγραφή είναι αυθεντικοποιημένη και φιλτραρισμένη βάσει ρόλου:
- Συνέπεια με το υπόλοιπο API: Κάθε άλλο endpoint (JSON-RPC, MCP
tools/list) αποκαλύπτει πάντα μόνο τις μεθόδους που επιτρέπεται να εκτελέσει ο ρόλος του client. Ένα μη αυθεντικοποιημένο, μη φιλτραρισμένο έγγραφο OpenAPI θα διέρρεε την ύπαρξη και τη μορφή των παραμέτρων functions που ένας συγκεκριμένος client δεν μπορεί στην πραγματικότητα να καλέσει. - Ίδιος μηχανισμός παντού: Ο handler της Go αυθεντικοποιεί με την ίδια λογική Basic/JWT/API-token όπως το
/jsonrpc, και στη συνέχεια εκτελείSET LOCAL ROLEπριν την παραγωγή της προδιαγραφής — ηpgarachne.generate_openapi_spec()είναιSECURITY INVOKERειδικά ώστε η εσωτερική κλήση της στηνcapabilities()να «βλέπει» αυτόν τον ρόλο, με τον ίδιο τρόπο που λειτουργεί ήδη το MCPtools/list.