4.1 KiB
4.1 KiB
ADR-004: SurrealDB como Database Único
Status: Accepted | Implemented Date: 2024-11-01 Deciders: Backend Architecture Team Technical Story: Selecting unified multi-model database for relational, graph, and document workloads
Decision
Usar SurrealDB 2.3 como base de datos única (no PostgreSQL + Neo4j, no MongoDB puro).
Rationale
- Multi-Model en una sola DB: Relational (SQL), graph (queries), document (JSON) sin múltiples conexiones
- Multi-Tenancy Nativa: SurrealDB scopes permiten aislamiento a nivel de database sin lógica en aplicación
- WebSocket Connection: Soporte nativo de conexiones bidireccionales (vs REST)
- SurrealQL: Sintaxis SQL-like + graph traversal en una sola query language
- VAPORA Requirements: Almacena projects (relational), agent relationships (graph), execution history (document)
Alternatives Considered
❌ PostgreSQL + Neo4j (Two Database Approach)
- Pros: Maduro, comunidad grande, especializados
- Cons: Sincronización entre dos DBs, dos conexiones, transacciones distribuidas complejas
❌ MongoDB Puro (Document Only)
- Pros: Flexible, escalable
- Cons: Sin soporte graph nativo, requiere aplicación para traversal, sin SQL
✅ SurrealDB (CHOSEN)
- Unifica relational + graph + document
- Multi-tenancy built-in
- WebSocket para real-time
Trade-offs
Pros:
- ✅ Una sola DB para todos los modelos de datos
- ✅ Scopes para isolamiento de tenants (no en aplicación)
- ✅ Transactions ACID
- ✅ SurrealQL es SQL + graph en una query
- ✅ WebSocket bidireccional
Cons:
- ⚠️ Ecosistema más pequeño que PostgreSQL
- ⚠️ Drivers/herramientas menos maduras
- ⚠️ Soporte de clusters más limitado (vs Postgres)
Implementation
Database Connection:
// crates/vapora-backend/src/main.rs:48-59
let db = surrealdb::Surreal::new::<surrealdb::engine::remote::ws::Ws>(
&config.database.url
).await?;
db.signin(surrealdb::opt::auth::Root {
username: "root",
password: "root",
}).await?;
db.use_ns("vapora").use_db("main").await?;
Scope-Based Multi-Tenancy:
// All queries use scope for tenant isolation
db.query("SELECT * FROM projects WHERE tenant_id = $tenant_id")
.bind(("tenant_id", tenant_id))
.await?
Key Files:
/crates/vapora-backend/src/main.rs:45-59(connection setup)/crates/vapora-backend/src/services/(query implementations)/crates/vapora-shared/src/models.rs(Project, Task, Agent models with tenant_id)
Verification
# Connect to SurrealDB
surreal sql --conn ws://localhost:8000 --user root --pass root
# Verify namespace and database exist
USE ns vapora db main;
INFO FOR DATABASE;
# Test multi-tenant query
SELECT * FROM projects WHERE tenant_id = 'workspace:123';
# Test graph traversal
SELECT
*,
->assigned_to->agents AS assigned_agents
FROM tasks
WHERE project_id = 'project:123';
# Run backend tests with SurrealDB
cargo test -p vapora-backend -- --nocapture
Expected Output:
- SurrealDB connects via WebSocket
- Projects table exists and is queryable
- Graph relationships (->assigned_to) resolve
- Multi-tenant queries filter correctly
- 79+ backend tests pass
Consequences
Data Model Changes
- All tables must include
tenant_idfield for scoping - Relations use SurrealDB's
->edge syntax for graph queries - No foreign key constraints (SurrealDB uses references instead)
Query Patterns
- Services layer queries must include tenant_id filter (defense-in-depth)
- SurrealQL instead of raw SQL learning curve for team
- Graph traversal enables efficient knowledge graph queries
Scaling Considerations
- Horizontal scaling requires clustering (vs Postgres replication)
- Backup/recovery different from traditional databases (see ADR-020)
References
- SurrealDB Documentation
/crates/vapora-backend/src/services/(query patterns)/crates/vapora-shared/src/models.rs(model definitions with tenant_id)- ADR-025 (Multi-Tenancy with Scopes)
Related ADRs: ADR-001 (Workspace), ADR-025 (Multi-Tenancy)