Skip to content

Video Streaming System Architecture

Date: 2025-07-04
Status: Accepted
Authors: Canh
Deciders: canh-labs
Supersedes: -
Superseded-by: -

Context and Problem Statement

We are building a video streaming system where users can browse and watch videos on demand. The system must handle streaming efficiently, support video metadata management, in-memory caching, background download jobs, and telemetry for monitoring.

We adopt the C4 Model to document our software architecture in 3 levels: System Context, Container, and Component (for Backend).


Decision Drivers

  • Efficient video streaming with support for chunked responses
  • Clear separation between frontend/backend/database
  • Future-proof design with Redis integration and observability
  • Developer onboarding and system clarity

Considered Options

  • Express + Node.js (not chosen)
  • Monolithic Spring Boot with embedded Tomcat and Virtual Threads ✅
  • CDN-based architecture (planned in future)

Decision Outcome

Adopt Spring Boot monolith with clearly defined layers and caching mechanism, and monitor it via OpenTelemetry and Grafana Cloud.


Architecture Overview (C4 Model)

Level 1 - System Context

img.png


Level 2 - Container Diagram

img_1.png


Level 3 - Component Diagram (Backend)

img_2.png


Deployment Diagram

Deployment.png


Deployment process (Current CICD)

CiCd-Deployment.png


Gitops Continuous Deployment Process (Future improvement)

Gitops-arch.png


Consequences

  • Easy to scale the backend horizontally
  • Clear layering improves maintainability
  • Video chunk caching can be switched from Guava to Redis later
  • Monitoring is decoupled and aligned with observability standards

✨ Optimizations

  • ✅ Uses virtual threads for controller to handle concurrent streams efficiently
  • ✅ Uses Guava cache abstraction to allow future swap to Redis
  • ✅ OTEL-compatible tracing enabled via agent .jar and WithSpan on service methods
  • ✅ Metrics collected via CacheStatsService for total hits, misses, and per-file stats

Future Improvements

  • Switch to HLS or DASH for adaptive bitrate
  • Upload background jobs to warm cache by pre-fetching popular videos
  • Support Redis-based distributed cache with same AppCache abstraction
  • Use async Netty-based streaming for ultimate IO scalability

Notes

  • Virtual Threads are used in Backend for efficient streaming
  • In-memory cache with Guava used until Redis is integrated
  • CronJob handles background downloading and database update

Architecture Decision Records (ADR)

The following ADRs record the key decisions made for this system. Each links to the full decision document including context, options considered, and consequences.

ADR Title Status
ADR-0001 Use Relational Database (PostgreSQL) for Core Data Accepted
ADR-0002 Use Layered Spring Boot Architecture with Virtual Threads Accepted
ADR-0003 Use Guava Cache Initially, with Planned Migration to Redis Accepted
ADR-0004 Use Vanilla JS + Bootstrap with Planned Migration to React Accepted
ADR-0005 Use LRU Cache for Video Streaming Accepted
ADR-0006 Switch to HLS for Local Video Streaming Proposed
ADR-0007 Admin Dashboard — Content & Account Management Accepted
ADR-0008 Migrate Frontend to React with Vite Proposed
ADR-0009 Unified Video List API for UI Display Proposed
ADR-0010 Add Trivy Dependency & Filesystem Scanning to CI Accepted
ADR-0011 Hot Video Priority Scoring Accepted

Decision Map

The diagram below shows how the ADRs relate to each architectural layer:

graph TD
    subgraph "Data Layer"
        ADR1[ADR-0001<br/>PostgreSQL]
    end

    subgraph "Backend"
        ADR2[ADR-0002<br/>Spring Boot + Virtual Threads]
        ADR3[ADR-0003<br/>Guava Cache → Redis]
        ADR5[ADR-0005<br/>LRU Cache for Streaming]
        ADR6[ADR-0006<br/>HLS Streaming]
        ADR7[ADR-0007<br/>Admin Dashboard]
        ADR10[ADR-0010<br/>Trivy CI Scanning]
    end

    subgraph "Frontend"
        ADR4[ADR-0004<br/>Vanilla JS → React]
        ADR8[ADR-0008<br/>React + Vite Migration]
        ADR9[ADR-0009<br/>Unified Video List API]
    end

    ADR2 --> ADR1
    ADR3 --> ADR2
    ADR5 --> ADR3
    ADR6 --> ADR5
    ADR7 --> ADR2
    ADR8 --> ADR4
    ADR9 --> ADR8
    ADR10 --> ADR2