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 pipelinesImport aliases
Path aliases enforce the layer boundaries and keep imports readable:
| Alias | Resolves 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 rule | src/domain/ (add a test alongside) |
| Add or change a backend query | src/data/repositories/ |
| Add a screen or component | app/ and src/presentation/ |
| Change the database schema | add a migration in supabase/migrations/ |