Status: Accepted
Date: 2024-07
Deciders: Core development team
AetherMint serves two distinct data workloads:
- Relational/transactional data: User accounts, enrollments, course structures, credential records — data with strict schemas, relationships, and ACID requirements.
- Document/flexible data: Course content, learning materials, analytics events, user-generated metadata — data with variable schemas, nested structures, and eventual-consistency tolerance.
Using a single database for both workloads would force compromises: either denormalizing relational data into documents (losing integrity guarantees) or forcing flexible content into rigid relational schemas (losing agility).
Additionally, a caching layer is needed for session management, rate limiting, and frequently-accessed data like user profiles and course metadata.
We will use a dual-database strategy with a dedicated caching layer:
| Database | Purpose |
|---|---|
| PostgreSQL (primary) | User accounts, enrollments, credentials, courses — all relational/transactional data requiring ACID compliance |
| MongoDB (document store) | Course content, analytics events, learning materials, user activity logs — flexible-schema data |
| Redis (cache) | Session tokens, rate limiting, frequently-accessed queries, real-time analytics counters |
| Neo4j (graph — backend only) | Learning path recommendations, skill relationship graphs, social connections (available in backend services, not in local docker-compose) |
Specifically:
- PostgreSQL accessed via the
pgdriver for direct SQL queries - MongoDB accessed via
mongooseODM for schema validation on flexible documents - Redis via
iorediswith connection pooling - Neo4j via
neo4j-driverfor graph queries
- Pros: Single database to operate, simpler infrastructure, ACID for all data
- Cons: JSONB columns for flexible content add query complexity, poor performance for analytics events at scale, no native graph traversal
- Why rejected: The learning analytics and content management workloads would strain a single relational database. JSONB queries are less ergonomic than native MongoDB document operations.
- Pros: Flexible schema, good for content and analytics, horizontal scaling
- Cons: Weak relational integrity, no native JOINs for complex enrollment queries, transaction support less mature
- Why rejected: Credential verification and enrollment management require strong consistency and relational integrity that MongoDB's document model doesn't naturally provide.
- Pros: Strong search capabilities for course discovery
- Cons: Additional infrastructure complexity, operational overhead of keeping indexes in sync
- Why rejected: The search requirements (course name, description, tags) are well-served by PostgreSQL's full-text search and MongoDB's text indexes. Elasticsearch adds complexity without proportional benefit at current scale.
- Workload-optimized: Each database is chosen for its specific workload characteristics
- Scalability: MongoDB handles horizontal scaling for analytics; PostgreSQL handles vertical scaling for transactions
- Developer ergonomics: Direct SQL via
pggives full control over query optimization; Mongoose provides schema flexibility for content - Caching: Redis reduces database load for hot paths (session validation, profile reads)
- Operational complexity: Four databases to manage, monitor, backup, and secure
- Data consistency: No cross-database transactions; eventual consistency between PostgreSQL and MongoDB
- Learning curve: Team needs proficiency in SQL, MongoDB queries, Cypher (Neo4j), and Redis commands
- Infrastructure cost: Higher resource requirements in both development and production
- Backup strategy: Each database requires its own backup configuration (
scripts/backup-db.shfor PostgreSQL, MongoDB snapshots, Redis AOF persistence) - Migrations: PostgreSQL uses migration scripts (
backend/migrations/); MongoDB uses schema versioning in Mongoose models - Monitoring: Need database-specific metrics for each store
docker-compose.yml— service definitions for all databasesbackend/package.json— database driver dependenciesscripts/backup-db.sh— PostgreSQL backup with S3 upload