# dipto0321/spendrift-fe architecture

> Architecture diagram of the GitHub repository dipto0321/spendrift-fe, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-06-27). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/dipto0321/spendrift-fe
- Repository: https://github.com/dipto0321/spendrift-fe

## Overview

1. **Overall shape**
   - Spendrift is a **TanStack Start** frontend that renders React on the server/client and delegates all business data to an external **FastAPI Spendrift API**.
   - The repo is organized as a **feature-based, domain-oriented frontend** rather than by technical layer alone.

2. **Primary architectural boundary**
   - The core boundary is the **repository seam** inside each feature:
     - `presentation` talks to React Query hooks
     - hooks call `data/repository.ts`
     - repositories call `shared/api/client.ts`
   - This keeps pages and UI from touching `fetch` or wire formats directly.

3. **Feature structure**
   - Each major product area lives in `src/features/<feature>/`:
     - `auth`
     - `trackers`
     - `expenses`
     - `budgets`
     - `dashboard`
     - `reports`
   - Each feature is split into:
     - `domain/` for types, schemas, and pure services
     - `data/` for DTO mapping, API calls, and query keys
     - `presentation/` for pages, React Query hooks, and feature UI

4. **Domain layer responsibilities**
   - The `domain/` layer contains the business rules that should stay stable even if the API or UI changes.
   - Examples:
     - budget calculations and savings-health logic
     - expense categorization and report aggregation
     - feature-level schema validation and typed domain models
   - These services are unit-tested with **Vitest** in the feature folders.

5. **Data layer responsibilities**
   - The `data/` layer is the translation layer between API and domain.
   - It handles:
     - DTO mapping from `snake_case` to `camelCase`
     - money values as wire strings to domain numbers
     - query-key definitions for TanStack Query caching and invalidation
   - This is where the app adapts to backend contracts without polluting domain logic.

6. **API and auth flow**
   - `src/shared/api/client.ts` is the single HTTP client used by repositories.
   - Authentication uses **JWT access + refresh tokens stored in localStorage**.
   - The client implements a **single-flight refresh-on-401 retry** flow, which is important for the architecture because it centralizes auth recovery instead of spreading it across features.

7. **Route and shell composition**
   - Routes are file-based under `src/routes/` and map to the major application screens.
   - `src/routes/__root.tsx` acts as the application shell and likely wires:
     - shared layout
     - provider setup
     - auth/workspace gating
   - The root-level gate is necessary because SSR cannot read the localStorage-based auth tokens.

8. **Tracker as a cross-cutting concept**
   - A **tracker** is the app’s key workspace boundary: each tracker is an independent financial context with its own currency, expenses, budgets, and reports.
   - The active tracker is carried in the URL as `?tracker=<id>`.
   - This means tracker selection influences almost every feature and is not just a page-level filter; it is a core application context.

9. **Feature interactions**
   - **Expenses** is the transactional source of truth for spending data and category management.
   - **Budgets** consumes spending data to compare monthly limits vs actual spend.
   - **Dashboard** aggregates current-month budget, spend, cashflow, and recent expenses.
   - **Reports** consumes historical expense data for time-based analytics and comparisons.
   - **Auth** and **Trackers** sit underneath those features as access/context providers.

10. **UI composition**
    - The repo uses **ShadCN UI / Radix primitives** in `src/components/ui/` for reusable low-level controls.
    - Feature screens build on top of shared app-level components in `src/shared/ui/` such as:
      - sidebar and page chrome
      - stat cards
      - theme toggle
      - devtools panel
    - This separates generic UI primitives from feature-specific controls like expense tables, budget forms, and charts.

11. **Design system and styling**
    - Styling is driven by **TailwindCSS** with theme tokens defined in `src/styles.css`.
    - The app is intentionally **dark-theme-first** with emerald-oriented semantic colors.
    - `public/theme-init.js` helps establish the theme early, before React hydration, to avoid flashes of incorrect theme.

12. **Charts, analytics, and reporting**
    - Dashboard and reports rely on chart components built around **Recharts** through ShadCN chart wrappers.
    - These are used for spending trends, category breakdowns, needs-vs-wants views, and multi-year comparisons.
    - This makes analytics a first-class UI subsystem rather than a secondary detail.

13. **Internationalization and environment**
    - Translation files live in `messages/en.json` and `messages/de.json`, with Inlang project config under `project.inlang/`.
    - Environment handling is centralized in `src/env.ts`, with the API base URL and Sentry settings coming from `.env.local` / Vite env vars.
    - These are supporting boundaries that affect bootstrapping rather than business logic.

14. **Observability and tooling**
    - `instrument.server.mjs` indicates server-side instrumentation, aligned with the README’s **Sentry** integration.
    - The repo uses:
      - **Biome** for formatting/linting
      - **Vitest** for domain tests
      - **TanStack Query devtools** in the app for cache inspection
    - These tools support the architecture by making the feature/repository split observable and testable.

15. **What to draw in an architecture diagram**
    - Draw the app as a **TanStack Start shell** on top, with route pages feeding into feature presentation layers.
    - Show each feature as a self-contained vertical slice: presentation → data → API, with domain logic beside the data boundary.
    - Place **shared API client**, **shared UI**, **TanStack Query**, and **tracker context** as cross-cutting services.
    - External dependencies are primarily the **Spendrift FastAPI backend**, plus **Sentry** and the translation/theme infrastructure.

Main entry points: [`src/features/expenses/data/repository.ts`](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/expenses/data/repository.ts) (Feature data), [`src/routes/index.tsx`](https://github.com/dipto0321/spendrift-fe/blob/main/src/routes/index.tsx) (Routes), [`src/features/trackers/presentation/TrackerContext.tsx`](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/trackers/presentation/TrackerContext.tsx) (Trackers), [`src/shared/api/client.ts`](https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/api/client.ts) (API client)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_shell["App Shell"]
  node_root["Root shell<br/>TanStack Start app shell<br/>[__root.tsx]"]
  node_router["Routes<br/>file-based routing<br/>[index.tsx]"]
end

subgraph group_features["Features"]
  node_trackers["Trackers<br/>workspace context<br/>[TrackerContext.tsx]"]
  node_auth["Auth<br/>identity feature<br/>[SignInPage.tsx]"]
  node_expenses["Expenses<br/>spending feature<br/>[ExpensePage.tsx]"]
  node_budgets["Budgets<br/>budget feature<br/>[BudgetPage.tsx]"]
  node_dashboard["Dashboard<br/>overview feature<br/>[DashboardPage.tsx]"]
  node_reports["Reports<br/>analytics feature<br/>[ReportsPage.tsx]"]
  node_featuredata["Feature data<br/>repository + dto layer<br/>[repository.ts]"]
  node_featuredomain["Feature domain<br/>business rules<br/>[services.ts]"]
end

subgraph group_shared["Shared Core"]
  node_query["Query provider<br/>TanStack Query<br/>[root-provider.tsx]"]
  node_devtools["Query devtools<br/>debug tooling<br/>[devtools.tsx]"]
  node_http["API client<br/>HTTP client<br/>[client.ts]"]
  node_tokens["Token store<br/>localStorage auth<br/>[tokens.ts]"]
  node_ui["UI primitives<br/>ShadCN/Radix<br/>[button.tsx]"]
  node_appui["App UI<br/>shared components<br/>[PageHeader.tsx]"]
  node_charts["Charts<br/>[chart.tsx]"]
end

subgraph group_platform["Platform"]
  node_theme["Theme init<br/>bootstrap script<br/>[theme-init.js]"]
  node_env["Env config<br/>runtime config<br/>[env.ts]"]
  node_i18n["Translations<br/>Inlang messages<br/>[en.json]"]
  node_backend[("Spendrift API<br/>external backend")]
  node_sentry(("Sentry<br/>observability service"))
end

node_root -->|"provides"| node_query
node_root -->|"hosts"| node_router
node_root -->|"gates"| node_trackers
node_router -->|"loads"| node_auth
node_router -->|"loads"| node_expenses
node_router -->|"loads"| node_budgets
node_router -->|"loads"| node_dashboard
node_router -->|"loads"| node_reports
node_auth -->|"uses"| node_http
node_expenses -->|"queries"| node_featuredata
node_budgets -->|"queries"| node_featuredata
node_dashboard -->|"queries"| node_featuredata
node_reports -->|"queries"| node_featuredata
node_featuredata -->|"maps to"| node_featuredomain
node_featuredomain -->|"depends on"| node_http
node_http -->|"reads/writes"| node_tokens
node_http -->|"requests"| node_backend
node_http -.->|"reports"| node_sentry
node_trackers -->|"scopes"| node_featuredata
node_trackers -->|"filters"| node_dashboard
node_trackers -->|"filters"| node_expenses
node_trackers -->|"filters"| node_budgets
node_trackers -->|"filters"| node_reports
node_ui -->|"supports"| node_appui
node_appui -->|"composes"| node_dashboard
node_appui -->|"composes"| node_expenses
node_appui -->|"composes"| node_budgets
node_appui -->|"composes"| node_reports
node_charts -->|"renders"| node_dashboard
node_charts -->|"renders"| node_reports
node_theme -->|"boots"| node_root
node_env -->|"configures"| node_http
node_i18n -->|"localizes"| node_root
node_query -->|"caches"| node_featuredata
node_devtools -.->|"inspects"| node_query

click node_root "https://github.com/dipto0321/spendrift-fe/blob/main/src/routes/__root.tsx"
click node_router "https://github.com/dipto0321/spendrift-fe/blob/main/src/routes/index.tsx"
click node_query "https://github.com/dipto0321/spendrift-fe/blob/main/src/integrations/tanstack-query/root-provider.tsx"
click node_devtools "https://github.com/dipto0321/spendrift-fe/blob/main/src/integrations/tanstack-query/devtools.tsx"
click node_http "https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/api/client.ts"
click node_tokens "https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/api/tokens.ts"
click node_trackers "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/trackers/presentation/TrackerContext.tsx"
click node_auth "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/auth/presentation/SignInPage.tsx"
click node_expenses "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/expenses/presentation/ExpensePage.tsx"
click node_budgets "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/budgets/presentation/BudgetPage.tsx"
click node_dashboard "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/dashboard/presentation/DashboardPage.tsx"
click node_reports "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/reports/presentation/ReportsPage.tsx"
click node_featuredata "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/expenses/data/repository.ts"
click node_featuredomain "https://github.com/dipto0321/spendrift-fe/blob/main/src/features/budgets/domain/services.ts"
click node_ui "https://github.com/dipto0321/spendrift-fe/blob/main/src/components/ui/button.tsx"
click node_appui "https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/ui/PageHeader.tsx"
click node_charts "https://github.com/dipto0321/spendrift-fe/blob/main/src/components/ui/chart.tsx"
click node_theme "https://github.com/dipto0321/spendrift-fe/blob/main/public/theme-init.js"
click node_env "https://github.com/dipto0321/spendrift-fe/blob/main/src/env.ts"
click node_i18n "https://github.com/dipto0321/spendrift-fe/blob/main/messages/en.json"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_root,node_router toneBlue
class node_trackers,node_auth,node_expenses,node_budgets,node_dashboard,node_reports,node_featuredata,node_featuredomain toneAmber
class node_query,node_devtools,node_http,node_tokens,node_ui,node_appui,node_charts toneMint
class node_theme,node_env,node_i18n,node_backend,node_sentry toneRose
```

## Components

### App Shell

Routing, providers, and bootstrapping

- [Root shell](https://github.com/dipto0321/spendrift-fe/blob/main/src/routes/__root.tsx): TanStack Start app shell · Composes providers, layout, and app-wide gating · `src/routes/__root.tsx`
- [Routes](https://github.com/dipto0321/spendrift-fe/blob/main/src/routes/index.tsx): file-based routing · Maps major screens to feature pages · `src/routes/index.tsx`

### Features

Domain-oriented vertical slices

- [Trackers](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/trackers/presentation/TrackerContext.tsx): workspace context · Carries the active tracker across the app · `src/features/trackers/presentation/TrackerContext.tsx`
- [Auth](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/auth/presentation/SignInPage.tsx): identity feature · Sign-in, sign-up, profile, and route guards · `src/features/auth/presentation/SignInPage.tsx`
- [Expenses](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/expenses/presentation/ExpensePage.tsx): spending feature · Transactions, categories, and expense management · `src/features/expenses/presentation/ExpensePage.tsx`
- [Budgets](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/budgets/presentation/BudgetPage.tsx): budget feature · Limits, status, and savings-health logic · `src/features/budgets/presentation/BudgetPage.tsx`
- [Dashboard](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/dashboard/presentation/DashboardPage.tsx): overview feature · Current-month summary and recent activity · `src/features/dashboard/presentation/DashboardPage.tsx`
- [Reports](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/reports/presentation/ReportsPage.tsx): analytics feature · Historical charts and comparisons · `src/features/reports/presentation/ReportsPage.tsx`
- [Feature data](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/expenses/data/repository.ts): repository + dto layer · Maps API contracts to domain models and query keys · `src/features/expenses/data/repository.ts`
- [Feature domain](https://github.com/dipto0321/spendrift-fe/blob/main/src/features/budgets/domain/services.ts): business rules · Schemas, pure services, and typed models · `src/features/budgets/domain/services.ts`

### Shared Core

Cross-cutting client, UI, and utilities

- [Query provider](https://github.com/dipto0321/spendrift-fe/blob/main/src/integrations/tanstack-query/root-provider.tsx): TanStack Query · Cache and server-state coordination · `src/integrations/tanstack-query/root-provider.tsx`
- [Query devtools](https://github.com/dipto0321/spendrift-fe/blob/main/src/integrations/tanstack-query/devtools.tsx): debug tooling · Inspects client cache and requests · `src/integrations/tanstack-query/devtools.tsx`
- [API client](https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/api/client.ts): HTTP client · Single fetch boundary with token refresh · `src/shared/api/client.ts`
- [Token store](https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/api/tokens.ts): localStorage auth · Holds access and refresh tokens · `src/shared/api/tokens.ts`
- [UI primitives](https://github.com/dipto0321/spendrift-fe/blob/main/src/components/ui/button.tsx): ShadCN/Radix · Reusable low-level controls · `src/components/ui/button.tsx`
- [App UI](https://github.com/dipto0321/spendrift-fe/blob/main/src/shared/ui/PageHeader.tsx): shared components · Sidebar, headers, stat cards, and theme toggle · `src/shared/ui/PageHeader.tsx`
- [Charts](https://github.com/dipto0321/spendrift-fe/blob/main/src/components/ui/chart.tsx): Recharts wrappers · Reusable chart components for analytics screens · `src/components/ui/chart.tsx`

### Platform

Supporting infrastructure and external services

- [Theme init](https://github.com/dipto0321/spendrift-fe/blob/main/public/theme-init.js): bootstrap script · Sets theme before React hydration · `public/theme-init.js`
- [Env config](https://github.com/dipto0321/spendrift-fe/blob/main/src/env.ts): runtime config · API base URL and observability settings · `src/env.ts`
- [Translations](https://github.com/dipto0321/spendrift-fe/blob/main/messages/en.json): Inlang messages · Locale resources and project config · `messages/en.json`
- **Spendrift API**: external backend · FastAPI service providing business data
- **Sentry**: observability service · Error and performance monitoring

## Connections

- Root shell → Query provider: provides · Sets up TanStack Query and app providers
- Root shell → Routes: hosts · Renders file-based routes inside the app shell
- Root shell → Trackers: gates · Initializes workspace context for the active tracker
- Routes → Auth: loads · Routes to auth screens and profile page
- Routes → Expenses: loads · Routes to expense management screens
- Routes → Budgets: loads · Routes to budget screens
- Routes → Dashboard: loads · Routes to the overview dashboard
- Routes → Reports: loads · Routes to analytics screens
- Auth → API client: uses · Repositories call the shared HTTP client
- Expenses → Feature data: queries · Presentation hooks depend on feature repositories
- Budgets → Feature data: queries · Budget screens consume repository-backed hooks
- Dashboard → Feature data: queries · Dashboard hooks fetch aggregated data
- Reports → Feature data: queries · Report views fetch analytics data
- Feature data → Feature domain: maps to · Repositories adapt DTOs into domain models
- Feature domain → API client: depends on · Domain-facing repositories still read through the API client
- API client → Token store: reads/writes · Uses localStorage tokens for auth and refresh
- API client → Spendrift API: requests · Single transport boundary to the FastAPI API
- API client → Sentry: reports · Surface client-side errors and failures
- Trackers → Feature data: scopes · Active tracker influences repository queries
- Trackers → Dashboard: filters · Dashboard data is bound to the selected tracker
- Trackers → Expenses: filters · Expense operations are tracker-scoped
- Trackers → Budgets: filters · Budget calculations depend on tracker context
- Trackers → Reports: filters · Reports aggregate data per tracker
- UI primitives → App UI: supports · Feature screens are built from shared primitives and chrome
- App UI → Dashboard: composes · Dashboard and other pages use shared layout components
- App UI → Expenses: composes · Expense screens use shared page chrome
- App UI → Budgets: composes · Budget screens use shared page chrome
- App UI → Reports: composes · Report screens use shared page chrome
- Charts → Dashboard: renders · Dashboard cards embed charts and trend visualizations
- Charts → Reports: renders · Reports rely on chart visualizations
- Theme init → Root shell: boots · Pre-hydration theme selection
- Env config → API client: configures · Provides API base URL and runtime settings
- Translations → Root shell: localizes · Message catalog supports app bootstrapping
- Query provider → Feature data: caches · Feature repositories expose query keys and cached requests
- Query devtools → Query provider: inspects · Provides cache visibility during development
