Skip to main content

Monorepo & Tooling

GospeLib uses a polyglot monorepo orchestrated by Nx with three package managers for three language ecosystems.

Why Nx?

Nx was chosen over Turborepo and Bazel because:

  • Mixed language support — first-class plugins for TypeScript, Python, Go, React Native, and Next.js
  • Project graph — visual dependency graph helps onboard contributors
  • Affected commands — only build/test/lint what changed (nx affected -t test)
  • Remote caching — Nx Cloud for CI cache sharing across branches
  • Generators — scaffolding for new services and packages

Package Managers

EcosystemManagerWhy
TypeScript/JSpnpm 9.15.0Strict hoisting prevents phantom dependency bugs; disk-efficient
Pythonuv (latest)10–100× faster than pip; built-in venv; Rust-native lockfile
GoGo modulesStandard, no alternative needed

Workspace Layout

gospelib/
├── apps/ # Deployable applications
│ ├── web/ # Next.js 15 — web reader (port 3002)
│ ├── admin/ # Next.js 15 — admin dashboard (port 3001)
│ └── mobile/ # Expo 52 / React Native

├── services/ # Backend microservices
│ ├── gateway/ # Go — API gateway (port 8080)
│ ├── content/ # Python — graph queries (port 8100)
│ ├── auth/ # Go — Clerk wrapper (port 8200)
│ ├── billing/ # Go — Stripe (port 8300)
│ ├── ai/ # Python — LLM features (port 8400)
│ ├── notifications/ # Go — push + email (port 8500)
│ ├── plugin-registry/ # Python — plugin manifest registry (port 8500)
│ └── ingest/ # Python — CLI data pipeline

├── packages/ # Shared code packages
│ ├── core/ # Shared domain logic (book registry, events, navigation)
│ ├── ui/ # Cross-platform React Native components
│ ├── types/ # Shared TypeScript types (OpenAPI-generated)
│ ├── sdk/ # Client SDK (openapi-fetch)
│ ├── config/ # ESLint/TSConfig presets + Zod env schemas
│ ├── schemas/ # Shared Pydantic schema models (Python services)
│ ├── scripture-ref/ # Scripture reference parser and formatter
│ ├── plugin-sdk/ # Plugin authoring SDK for plugin ecosystem
│ └── testing/ # Shared test fixtures, mocks, helpers

├── infra/ # Infrastructure as code
│ ├── terraform/ # Terraform modules + environments
│ ├── k8s/ # Kubernetes manifests (Kustomize)
│ ├── docker/ # Docker Compose files
│ └── grafana/ # Dashboards + provisioning

├── tools/ # Developer tooling
│ ├── scripts/ # Shell scripts (setup, health-check, etc.)
│ └── generators/ # Nx generators for scaffolding

├── data/ # Scripture corpus + fixtures
│ ├── book_registry.json # Canonical book metadata
│ └── fixtures/ # Test fixture data

└── docs/ # Project documentation

Dependency Graph

graph TB
subgraph apps["Apps (TypeScript)"]
web["apps/web"]
admin["apps/admin"]
mobile["apps/mobile"]
end

subgraph pkgs["Packages (TypeScript)"]
sdk["@gospelib/sdk"]
types["@gospelib/types"]
ui["@gospelib/ui"]
config["@gospelib/config"]
testing["@gospelib/testing"]
core["@gospelib/core"]
scripref["@gospelib/scripture-ref"]
pluginsdk["@gospelib/plugin-sdk"]
end

subgraph svcs["Services (Go/Python)"]
gateway["gateway"]
content["content"]
auth["auth"]
billing["billing"]
ai["ai"]
notif["notifications"]
end

web --> sdk & ui & config & core & scripref
admin --> sdk & ui & config
mobile --> sdk & ui & config & core
sdk --> types
core --> types & scripref
pluginsdk --> types

gateway --> content & auth & billing & ai & notif

Dependency rules:

  • Apps import all packages
  • @gospelib/sdk imports only @gospelib/types
  • Services never import TypeScript packages at runtime
  • Services communicate only via HTTP or Redis Streams

Task Caching

Nx caches results for build, test, lint, and typecheck targets. The dev target is not cached.

// nx.json (simplified)
{
"targetDefaults": {
"build": { "dependsOn": ["^build"], "cache": true },
"test": { "cache": true },
"lint": { "cache": true },
"typecheck": { "cache": true }
}
}

Affected Commands

Only run tasks for projects affected by your changes:

# Test only what changed since main
pnpm nx affected -t test

# Lint affected projects in parallel
pnpm nx affected -t lint --parallel=4

# See which projects are affected
pnpm nx show projects --affected

Naming Conventions

CategoryConventionExample
npm packages@gospelib/<name>@gospelib/ui
Python packagesgospelib_<name>gospelib_content
Service containersgospelib-<name>gospelib-content
Env varsGOSPELIB_<SERVICE>_<KEY>GOSPELIB_CONTENT_DB_URL
DB schemas/tablesgl_<name>gl_users
Redis keysgl:<service>:<key>gl:content:passage:gen.1.1
API routes/api/v1/<resource>/api/v1/passages
Docker tags<registry>/<service>:<semver>ecr/gospelib-content:1.2.3