Architecture Overview
GospeLib starts as a solo-founder product and must eventually support tens of thousands of subscribers. The architecture accomplishes two competing goals simultaneously: be buildable by one person on day one, and never require a rewrite to reach enterprise scale.
The failure mode to avoid is "startup architecture" — the pattern where early decisions bake in assumptions that force a complete rewrite when the product achieves traction. Instead, we adopt evolutionary architecture: add complexity only where it's earned, but design every seam so that complexity can be added without breaking what exists.
Core Principles
Principle 1: Language-Right, Not TypeScript-by-Default
TypeScript is the right choice for UI code, SDK code, and Node-based tooling. It is not the right choice for a high-throughput API gateway, a data ingestion pipeline, or AI feature development. Each service uses the language whose ecosystem best matches its problem domain.
| Language | Services | Why |
|---|---|---|
| Go | Gateway, Auth, Billing, Notifications | Native concurrency, sub-ms overhead, tiny Docker images |
| Python | Content, AI, Ingest | Best graph DB client ecosystem, AI library access, data transformation |
| TypeScript | Web, Admin, Mobile, SDK, UI | React/Next.js ecosystem, shared types across frontends |
Principle 2: Shared Infrastructure, Independent Deployability
Services share infrastructure definitions, config schemas, CI/CD patterns, logging conventions, and error formats — but each service can be built, tested, and deployed independently. No service imports code from another service at runtime. Shared logic lives in explicitly versioned packages.
Principle 3: Seams Before Premature Splitting
On day one, services that are logically distinct may run in the same process. But they are authored as separate modules with their own API contracts from the start. When traffic demands it, they split into separate containers without changing any public interface.
Principle 4: Infrastructure as Code, From Day One
Even local development runs through Docker Compose driven by the same Terraform variable definitions used in production. There is no "I'll add IaC later."
Principle 5: Observability Is Not Optional
Every service emits structured JSON logs, metrics in OpenTelemetry format, and distributed traces with correlation IDs. This is wired up at project init, not added when production breaks.
Principle 6: Data at the Boundary
Services communicate through well-typed contracts (OpenAPI). Internal data models are never exposed directly. Schema versioning is explicit. This makes internal refactoring safe.
Principle 7: The Graph Is the Product
FalkorDB is not a cache or secondary store — it is the product. Every architectural decision about performance, caching, and query design serves the health of the graph. PostgreSQL handles operational data (users, subscriptions, audit logs) that does not belong in the graph.
The Three Phases
Decisions made in Phase 1 must not block Phase 2 or Phase 3.
| Phase | Timeline | Scale | Key Constraints |
|---|---|---|---|
| Phase 1: Solo/Seed | Months 0–18 | 1–3 engineers, 0–10K users | Speed of execution, minimal ops overhead |
| Phase 2: Growth | Months 18–48 | 3–12 engineers, 10K–100K users | Team coordination, service independence, horizontal scale |
| Phase 3: Enterprise | Month 48+ | 12+ engineers, 100K+ users | Compliance, SLA, multi-region, data residency |
Phase 1 may collapse multiple services into one process; Phase 2 and 3 split them. The code is the same either way.
graph LR
P1["Phase 1<br/>Docker Compose<br/>Single node<br/>$150-300/mo"] --> P2["Phase 2<br/>EKS Cluster<br/>Auto-scaling<br/>$1.5-3K/mo"]
P2 --> P3["Phase 3<br/>Multi-region<br/>Service mesh<br/>Enterprise SLAs"]
High-Level Architecture
graph TB
subgraph Clients
Web["Web App<br/>(Next.js)"]
Mobile["Mobile App<br/>(Expo)"]
Admin["Admin Dashboard<br/>(Next.js)"]
end
subgraph Gateway Layer
GW["API Gateway<br/>(Go/Chi)"]
end
subgraph Services
Content["Content Service<br/>(Python/FastAPI)"]
Auth["Auth Service<br/>(Go)"]
Billing["Billing Service<br/>(Go)"]
AI["AI Service<br/>(Python/FastAPI)"]
Notif["Notifications<br/>(Go)"]
end
subgraph Data Stores
FDB["FalkorDB<br/>(Graph DB)"]
PG["PostgreSQL<br/>(+ pgvector)"]
Redis["Redis<br/>(Cache/Queue)"]
TS["Typesense<br/>(Search)"]
end
Web & Mobile & Admin --> GW
GW --> Content & Auth & Billing & AI & Notif
Content --> FDB & Redis & TS
Auth --> PG & Redis
Billing --> PG & Redis
AI --> PG & Redis
Notif --> PG & Redis
What Does NOT Change Across Phases
- API contracts — versioned from day 1
- Service boundaries — drawn correctly from day 1
- Database schemas — migrate forward, never backward
- Frontend code — scales independently via CDN
- Ingest pipeline — runs as a job, not a server
Related Pages
- Monorepo & Tooling — How the codebase is organized
- Technology Stack — Pinned versions and decision rationale
- Service Communication — How services talk to each other
- Scaling Strategy — Phase-specific infrastructure details
- Data Architecture — The four data stores
- Platform — The user-facing product this architecture supports
- Frontend — Frontend architecture and component system
- Operations — Deployment, CI/CD, and infrastructure operations