Skip to content

Project structure

WeSpend uses a layered architecture with a pure domain core. The dependency rule points inward: domain imports no framework code, data maps DB rows to domain entities, application orchestrates, and presentation renders.

wespend/
├── app/                      Expo Router routes (thin; delegate to presentation)
│   ├── _layout.tsx           Root layout, session gating, realtime subscription
│   ├── onboarding.tsx        Create or join a household
│   ├── (tabs)/               Home, Settle, History, Card, Bills, Settings
│   ├── expense/new.tsx       Add-expense modal
│   └── meal-card, members, categories, appearance, ...  Settings sub-screens
├── src/
│   ├── presentation/         Screens and components (NativeWind)
│   ├── application/          Query hooks, Zustand stores, realtime, overview orchestrator
│   ├── data/                 Supabase client, repositories, mappers, DB row types
│   ├── domain/               Pure business rules: budget, rollover, settlement, breakdown
│   └── shared/               Config, date, invite-code utilities
├── supabase/migrations/      Schema, RLS policies, bootstrap RPCs
├── e2e/                      Maestro end-to-end flows
├── docs/                     This documentation site (VitePress)
└── .github/workflows/        CI and release pipelines

Import aliases

Path aliases enforce the layer boundaries and keep imports readable:

AliasResolves to
@domain/*src/domain/*
@data/*src/data/*
@application/*src/application/*
@ui/*src/presentation/*
@shared/*src/shared/*

They are configured in both tsconfig.json (for the editor and typecheck) and jest.config.js (for tests).

Where to make a change

You want to...Edit
Change a budget or settlement rulesrc/domain/ (add a test alongside)
Add or change a backend querysrc/data/repositories/
Add a screen or componentapp/ and src/presentation/
Change the database schemaadd a migration in supabase/migrations/

Spend together, settle simply.