Several pages in this bucket describe patterns as general design guidance, not
as fully-adopted Tuturuuu implementations. The platform apps are conventional
Next.js App Router apps (
apps/web) plus a TanStack Start frontend
(apps/tanstack-web) and a Rust backend (apps/backend) that share one
Supabase project. There is no hexagonal layering, event-store, or inter-service
event bus in the codebase today. Where a pattern below is conceptual rather
than implemented, treat it as a “when you would reach for this” guide and trust
the cited source files over the prose.Architectural Philosophy
Our architectural approach is guided by several core principles:- Loose Coupling, High Cohesion: Services and components are independently deployable with well-defined boundaries
- Evolutionary Design: The system can adapt to changing requirements without extensive rewrites
- Resilience by Default: Failures are isolated and don’t cascade across the system
- Developer Experience First: Architecture choices prioritize maintainability and developer productivity
- Pragmatic Trade-offs: We make conscious decisions about complexity vs. simplicity based on actual needs
Architectural Layers
The
tRPC node is intentionally small: apps/web/src/trpc is a stub that
exports only a healthCheck baseProcedure with a hardcoded context. Real
product data flows through packages/internal-api helpers calling REST
/api/v1 routes (legacy apps/web) or the Rust backend (apps/backend). A
guard (check-tanstack-api-access) forbids direct /trpc, /api, and
/internal calls from apps/tanstack-web/src.Core Architectural Patterns
1. Background And Scheduled Tasks (Trigger.dev)
Asynchronous, long-running work runs as Trigger.dev v4task() definitions in
packages/trigger. Today this is scoped to a handful of concrete jobs —
Google Calendar full/incremental sync and the unified habit/task scheduler —
not a general cross-service event bus.
The
@trigger.dev/sdk is on v4 (^4.4.5). The v4 task() API is imported
from @trigger.dev/sdk/v3 (the path is unchanged across the v3→v4 SDK). The
older v2 patterns (client.defineJob, eventTrigger, io.runTask,
io.sendEvent) are not used in this repo.- Background processing that shouldn’t block user requests
- Long-running operations (calendar full sync, batch scheduling)
- Scheduled/recurring work coordinated through Trigger.dev queues and concurrency limits
2. Hexagonal Architecture (Ports & Adapters)
Clean separation between business logic and infrastructure concerns within a service.Tuturuuu apps do not implement full hexagonal layering: there are no
domain/, application/, or infrastructure/ directories in apps/web.
The closest real boundary is packages/internal-api (a typed facade in front
of REST/backend endpoints) and the Rust backend’s per-domain route modules in
apps/backend/src (for example inventory, changelog, aurora). Treat
this section as guidance for where a port/adapter seam is worth introducing,
not a description of an existing layered structure.- Complex business logic that needs protection from technology changes
- Services requiring high testability
- When multiple adapters might be needed (REST + tRPC + GraphQL)
3. Microservices in a Monorepo
Logical service boundaries with shared tooling and dependencies via Turborepo. When to use:- Independent teams working on different features
- Services with different scaling needs
- Features that benefit from technological flexibility
4. Shared UI With Per-App Frontends
Organized frontend with a shared@tuturuuu/ui component library consumed by
each app. The legacy apps/web is a Next.js App Router app (Server Components
by default); the target apps/tanstack-web is a TanStack Start app. Both pull
from the same shared packages.
This was previously framed as a single “React Modular Monolith.” In practice
the frontend is split across distinct apps (
apps/web, apps/tanstack-web,
the Flutter apps/mobile) sharing packages, and the web frontend is mid-
migration from Next.js to TanStack Start.- Complex UIs with shared component libraries
- Multiple client types (web, mobile) sharing logic
- Teams that value fast iteration over distributed deployments
Decision Matrix
| Requirement | Recommended Pattern | Alternative | Trade-off |
|---|---|---|---|
| Background / scheduled job processing | Trigger.dev v4 task() | Synchronous API | Complexity vs. Resilience |
| User-facing API (target) | Rust backend (apps/backend) via packages/internal-api | Direct REST | Centralization vs. Effort |
| User-facing API (legacy) | Next.js API Routes (/api/v1) in apps/web | tRPC | Existing surface vs. migration |
| Shared/typed API access | packages/internal-api facades | Scattered raw fetch | Encapsulation vs. Directness |
| Service boundaries | Monorepo apps + shared packages | Polyrepo | DX vs. Independence |
| Frontend state | TanStack Query + Jotai | Redux | Simplicity vs. Predictability |
| Data access | Supabase RLS | Application-level auth | Security vs. Performance |
tRPC is not the user-facing API layer in Tuturuuu. The
apps/web/src/trpc
module is a stub (healthCheck only). Use packages/internal-api helpers in
front of the Rust backend (apps/backend, target) or legacy apps/web
/api/v1 routes.Key Design Documents
Foundational Decisions
- Architectural Decisions - Why we chose microservices, EDA, hexagonal architecture, and React patterns
Pattern Deep Dives
- Event-Driven Architecture - Advantages, drawbacks, and implementation
- Extensibility, Resilience & Scalability - 15 reasons these properties are prioritized
- Encapsulation Patterns - Preventing unwanted coupling
Implementation Guides
- Hexagonal Architecture - Ports, adapters, and layering
- Microservices Patterns - Service boundaries and communication
Integration with Existing Docs
This system design documentation complements our existing architecture guides:- Routing: Workspace-scoped routing implementation
- Data Fetching: RSC, Server Actions, TanStack Query patterns
- Authentication: Supabase Auth integration
- Authorization: RLS and permission system
- tRPC: Type-safe API layer (currently a stub; not the product API path)
- API Routes: REST endpoint patterns (legacy
apps/web/api/v1) - TanStack Start And Rust Migration: Target frontend/backend split and route-ownership contract
The Routing, Data Fetching, tRPC, and API Routes guides above describe the
legacy
apps/web runtime. For the target architecture, the equivalent path is
TanStack Start loaders/server functions plus Rust backend endpoints fronted by
packages/internal-api.Quality Attributes
Our architecture explicitly optimizes for:- Extensibility: New features can be added with minimal changes to existing code
- Resilience: Failures are isolated and the system degrades gracefully
- Scalability: Services can scale independently based on demand
- Maintainability: Code is organized, testable, and understandable
- Developer Experience: Fast feedback loops and productive workflows
Getting Started
For developers new to the codebase:- Start with Architectural Decisions to understand the “why”
- Review Microservices Patterns to understand service boundaries
- Explore Hexagonal Architecture for code organization within services
- Read Event-Driven Architecture for async communication patterns
- Determine which app the work belongs to (
apps/web,apps/tanstack-web,apps/backend,apps/mobile) - Route shared/typed API access through
packages/internal-apirather than scattered rawfetch - Use Trigger.dev v4
task()definitions inpackages/triggerfor background/scheduled work - Follow data fetching patterns (TanStack Query; no
useEffectfetching) for frontend integration - Keep boundaries clean and add tests; for new TanStack frontend ports, check the migration contract and the
check-tanstack-api-accessguard
Architecture Evolution
This architecture is not static. The most significant in-flight change is the migration of the web frontend from Next.js (apps/web) to TanStack Start
(apps/tanstack-web) plus a dedicated Rust backend (apps/backend), tracked
route-by-route in
TanStack Start And Rust Migration.
As the platform grows, we continuously evaluate:
- When to extract shared logic into new packages
- When to move product API surface from legacy
apps/webroutes to the Rust backend - When to introduce new communication patterns
- When to adopt new technologies