CRUMB a card from devarno-cloud

Microservices Topology Overview

tektree beginner 5 min read

ELI5

Tektree is a food court: each stall (service) cooks one thing — users, knowledge, gamification, payments, realtime, jobs — and a single host stand at the door (the gateway) checks IDs and walks every order to the right stall. The mall building itself is just the parking lot; each stall has its own lease (its own git repo).

Technical Deep Dive

The workspace at /home/devarno/code/workspace/tektree-io mirrors the tektree-io GitHub org. Every directory under services/, libs/, frontend/, infra/, docs/ is an independent git repo with its own go.mod/package.json and CI. The workspace root has no top-level Makefile or monorepo manifest — cd into the subrepo before running tooling.

C4 Context Diagram

---
title: "TekTree System Context"
---
flowchart TD
user(("<b>End User</b><br/>Web or mobile client")):::person
subgraph tt ["**TekTree Platform**"]
gw["<b>api-gateway :8080</b><br/>Public ingress, JWT, rate-limit, reverse proxy"]:::system
svc["<b>Backend Services</b><br/>user, knowledge, gamification, payment, realtime"]:::system
wp["<b>worker-pool</b><br/>Redis Streams job consumer"]:::system
end
mongo[("<b>MongoDB</b><br/>Primary datastore")]:::db
redis[("<b>Redis</b><br/>Rate-limit + Streams bus")]:::db
polar["<b>Polar.sh</b><br/>Subscription billing"]:::ext
user -- "HTTPS / WebSocket" --> gw
gw -- "HTTP, X-User-ID/X-User-Tier headers" --> svc
svc -- "drivers" --> mongo
svc -- "cache + event-bus" --> redis
wp -- "XREADGROUP jobs" --> redis
svc -- "checkout + webhooks" --> polar
classDef person fill:#1c1c24,stroke:#e85d3e,color:#f0ece6
classDef system fill:#1c1c24,stroke:#d4a574,color:#f0ece6
classDef ext fill:#141419,stroke:#8b7e74,color:#f0ece6,stroke-dasharray: 4 3
classDef db fill:#1c1c24,stroke:#d4a574,color:#f0ece6
classDef container fill:#1c1c24,stroke:#d4a574,color:#f0ece6

Service Inventory

ServicePortCmdRole
services/api-gateway8080cmd/server/main.goOnly public ingress; JWT auth; tier rate-limit; reverse-proxy
services/user-service8081cmd/server/main.goIdentity / profile CRUD
services/knowledge-service8082cmd/server/main.goAreas, questions, insights, resources
services/gamification-service8083cmd/server/main.goXP, achievements, leaderboards
services/payment-service8084cmd/server/main.goPolar subscriptions + webhooks
services/realtime-service8085cmd/server/main.goWebSocket hub + Redis presence
services/worker-poolcmd/worker/main.goBackground jobs from Redis Stream jobs
services/api (legacy)8000main.goPre-split monolith; 23 controllers; cookie auth

Shared libs sit under libs/: shared-go (auth, config, logging, metrics, errors), event-bus (Redis Streams envelope + publisher/subscriber), proto (gRPC scaffolding — generated, not used between services today). Clients live under frontend/web (Next.js 14 App Router, pnpm) and frontend/mobile (Expo SDK 50).

Edit-scope Rule

Per the workspace CLAUDE.md, each subrepo is independent — build, test, and lint commands run per-subrepo. Scope a change to one subrepo at a time. Cross-cutting docs in docs/docs/architecture/ (ARCHITECTURE_OVERVIEW.md, EVENT_CATALOG.md, DATA_MODELS.md, SECURITY_ARCHITECTURE.md, OBSERVABILITY_PLAN.md) are the source of truth for cross-service decisions.

Key Terms

  • Subrepo → an independent git repo nested in the workspace mirror; not a monorepo package.
  • api-gateway → the only public ingress; downstream services are not directly exposed.
  • Public ingress → port :8080; everything else is internal-only by convention.
  • Worker-pool → headless service consuming Redis Stream jobs; no HTTP surface.

Q&A

Q: Where do you regenerate gRPC code if you change libs/proto/user.proto? A: From the libs/proto subrepo using its buf.gen.yaml / buf.yaml configuration. Generated Go is checked in; do not hand-edit it.

Q: Why is services/api listed but flagged “legacy”? A: It is the pre-split monolithic Gin server (port 8000, 23 controllers under controllers/, models/, routes/ at the top level). Treat it as legacy unless a task explicitly targets it; new work belongs in the matching services/<name>-service subrepo.

Q: What enforces tier-based rate limits? A: Only the api-gateway. Downstream services trust the gateway and do not re-implement limits.

Examples

To add a new endpoint that lists a user’s earned achievements: edit services/gamification-service/internal/handlers/handlers.go and register the route in cmd/server/main.go; then add ANY /achievements/*path (or a more specific route group) to services/api-gateway/internal/routes/routes.go. Two subrepos touched, two CI runs.

neighbors on the map