Skip to content

Architecture Decision Records (ADRs)

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