Architecture

From request to response.

WC26 Threads is a small but properly-shaped backend with a native Android client. Every layer is a choice I'd defend in an interview.

Request flow

An HTTPS request from a client to a JSON response, in one picture:

Android / curl HTTPS request Cloudflare DNS resolves to VM IP ORACLE CLOUD VM · UBUNTU 24.04 nginx TLS termination · reverse proxy routes /api/* → :8080 DOCKER COMPOSE NETWORK Ktor 3 · Kotlin routing · auth · validation listens on :8080 PostgreSQL 16 internal only · volume-backed

The stack, layer by layer

LayerChoiceWhy
Language Kotlin One language across backend and Android. Modern, expressive, null-safe.
Backend framework Ktor 3 Coroutine-first, minimal, designed by JetBrains. Clean fit for Kotlin idioms.
Database PostgreSQL 16 Boring, battle-tested, free. CITEXT, JSON, generated columns when needed.
SQL framework Exposed Typed Kotlin DSL over JDBC. Repository pattern, no ORM magic.
Migrations Flyway Versioned, idempotent, ships with the app. Source of truth for schema.
Auth JWT + BCrypt Stateless tokens, server-side hashing at cost 12. Separate auth provider for admin routes.
Connection pool HikariCP The standard. Configured for the app's expected concurrency.
Pagination Cursor + offset Cursor for infinite-scroll feeds (posts), offset for bounded resources (matches).
Mobile client Native Android Jetpack Compose, Material 3, MVVM + Clean, Paging 3, Hilt.
Hosting Oracle Cloud (Always Free) Ampere A1 ARM, 2 OCPU / 12 GB RAM. Zero cost at this scale.
Reverse proxy nginx TLS termination, path-based routing, home for the static sites too.
TLS Let's Encrypt Auto-issued, auto-renewed via certbot's systemd timer.
CI/CD GitHub Actions Build, push to GHCR, SSH deploy on every push to main.
Container registry GitHub Container Registry Free for public images, lives next to the source.

Android Client Architecture

The Android client is a native app engineered to represent Google's Modern Android Development (MAD) standards. It is built as a reactive, single-activity application that mirrors the backend's clean engineering principles.

Key Architectural Choices

Clean Architecture & MVVM: The app is structured feature-by-feature (e.g., matches, posts, notifications, auth). Inside each feature, the presentation layer (Compose + ViewModels) interacts with pure-Kotlin use cases and domain entities, keeping business logic isolated from Android framework dependencies and database/network details.

Type-Safe Navigation (Compose 2.8+): Instead of error-prone string routes like "match_detail/{id}", destinations are declared as compile-safe @Serializable data classes and objects. Arguments are checked at compile-time and parsed automatically, preventing runtime routing crashes.

Reactive Session & App-Control Interceptor: A centralized OkHttp interceptor manages security and application flow at the network layer. It automatically attaches auth headers and intercepts response status codes to manage:

  • 401 Unauthorized: Reactively wipes the local DataStore session and redirects the user to the welcome screen.
  • 426 Upgrade / 503 Maintenance: Buffers response bodies in-flight to check for app blocking states, notifying the app status manager to show system overlay blockers globally.
  • 503 Maintenance: Buffers response bodies in-flight to check for maintenance states, letting the app reactively show maintenance screens.

Unified Cursor-Based Pagination: Feed rendering relies on Jetpack Paging 3. To avoid duplicated logic, a generic PostPagingSource accepts a suspension lambda to perform network requests. This allows the exact same pagination implementation to drive the global feed, match threads, and user profile posts.

FCM Integration with Graceful Mock Fallback: Real-time push notifications use Firebase Cloud Messaging to refresh feed feeds and update unread counts reactively. To ensure a smooth out-of-the-box build experience for other developers, a custom Gradle task copies a mock configuration template if the real google-services.json is missing, allowing the app to compile and run with silent notification degradation.