The layers
Key principles
Resolvers are thin. A GraphQL resolver receives input, calls a utility function, and returns a result. It never contains business logic, never calls the database directly, and never imports a vendor SDK. Tools are injected, not imported. Every resolver and utility receives atools object. No part of the codebase imports pino, Stripe, or redis directly — it only calls tools.logger, tools.paymentProcessor, tools.cache.
Configuration resolves once. process.env is read exactly once, in apps/backend/src/configuration/<env>.ts. Everything downstream receives a typed, validated ConfigType.
Implementations are swappable. Every external dependency — logger, queue, metrics, mailer, analytics — is pluggable. Change the client value in config and the loader dynamically imports the right package. Nothing else changes.
Architecture deep dives
What’s next?
Start with whichever system you’re about to touch:- Authentication — sessions, Passport, email verification, OAuth
- Platform MCP — super-admin MCP server, OAuth, registry
- GDPR Consent Flow — cookie/session consent propagation and analytics gates
- RBAC — roles, scopes, and how feature access is gated
- Billing — Stripe subscriptions, webhooks, checkout, downgrades
- Tooling system — how pluggable tools are loaded and swapped
- Frontend app — React + GraphQL wiring, generated hooks, and Apollo caching
