Skip to main content
Tuturuuu uses a monorepo of conventional Next.js App Router apps that share one Supabase project plus a growing set of workspace packages. We describe this as a “microservices-within-a-monorepo” style: each app deploys independently and owns a product surface, but they are not isolated services with private databases or an inter-service token mesh. This page documents the patterns we actually use today, and flags where the platform is mid-migration.
Active migration. The legacy apps/web Next.js runtime (port 7803) is being replaced by apps/tanstack-web (TanStack Start) for the frontend and apps/backend (Rust) for the API layer. See TanStack Start And Rust Migration for the migration contract and apps/tanstack-web/migration/route-manifest.json for live route-by-route progress. Treat the tRPC and Next.js REST sections below as the legacy apps/web path; new read/write endpoints increasingly land in the Rust backend.

Service Boundaries

Current Services

Additional product apps (apps/chat, apps/drive, apps/mail, apps/cms, apps/mind, apps/teach, apps/track, apps/storefront, and others) follow the same conventional Next.js App Router shape and share the same Supabase project.
All these apps share one Supabase project. There are no per-app distinct Supabase keys, no per-service databases, and no inter-service token system in the codebase today. “Service-specific databases” and an inter-service token mesh are aspirational patterns discussed later, not implemented reality.

Shared Packages

All services share common infrastructure via workspace packages:

Monorepo Architecture

Benefits

  1. Shared Code: Common utilities, UI components, types shared across services
  2. Atomic Changes: Change types in one commit, update all services
  3. Simplified Tooling: Single build system (Turborepo + Bun)
  4. Easy Refactoring: Move code between services, extract packages
  5. Consistent Standards: Shared linting, testing, deployment configs

Structure

Workspace Dependencies

Services declare dependencies on shared packages:

Communication Patterns

1. Background Jobs (Trigger.dev v4)

When to use: Asynchronous workflows, background processing, scheduled work Implementation: Trigger.dev v4 (@trigger.dev/sdk ^4.4.5), using the task() API. Task definitions live in packages/trigger/src. The v2/v3 client.defineJob / eventTrigger / io.runTask APIs do not exist in this repo. Define a task with task() and run it from app code with tasks.trigger(...) (or <task>.trigger(...)):
The import path is @trigger.dev/sdk/v3 even though the installed SDK is the v4 line — that path is the current entrypoint for the task() API.
Characteristics:
  • Loose coupling: producers fire-and-forget, tasks run out of band
  • Resilient to failures, with built-in retries and queue concurrency limits
  • Asynchronous processing
  • Run history and replay in the Trigger.dev dashboard

2. Shared Database (Current)

When to use: Strong consistency requirements, complex queries across entities Implementation: Supabase PostgreSQL with RLS
Characteristics:
  • Strong consistency
  • ACID transactions
  • Complex joins possible
  • Shared schema (requires coordination)
Trade-offs:
  • ✅ Simple implementation
  • ✅ Strong consistency
  • ❌ Tight coupling at data layer
  • ❌ Schema changes affect multiple services

3. tRPC (legacy apps/web, currently a stub)

Status: The tRPC layer in apps/web/src/trpc is intentionally a stub. The router (apps/web/src/trpc/routers/_app.ts) exports a single healthCheck baseProcedure to keep the type system wired up:
The real exports are createTRPCRouter, createTRPCContext, baseProcedure, and createCallerFactory. There is no protectedProcedure, no auth middleware, and no workspace/user/tasks/ai routers — the context is not a product data layer. Product data flows through @tuturuuu/internal-api helpers and REST /api/v1/* routes (below), not through tRPC. In apps/tanstack-web a guard (check-tanstack-api-access) forbids /trpc calls entirely. Do not document tRPC as the canonical internal API surface — it is a thin compatibility shim that may be removed during the migration.

4. REST API (/api/v1, legacy apps/web) and the Rust backend

When to use: Public APIs, webhook endpoints, third-party integrations, and product read/write endpoints. Legacy implementation: Next.js App Router route handlers under apps/web/src/app/api/v1/* (e.g. workspaces, inventory, nova, storage):
Migration target: The Rust backend (apps/backend, port 7820) is progressively taking ownership of these endpoints. Route groups live in apps/backend/src/*.rs (for example inventory.rs, nova.rs, onboarding_progress.rs, aurora.rs), and the contract is documented in apps/backend/api/openapi.yaml. Migration progress per endpoint is tracked in apps/tanstack-web/migration/route-manifest.json. Characteristics:
  • Standard HTTP / JSON
  • Versioned endpoints (/api/v1/...)
  • Rate limiting and authentication
  • OpenAPI documentation for the Rust backend (apps/backend/api/openapi.yaml)

Service Communication Matrix

Most cross-surface coordination is not direct service-to-service RPC: apps share the Supabase database (guarded by RLS) and hand off long-running work to Trigger.dev. This keeps coupling at the data and job layers rather than in a service mesh.

Deployment Strategies

Next.js apps: Vercel

Each Next.js app deploys independently to Vercel. CI builds the selected app, packages prebuilt artifacts, and deploys the production artifact through the matching GitHub Actions workflow. There is one Vercel workflow per app, named by product surface. For example, vercel-production-platform.yaml builds apps/web on pushes to the production branch and deploys the prebuilt production artifact:
Matching vercel-preview-*.yaml workflows run for pull requests. Node engines are >=22 (CI uses Node 24) and Bun is pinned to 1.3.14.

Rust backend

apps/backend is built and validated by .github/workflows/rust-backend.yml. It runs as a native container and keeps a Cloudflare Workers Rust entrypoint (apps/backend/wrangler.jsonc) ready for edge preview deployment, per the migration contract.

Independent Scaling

Each app scales independently on its hosting platform (Vercel autoscaling for the Next.js apps; container/edge scaling for the Rust backend). The per-app minInstances/maxInstances objects shown in earlier versions of this page were illustrative, not a real config file in the repo — scaling is configured in the Vercel project settings and the backend’s container/edge runtime, not in versioned source.

Service Boundary Decisions

When to Create a New Service

Extract to new service when:
  • Feature is logically independent (e.g., URL shortener)
  • Different scaling requirements (high-traffic vs low-traffic)
  • Different technology needs (Python for ML vs TypeScript for web)
  • Team ownership boundary (separate team owns feature)
  • Independent deployment cycle needed
Keep in existing service when:
  • Shares most code with existing service
  • Tight coupling to core domain
  • Low complexity (< 1000 LOC)
  • No special scaling or technology needs

Example: Why finance is a separate app

Data Ownership

Current: Shared Database Pattern

Pros:
  • Simple joins across entities
  • Strong consistency
  • ACID transactions
  • Single source of truth
Cons:
  • Tight coupling at data layer
  • Schema changes affect multiple services
  • Harder to scale independently

Aspirational: Service-Specific Databases

This is not implemented today. Every app shares one Supabase project. The following is a future option, not current architecture.
When to consider:
  • A surface needs a specialized database (e.g., PostGIS for geolocation)
  • Independent scaling requirements for specific data
  • Strong service boundaries needed
Illustrative pattern (not in the codebase):

Package Extraction Strategy

When to Extract to Package

The repo’s root AGENTS.md carries the package-extraction decision matrix. As a rough heuristic, extract when ≥3 HIGH signals: Example extractions in Tuturuuu:

Testing Strategies

Unit Tests

Test individual services in isolation:

Integration Tests

Test service interactions:

End-to-End Tests

Test full user workflows across services (future):

Monitoring & Observability

Service Health

Correlation IDs

There is no dedicated @tuturuuu/logging or @tuturuuu/observability package and no event-store; the example below is an illustrative pattern. Runtime server code should log diagnostics with the native console method matching severity.

Migration Paths

Current State → Future State

Current: A monorepo of Next.js apps over one shared Supabase project, with background work on Trigger.dev v4. In progress: The frontend is migrating from apps/web (Next.js) to apps/tanstack-web (TanStack Start), and the API layer to apps/backend (Rust). This is the concrete, active migration — see TanStack Start And Rust Migration. Longer-term options (not committed):
  1. Service-Specific Databases (when needed)
    • Extract finance data to dedicated DB
    • Communicate via events
    • Maintain consistency with sagas
  2. API Gateway Layer (if REST APIs grow)
    • Single entry point for external clients
    • Route to appropriate services
    • Handle auth, rate limiting centrally
  3. GraphQL Federation (if complex queries needed)
    • Each service exposes GraphQL schema
    • Gateway federates schemas
    • Clients query unified graph
Migration principle: Evolve gradually based on real needs, not speculation.