# dipto0321/spendrift-be architecture

> Architecture diagram of the GitHub repository dipto0321/spendrift-be, 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-be
- Repository: https://github.com/dipto0321/spendrift-be

## Overview

1. **What this repository is**
   - Spendrift is a **backend-only personal finance API** built around **multiple independent “Trackers”** per user.
   - A tracker is the main workspace boundary: expenses, categories, budgets, dashboards, and reports are all scoped to a specific tracker.

2. **Request entrypoint and API surface**
   - The app is a **FastAPI** service with a versioned API under **`/api/v1`**.
   - `app/main.py` is the runtime entrypoint, while `app/api/v1/api.py` aggregates the module routers into the public API surface.

3. **Layered architecture inside each feature**
   - Most business areas follow the same vertical slice pattern:
     - **router**: HTTP endpoints and request/response wiring
     - **service**: business rules and orchestration
     - **repo**: database access
     - **model**: SQLModel persistence objects
     - **schema**: Pydantic request/response shapes
   - This pattern is repeated across auth, users, trackers, categories, expenses, budgets, refresh tokens, dashboard, and reports.

4. **Core/shared infrastructure**
   - `app/core/` contains shared platform concerns:
     - `config.py` for environment-driven settings
     - `database.py` for database session/engine setup
     - `security.py` for password hashing and token handling
     - `logging_config.py` for application logging
     - `storage/` for object storage abstraction
   - `app/middleware/rate_limit.py` adds API throttling via middleware.

5. **Authentication and session management**
   - Auth is based on **Argon2 password hashing** and **JWT** tokens.
   - The `auth` module handles registration/login, while `refresh_tokens` persists refresh token state separately, indicating token rotation/revocation is part of the architecture.
   - The README’s sample responses confirm the API issues both **access_token** and **refresh_token** on login/register.

6. **Domain boundaries and data ownership**
   - **Users** manage profile data such as name, email, activity status, and avatar URL.
   - **Trackers** represent isolated finance workspaces and are the root for all financial data.
   - **Categories**, **Expenses**, and **Budgets** are subordinate to a tracker, which is the key multi-tenant-like boundary in the system.
   - **Dashboard** and **Reports** are read-focused modules that aggregate tracker data rather than owning primary records.

7. **Tracker lifecycle and seeded defaults**
   - Creating a tracker triggers automatic setup of default categories; the README shows 10 seeded categories.
   - That implies tracker creation is not just a CRUD operation, but an orchestration step that initializes dependent data.

8. **Persistence and migrations**
   - The primary database is **PostgreSQL 18**, accessed via **SQLModel**.
   - **Alembic** manages schema evolution, with migrations covering tracker/category/budget/expense changes, refresh tokens, avatars, and timezone-aware timestamps.
   - The repository is designed so schema changes are explicit and versioned, which is important for diagramming the persistence boundary.

9. **Storage beyond the database**
   - `app/core/storage/` includes a storage abstraction plus an S3 implementation, which is relevant for user avatars.
   - The repo also contains migrations for avatar-related tables/fields, so file/object storage and DB metadata are part of the user-profile subsystem.

10. **Cross-cutting runtime and deployment concerns**
   - Local development uses **Docker** and `docker-compose.yml` for PostgreSQL.
   - The project is configured for Python 3.12+ and uses `uv` / `pyproject.toml` for dependency and tool management.
   - CI is present in `.github/workflows/ci.yml`, and the Makefile exposes the main lifecycle commands: run, migrate, upgrade, test, and lint.

11. **Testing structure**
   - The test suite mirrors the domain modules: auth, budgets, categories, dashboard, expenses, reports, trackers, and users.
   - `tests/conftest.py` suggests shared fixtures for app/database setup, reinforcing that the architecture is integration-test friendly around the API and persistence layers.

12. **How to draw the architecture**
   - A useful diagram would show:
     - **Client → FastAPI app → versioned routers**
     - routers delegating to **service layer**
     - services using **repositories/models**
     - repositories reading/writing **PostgreSQL**
     - auth flowing through **JWT/refresh-token storage**
     - avatar uploads flowing through **storage abstraction / S3**
     - tracker-scoped business modules feeding **dashboard/report aggregation**

Main entry points: [`app/main.py`](https://github.com/dipto0321/spendrift-be/blob/main/app/main.py) (App Main), [`app/api/v1/api.py`](https://github.com/dipto0321/spendrift-be/blob/main/app/api/v1/api.py) (V1 API), [`modules/trackers/service.py`](https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/service.py) (Trackers Service), [`modules/dashboard/service.py`](https://github.com/dipto0321/spendrift-be/blob/main/modules/dashboard/service.py) (Dashboard Service)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_runtime["Runtime"]
  node_api_client(("API Client<br/>caller"))
  node_app_main["App Main<br/>fastapi entrypoint<br/>[main.py]"]
  node_api_v1["V1 API<br/>router aggregate<br/>[api.py]"]
end

subgraph group_core["Core"]
  node_rate_limit["Rate Limit<br/>middleware<br/>[rate_limit.py]"]
  node_core_config["Config<br/>settings<br/>[config.py]"]
  node_core_db[("Database<br/>sqlmodel engine<br/>[database.py]")]
  node_core_security["Security<br/>auth utils<br/>[security.py]"]
  node_storage_base["Storage Base<br/>storage abstraction<br/>[base.py]"]
  node_storage_s3["S3 Storage<br/>object storage<br/>[s3.py]"]
end

subgraph group_auth["Auth & Session"]
  node_auth_router["Auth Router<br/>http router<br/>[router.py]"]
  node_auth_service["Auth Service<br/>[service.py]"]
  node_refresh_repo[("Refresh Tokens<br/>token repo<br/>[repo.py]")]
  node_users_router["Users Router<br/>http router<br/>[router.py]"]
  node_users_service["Users Service<br/>[service.py]"]
  node_users_repo[("Users Repo<br/>[repo.py]")]
end

subgraph group_finance["Finance Domain"]
  node_trackers_router["Trackers Router<br/>http router<br/>[router.py]"]
  node_trackers_service["Trackers Service<br/>[service.py]"]
  node_trackers_repo[("Trackers Repo<br/>[repo.py]")]
  node_categories_service["Categories Service<br/>[service.py]"]
  node_expenses_service["Expenses Service<br/>[service.py]"]
  node_budgets_service["Budgets Service<br/>[service.py]"]
end

subgraph group_insights["Insights"]
  node_dashboard_service["Dashboard Service<br/>aggregator<br/>[service.py]"]
  node_reports_service["Reports Service<br/>aggregator<br/>[service.py]"]
end

subgraph group_persistence["Persistence"]
  node_postgres[("PostgreSQL<br/>database")]
end

node_api_client -->|"HTTP"| node_app_main
node_app_main -->|"mounts"| node_api_v1
node_app_main -->|"uses"| node_rate_limit
node_app_main -->|"loads"| node_core_config
node_app_main -->|"initializes"| node_core_db
node_api_v1 -->|"includes"| node_auth_router
node_api_v1 -->|"includes"| node_users_router
node_api_v1 -->|"includes"| node_trackers_router
node_auth_router -->|"calls"| node_auth_service
node_auth_service -->|"uses"| node_core_security
node_auth_service -->|"stores"| node_refresh_repo
node_refresh_repo -->|"reads/writes"| node_postgres
node_users_router -->|"calls"| node_users_service
node_users_service -->|"uses"| node_users_repo
node_users_service -->|"uploads"| node_storage_base
node_storage_s3 -->|"implements"| node_storage_base
node_users_repo -->|"reads/writes"| node_postgres
node_trackers_router -->|"calls"| node_trackers_service
node_trackers_service -->|"uses"| node_trackers_repo
node_trackers_service -->|"seeds"| node_categories_service
node_trackers_repo -->|"reads/writes"| node_postgres
node_categories_service -->|"reads/writes"| node_postgres
node_expenses_service -->|"reads/writes"| node_postgres
node_budgets_service -->|"reads/writes"| node_postgres
node_dashboard_service -->|"aggregates"| node_expenses_service
node_dashboard_service -->|"aggregates"| node_budgets_service
node_dashboard_service -->|"aggregates"| node_categories_service
node_reports_service -->|"analyzes"| node_expenses_service
node_reports_service -->|"scopes"| node_trackers_service
node_core_db -->|"connects"| node_postgres

click node_app_main "https://github.com/dipto0321/spendrift-be/blob/main/app/main.py"
click node_api_v1 "https://github.com/dipto0321/spendrift-be/blob/main/app/api/v1/api.py"
click node_rate_limit "https://github.com/dipto0321/spendrift-be/blob/main/app/middleware/rate_limit.py"
click node_core_config "https://github.com/dipto0321/spendrift-be/blob/main/app/core/config.py"
click node_core_db "https://github.com/dipto0321/spendrift-be/blob/main/app/core/database.py"
click node_core_security "https://github.com/dipto0321/spendrift-be/blob/main/app/core/security.py"
click node_storage_base "https://github.com/dipto0321/spendrift-be/blob/main/app/core/storage/base.py"
click node_storage_s3 "https://github.com/dipto0321/spendrift-be/blob/main/app/core/storage/s3.py"
click node_auth_router "https://github.com/dipto0321/spendrift-be/blob/main/modules/auth/router.py"
click node_auth_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/auth/service.py"
click node_refresh_repo "https://github.com/dipto0321/spendrift-be/blob/main/modules/refresh_tokens/repo.py"
click node_users_router "https://github.com/dipto0321/spendrift-be/blob/main/modules/users/router.py"
click node_users_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/users/service.py"
click node_users_repo "https://github.com/dipto0321/spendrift-be/blob/main/modules/users/repo.py"
click node_trackers_router "https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/router.py"
click node_trackers_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/service.py"
click node_trackers_repo "https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/repo.py"
click node_categories_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/categories/service.py"
click node_expenses_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/expenses/service.py"
click node_budgets_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/budgets/service.py"
click node_dashboard_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/dashboard/service.py"
click node_reports_service "https://github.com/dipto0321/spendrift-be/blob/main/modules/reports/service.py"

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_api_client,node_app_main,node_api_v1 toneBlue
class node_rate_limit,node_core_config,node_core_db,node_core_security,node_storage_base,node_storage_s3 toneAmber
class node_auth_router,node_auth_service,node_refresh_repo,node_users_router,node_users_service,node_users_repo toneMint
class node_trackers_router,node_trackers_service,node_trackers_repo,node_categories_service,node_expenses_service,node_budgets_service toneRose
class node_dashboard_service,node_reports_service toneIndigo
class node_postgres toneTeal
```

## Components

### Runtime

Process entrypoint and HTTP surface

- **API Client**: caller · External consumer of the REST API
- [App Main](https://github.com/dipto0321/spendrift-be/blob/main/app/main.py): fastapi entrypoint · Runtime entrypoint that boots the service · `app/main.py`
- [V1 API](https://github.com/dipto0321/spendrift-be/blob/main/app/api/v1/api.py): router aggregate · Collects versioned routers into the public API · `app/api/v1/api.py`

### Core

Shared platform infrastructure

- [Rate Limit](https://github.com/dipto0321/spendrift-be/blob/main/app/middleware/rate_limit.py): middleware · API throttling middleware · `app/middleware/rate_limit.py`
- [Config](https://github.com/dipto0321/spendrift-be/blob/main/app/core/config.py): settings · Environment-driven application settings · `app/core/config.py`
- [Database](https://github.com/dipto0321/spendrift-be/blob/main/app/core/database.py): sqlmodel engine · Engine and session setup for PostgreSQL · `app/core/database.py`
- [Security](https://github.com/dipto0321/spendrift-be/blob/main/app/core/security.py): auth utils · Password hashing and token handling · `app/core/security.py`
- [Storage Base](https://github.com/dipto0321/spendrift-be/blob/main/app/core/storage/base.py): storage abstraction · Object storage interface · `app/core/storage/base.py`
- [S3 Storage](https://github.com/dipto0321/spendrift-be/blob/main/app/core/storage/s3.py): object storage · S3-backed storage implementation · `app/core/storage/s3.py`

### Auth & Session

Login, tokens, and user identity

- [Auth Router](https://github.com/dipto0321/spendrift-be/blob/main/modules/auth/router.py): http router · Register and login endpoints · `modules/auth/router.py`
- [Auth Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/auth/service.py): service · Authentication and token issuance logic · `modules/auth/service.py`
- [Refresh Tokens](https://github.com/dipto0321/spendrift-be/blob/main/modules/refresh_tokens/repo.py): token repo · Persisted refresh token state · `modules/refresh_tokens/repo.py`
- [Users Router](https://github.com/dipto0321/spendrift-be/blob/main/modules/users/router.py): http router · User profile endpoints · `modules/users/router.py`
- [Users Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/users/service.py): service · Profile management and avatar updates · `modules/users/service.py`
- [Users Repo](https://github.com/dipto0321/spendrift-be/blob/main/modules/users/repo.py): repo · User persistence access · `modules/users/repo.py`

### Finance Domain

Tracker-scoped business modules

- [Trackers Router](https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/router.py): http router · Tracker workspace endpoints · `modules/trackers/router.py`
- [Trackers Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/service.py): service · Tracker lifecycle and default seeding · `modules/trackers/service.py`
- [Trackers Repo](https://github.com/dipto0321/spendrift-be/blob/main/modules/trackers/repo.py): repo · Tracker persistence access · `modules/trackers/repo.py`
- [Categories Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/categories/service.py): service · Tracker-scoped category rules · `modules/categories/service.py`
- [Expenses Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/expenses/service.py): service · Expense entry and lookup logic · `modules/expenses/service.py`
- [Budgets Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/budgets/service.py): service · Budget rules and enforcement · `modules/budgets/service.py`

### Insights

Read-focused aggregation modules

- [Dashboard Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/dashboard/service.py): aggregator · Tracker overview summaries · `modules/dashboard/service.py`
- [Reports Service](https://github.com/dipto0321/spendrift-be/blob/main/modules/reports/service.py): aggregator · Tracker report generation · `modules/reports/service.py`

### Persistence

Database and object storage

- **PostgreSQL**: database · Primary relational datastore

## Connections

- API Client → App Main: HTTP · Requests enter the FastAPI app
- App Main → V1 API: mounts · Registers the versioned API surface
- App Main → Rate Limit: uses · Applies request throttling
- App Main → Config: loads · Reads runtime settings
- App Main → Database: initializes · Creates database access layer
- V1 API → Auth Router: includes · Publishes auth endpoints
- V1 API → Users Router: includes · Publishes user endpoints
- V1 API → Trackers Router: includes · Publishes tracker endpoints
- Auth Router → Auth Service: calls · Delegates auth requests
- Auth Service → Security: uses · Hashes passwords and issues tokens
- Auth Service → Refresh Tokens: stores · Persists refresh token state
- Refresh Tokens → PostgreSQL: reads/writes · Refresh tokens live in PostgreSQL
- Users Router → Users Service: calls · Delegates profile operations
- Users Service → Users Repo: uses · Loads and updates users
- Users Service → Storage Base: uploads · Abstracts avatar storage
- S3 Storage → Storage Base: implements · S3 backend for object storage
- Users Repo → PostgreSQL: reads/writes · User records are stored in PostgreSQL
- Trackers Router → Trackers Service: calls · Delegates tracker operations
- Trackers Service → Trackers Repo: uses · Stores tracker workspaces
- Trackers Service → Categories Service: seeds · Initializes default categories on create
- Trackers Repo → PostgreSQL: reads/writes · Trackers are persisted in PostgreSQL
- Categories Service → PostgreSQL: reads/writes · Categories are tracker-scoped data
- Expenses Service → PostgreSQL: reads/writes · Expense records are stored in PostgreSQL
- Budgets Service → PostgreSQL: reads/writes · Budget records are stored in PostgreSQL
- Dashboard Service → Expenses Service: aggregates · Builds summaries from expense data
- Dashboard Service → Budgets Service: aggregates · Compares spending to budgets
- Dashboard Service → Categories Service: aggregates · Groups tracker data by category
- Reports Service → Expenses Service: analyzes · Produces reporting views from expenses
- Reports Service → Trackers Service: scopes · Reports are tracker-bound
- Database → PostgreSQL: connects · Database engine targets PostgreSQL
