Skip to main content

Architecture Decision Records (ADRs)

Overview​

Architecture Decision Records (ADRs) capture important architectural decisions made during the development of the Learnille platform. Each ADR describes the context, decision, and consequences of a significant architectural choice.

ADR Template​

ADR [Number]: [Title]​

Date: YYYY-MM-DD

Status: [Proposed | Accepted | Rejected | Deprecated | Superseded]

Context: [Describe the context and problem being solved]

Decision: [Describe the decision that was made]

Consequences: [Describe the positive and negative consequences of the decision]

Alternatives Considered: [List alternative solutions that were considered]

Related ADRs: [Links to related ADRs]


Current ADRs​

ADR 001: Technology Stack Selection​

Date: 2024-01-15

Status: Accepted

Context: We needed to choose a technology stack for building the Learnille platform that would support rapid development, scalability, and maintainability. The platform requires handling user authentication, course management, payments, and real-time features.

Decision: We decided to use:

  • Frontend: React with TypeScript (Vite)
  • Backend: Node.js with NestJS framework
  • Database: PostgreSQL (Self-Hosted)
  • Cache & Queue: Redis / BullMQ
  • Search: OpenSearch for vector and course search
  • Storage: Cloudflare R2 (S3 API compatible) / MinIO locally
  • Hosting & Observability: Self-Hosted Infrastructure with New Relic APM/Logs & Sentry

Consequences:

  • Positive:

    • Strong TypeScript support improves code quality and developer experience
    • NestJS provides excellent structure and scalability
    • PostgreSQL offers robust relational data management
    • Cloudflare R2 eliminates egress bandwidth costs
    • Self-hosting provides operational autonomy and predictable costs
    • New Relic provides centralized APM and log aggregation
  • Negative:

    • Self-hosting requires maintaining server deployment scripts and backups
    • Team operational responsibility for infrastructure health

Alternatives Considered:

  • Python/Django: More mature but slower development
  • Ruby on Rails: Rapid development but less scalable
  • Go: High performance but smaller ecosystem

ADR 002: Microservices Architecture​

Date: 2024-01-20

Status: Accepted

Context: As the platform grows, we need to ensure scalability and maintainability. The monolithic architecture was becoming complex to manage and deploy.

Decision: We decided to adopt a microservices architecture with the following services:

  • User Service
  • Course Service
  • Payment Service
  • Notification Service
  • Analytics Service

Consequences:

  • Positive:

    • Independent deployment and scaling
    • Technology diversity per service
    • Better fault isolation
    • Smaller, focused teams
  • Negative:

    • Increased complexity in deployment and monitoring
    • Distributed system challenges (consistency, latency)
    • Higher operational overhead

Alternatives Considered:

  • Monolithic with modules: Simpler but less scalable
  • Serverless functions: Good scalability but vendor lock-in

ADR 003: API Design with REST​

Date: 2024-01-25

Status: Accepted

Context: We needed a consistent API design that would be easy to understand, version, and maintain for both internal and external consumers.

Decision: We chose RESTful API design with the following principles:

  • Resource-based URLs
  • Standard HTTP methods
  • JSON responses
  • OpenAPI/Swagger documentation
  • Versioning through URL paths (/api/v1/)

Consequences:

  • Positive:

    • Widely adopted and understood
    • Good tooling support
    • Easy to cache and debug
    • Works well with HTTP infrastructure
  • Negative:

    • Can lead to over-fetching/under-fetching
    • Multiple round trips for complex data
    • Less efficient for mobile networks

Alternatives Considered:

  • GraphQL: More flexible but complex
  • gRPC: High performance but language-specific

ADR 004: Database Schema Design​

Date: 2024-02-01

Status: Accepted

Context: We needed to design a database schema that supports the core business entities while maintaining data integrity and performance.

Decision: We chose PostgreSQL with the following design principles:

  • Normalized schema to reduce redundancy
  • UUID primary keys for scalability
  • JSONB columns for flexible data
  • Proper indexing strategy
  • Foreign key constraints for data integrity

Consequences:

  • Positive:

    • Strong data consistency
    • Excellent JSON support for flexible data
    • Advanced querying capabilities
    • Good performance with proper indexing
  • Negative:

    • More complex queries for denormalized data
    • Migration complexity
    • Learning curve for advanced features

Alternatives Considered:

  • MongoDB: Flexible schema but weaker consistency
  • MySQL: Familiar but less advanced features

ADR 005: Authentication with JWT​

Date: 2024-02-05

Status: Accepted

Context: We needed a secure and scalable authentication system that works well with microservices and mobile applications.

Decision: We implemented JWT-based authentication with:

  • Access tokens with short expiration (1 hour)
  • Refresh tokens for session management
  • Role-based access control (RBAC)
  • Secure token storage

Consequences:

  • Positive:

    • Stateless authentication
    • Works well with microservices
    • Good mobile app support
    • Industry standard
  • Negative:

    • Token revocation complexity
    • No server-side session invalidation
    • Token size can be large

Alternatives Considered:

  • Session-based auth: Server state management
  • OAuth 2.0: More complex but more secure

ADR 006: Payment Processing with Stripe​

Date: 2024-02-10

Status: Accepted

Context: We needed a reliable payment processing solution that handles various payment methods and provides good developer experience.

Decision: We integrated Stripe for payment processing with:

  • Support for multiple payment methods
  • Webhook handling for payment events
  • PCI compliance
  • Subscription management

Consequences:

  • Positive:

    • Excellent developer documentation
    • Comprehensive API
    • Strong security and compliance
    • Good international support
  • Negative:

    • Transaction fees
    • Dependency on third-party service
    • Webhook reliability concerns

Alternatives Considered:

  • PayPal: Higher fees, less developer-friendly
  • Braintree: Good but more complex integration

ADR 007: Deployment with Kubernetes​

Date: 2024-02-15

Status: Proposed

Context: We need a container orchestration solution that can handle our microservices deployment, scaling, and management.

Decision: We are considering Kubernetes for container orchestration with:

  • Automated deployment and scaling
  • Service discovery and load balancing
  • Configuration management
  • Monitoring and logging integration

Consequences:

  • Positive:

    • Industry standard for container orchestration
    • Highly scalable and reliable
    • Rich ecosystem of tools
    • Cloud-agnostic
  • Negative:

    • Steep learning curve
    • Complex setup and maintenance
    • Resource overhead

Alternatives Considered:

  • Docker Compose: Simple but not scalable
  • AWS ECS: AWS-specific, less flexible

ADR Process​

Creating a New ADR​

  1. Identify Decision: Recognize when an architectural decision needs to be made
  2. Gather Context: Document the problem and constraints
  3. Evaluate Options: Consider multiple alternatives
  4. Make Decision: Choose the best option based on criteria
  5. Document ADR: Create ADR document following the template
  6. Review: Get feedback from team members
  7. Implement: Put the decision into practice
  8. Monitor: Track outcomes and adjust if needed

ADR Status Definitions​

  • Proposed: Decision is being considered
  • Accepted: Decision has been made and implemented
  • Rejected: Decision was considered but not chosen
  • Deprecated: Decision is no longer relevant
  • Superseded: Decision has been replaced by another

ADR Maintenance​

  • Review ADRs annually for relevance
  • Update status when decisions change
  • Link related ADRs for context
  • Use ADRs for onboarding new team members

Tools and Templates​

ADR Creation Script​

#!/bin/bash
# Create new ADR
NEXT_NUMBER=$(ls adr-*.md | grep -o '[0-9]\+' | sort -n | tail -1 | awk '{print $1+1}')
FILENAME="adr-$(printf "%03d" $NEXT_NUMBER)-title.md"
cp adr-template.md $FILENAME

ADR Template File (adr-template.md)​

# ADR [NUMBER]: [TITLE]

**Date:** $(date +%Y-%m-%d)

**Status:** Proposed

**Context:**
[Describe the context and problem]

**Decision:**
[Describe the decision]

**Consequences:**
[Positive and negative consequences]

**Alternatives Considered:**
[Other options]

**Related ADRs:**
[Links to related ADRs]

ADR 008: Beacon Analytics - Direct Database Writes​

Date: 2025-01-15

Status: Accepted

Context: The beacon analytics system tracks user engagement events (impressions, clicks, bounces) for marketplace entities. We needed to decide between:

  1. Direct database writes on each event
  2. Queue-based processing with background workers
  3. Time-series database for analytics data

Current traffic estimate: ~100 events/minute at launch, scaling with user growth.

Decision: We chose direct PostgreSQL writes with the following mitigations:

  • Client-side batching (10 events or 2s timeout)
  • Redis-based deduplication (prevents duplicate DB writes)
  • Rate limiting (50 requests per 5 minutes per IP)
  • Session-based authentication (reduces spam)

Consequences:

  • Positive:

    • Simple architecture, easy to debug
    • No additional infrastructure (queues, workers)
    • Lower latency for real-time dashboards
    • Fewer moving parts to maintain
  • Negative:

    • DB load scales linearly with traffic
    • Potential write contention at high scale
    • No built-in retry mechanism for failed writes

Scaling Triggers: Reconsider this decision when:

  • DB write latency consistently exceeds 50ms
  • Traffic reaches ~1000 concurrent users (~10k events/min)
  • Analytics queries start impacting main DB performance

Migration Path: When scaling is needed:

  1. Phase 1: BullMQ queue with background worker (already in stack)
  2. Phase 2: Batch inserts (100-500 events per query)
  3. Phase 3: TimescaleDB for time-series optimization

Alternatives Considered:

  • BullMQ Queue: Better scalability but added complexity
  • Kafka: Enterprise-grade but overkill for current scale
  • TimescaleDB: Excellent for analytics but migration overhead
  • ClickHouse: High performance but operational complexity

Related ADRs:

  • ADR 001: Technology Stack Selection
  • ADR 004: Database Schema Design