# lbragg98/edhapp architecture

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

- Interactive diagram: https://gitdiagram.com/lbragg98/edhapp
- Repository: https://github.com/lbragg98/edhapp

## Overview

Command Tower is a Commander-focused Magic companion: users browse cards, manage a personal collection, build and review decks, and can scan cards into their library. The main flows pass through Next.js pages and API routes into domain services and persistence; deck services also provide validation, analytics, intelligence, playtesting, and upgrades. The tree documents additional tracker, pricing, printable playtest, and background-job capabilities. Some connections are based on README or source-index references; unsampled implementations are represented conservatively.

Main entry points: [`src/modules/deck/application/deck-service.ts`](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/application/deck-service.ts) (Deck service), [`src/modules/library/infrastructure/prisma-library-repository.ts`](https://github.com/lbragg98/edhapp/blob/main/src/modules/library/infrastructure/prisma-library-repository.ts) (Collection repository), [`src/app/(protected)/decks/page.tsx`](https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/decks/page.tsx) (Deck workspace), [`src/modules/deck/infrastructure/prisma-deck-repository.ts`](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/infrastructure/prisma-deck-repository.ts) (Deck repository)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_experience["User experience"]
  node_card_ui["Card browser<br/>[page.tsx]"]
  node_card_api["Card search API<br/>[route.ts]"]
  node_library_ui["Collection workspace<br/>[page.tsx]"]
  node_library_api["Collection API<br/>[route.ts]"]
  node_deck_ui["Deck workspace<br/>[page.tsx]"]
  node_deck_api["Deck API<br/>[route.ts]"]
  node_scanner_ui["Scanner workspace<br/>[page.tsx]"]
  node_scanner_api["Scan API<br/>[route.ts]"]
  node_scanner_confirm["Scan confirmation<br/>[route.ts]"]
  node_tracker["Life tracker<br/>[page.tsx]"]
end

subgraph group_cards_library["Cards and collection"]
  node_catalog["Card catalog<br/>[search-cards.ts]"]
  node_scryfall_catalog["Scryfall catalog"]
  node_library_service["Collection services"]
  node_library_repo["Collection repository"]
end

subgraph group_deck["Deckbuilding"]
  node_deck_service["Deck service<br/>[deck-service.ts]"]
  node_deck_rules["Deck analysis"]
  node_deck_validation["Commander validation"]
  node_deck_repo["Deck repository"]
end

subgraph group_scanner["Card scanning"]
  node_scanner_pipeline["Scan pipeline"]
  node_ocr["OCR providers<br/>[providers.ts]"]
  node_candidate_match["Candidate matching"]
end

subgraph group_platform["Supporting capabilities"]
  node_database[("Postgres")]
  node_pricing["Price snapshots"]
  node_playtest_pdf["Playtest PDF"]
  node_jobs["Refresh job worker<br/>[run-job-worker.ts]"]
  node_scryfall["Scryfall"]
end

node_user(("Player"))

node_user -->|"browses"| node_card_ui
node_card_ui -.->|"searches"| node_card_api
node_card_api -->|"queries"| node_catalog
node_catalog -.->|"uses"| node_scryfall_catalog
node_user -->|"manages collection"| node_library_ui
node_library_ui -.->|"updates"| node_library_api
node_library_api -->|"calls"| node_library_service
node_library_repo -->|"fetches card data"| node_scryfall
node_library_repo -->|"reads and writes"| node_database
node_user -->|"builds decks"| node_deck_ui
node_deck_ui -.->|"edits"| node_deck_api
node_deck_api -->|"calls"| node_deck_service
node_deck_service -->|"persists decks"| node_deck_repo
node_deck_service -->|"analyzes composition"| node_deck_rules
node_deck_service -->|"validates"| node_deck_validation
node_deck_repo -->|"reads and writes"| node_database
node_user -.->|"scans cards"| node_scanner_ui
node_scanner_ui -.->|"submits scan"| node_scanner_api
node_scanner_api -.->|"runs scan"| node_scanner_pipeline
node_scanner_pipeline -.->|"recognizes text"| node_ocr
node_scanner_pipeline -->|"resolves candidate"| node_candidate_match
node_scanner_confirm -->|"imports holding"| node_library_service
node_user -.->|"tracks game"| node_tracker
node_deck_ui -.->|"exports playtest"| node_playtest_pdf
node_jobs -->|"loads jobs"| node_database
node_library_repo -->|"parses prices"| node_pricing
node_deck_repo -->|"parses prices"| node_pricing

click node_card_ui "https://github.com/lbragg98/edhapp/blob/main/src/app/cards/page.tsx"
click node_card_api "https://github.com/lbragg98/edhapp/blob/main/src/app/api/cards/route.ts"
click node_catalog "https://github.com/lbragg98/edhapp/blob/main/src/modules/catalog/application/search-cards.ts"
click node_scryfall_catalog "https://github.com/lbragg98/edhapp/blob/main/src/modules/catalog/infrastructure/scryfall/scryfall-card-catalog-repository.ts"
click node_library_ui "https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/library/page.tsx"
click node_library_api "https://github.com/lbragg98/edhapp/blob/main/src/app/api/library/route.ts"
click node_library_service "https://github.com/lbragg98/edhapp/blob/main/src/modules/library/application/library-services.ts"
click node_library_repo "https://github.com/lbragg98/edhapp/blob/main/src/modules/library/infrastructure/prisma-library-repository.ts"
click node_deck_ui "https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/decks/page.tsx"
click node_deck_api "https://github.com/lbragg98/edhapp/blob/main/src/app/api/decks/route.ts"
click node_deck_service "https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/application/deck-service.ts"
click node_deck_rules "https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/domain/analyze-deck-composition.ts"
click node_deck_validation "https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/domain/validate-commander-deck.ts"
click node_deck_repo "https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/infrastructure/prisma-deck-repository.ts"
click node_scanner_ui "https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/scanner/page.tsx"
click node_scanner_api "https://github.com/lbragg98/edhapp/blob/main/src/app/api/scanner/scan/route.ts"
click node_scanner_pipeline "https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/application/scanner-pipeline-service.ts"
click node_ocr "https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/ocr/providers.ts"
click node_candidate_match "https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/recognition/resolve-card-candidate.ts"
click node_scanner_confirm "https://github.com/lbragg98/edhapp/blob/main/src/app/api/scanner/confirm/route.ts"
click node_tracker "https://github.com/lbragg98/edhapp/blob/main/src/app/tracker/page.tsx"
click node_pricing "https://github.com/lbragg98/edhapp/blob/main/src/modules/pricing/infrastructure/prisma-price-snapshot.ts"
click node_playtest_pdf "https://github.com/lbragg98/edhapp/blob/main/src/modules/playtest-pdf/application/generate-deck-playtest-pdf.ts"
click node_jobs "https://github.com/lbragg98/edhapp/blob/main/src/server/jobs/infrastructure/run-job-worker.ts"

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_card_ui,node_card_api,node_library_ui,node_library_api,node_deck_ui,node_deck_api,node_scanner_ui,node_scanner_api,node_scanner_confirm,node_tracker toneBlue
class node_catalog,node_scryfall_catalog,node_library_service,node_library_repo toneAmber
class node_deck_service,node_deck_rules,node_deck_validation,node_deck_repo toneMint
class node_scanner_pipeline,node_ocr,node_candidate_match toneRose
class node_database,node_pricing,node_playtest_pdf,node_jobs,node_scryfall,node_user toneIndigo
```

## Components

### User experience

- [Card browser](https://github.com/lbragg98/edhapp/blob/main/src/app/cards/page.tsx): `src/app/cards/page.tsx`
- [Card search API](https://github.com/lbragg98/edhapp/blob/main/src/app/api/cards/route.ts): `src/app/api/cards/route.ts`
- [Collection workspace](https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/library/page.tsx): `src/app/(protected)/library/page.tsx`
- [Collection API](https://github.com/lbragg98/edhapp/blob/main/src/app/api/library/route.ts): `src/app/api/library/route.ts`
- [Deck workspace](https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/decks/page.tsx): `src/app/(protected)/decks/page.tsx`
- [Deck API](https://github.com/lbragg98/edhapp/blob/main/src/app/api/decks/route.ts): `src/app/api/decks/route.ts`
- [Scanner workspace](https://github.com/lbragg98/edhapp/blob/main/src/app/(protected)/scanner/page.tsx): `src/app/(protected)/scanner/page.tsx`
- [Scan API](https://github.com/lbragg98/edhapp/blob/main/src/app/api/scanner/scan/route.ts): `src/app/api/scanner/scan/route.ts`
- [Scan confirmation](https://github.com/lbragg98/edhapp/blob/main/src/app/api/scanner/confirm/route.ts): `src/app/api/scanner/confirm/route.ts`
- [Life tracker](https://github.com/lbragg98/edhapp/blob/main/src/app/tracker/page.tsx): `src/app/tracker/page.tsx`

### Cards and collection

- [Card catalog](https://github.com/lbragg98/edhapp/blob/main/src/modules/catalog/application/search-cards.ts): `src/modules/catalog/application/search-cards.ts`
- [Scryfall catalog](https://github.com/lbragg98/edhapp/blob/main/src/modules/catalog/infrastructure/scryfall/scryfall-card-catalog-repository.ts): `src/modules/catalog/infrastructure/scryfall/scryfall-card-catalog-repository.ts`
- [Collection services](https://github.com/lbragg98/edhapp/blob/main/src/modules/library/application/library-services.ts): `src/modules/library/application/library-services.ts`
- [Collection repository](https://github.com/lbragg98/edhapp/blob/main/src/modules/library/infrastructure/prisma-library-repository.ts): `src/modules/library/infrastructure/prisma-library-repository.ts`

### Deckbuilding

- [Deck service](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/application/deck-service.ts): `src/modules/deck/application/deck-service.ts`
- [Deck analysis](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/domain/analyze-deck-composition.ts): `src/modules/deck/domain/analyze-deck-composition.ts`
- [Commander validation](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/domain/validate-commander-deck.ts): `src/modules/deck/domain/validate-commander-deck.ts`
- [Deck repository](https://github.com/lbragg98/edhapp/blob/main/src/modules/deck/infrastructure/prisma-deck-repository.ts): `src/modules/deck/infrastructure/prisma-deck-repository.ts`

### Card scanning

- [Scan pipeline](https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/application/scanner-pipeline-service.ts): `src/modules/scanner/application/scanner-pipeline-service.ts`
- [OCR providers](https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/ocr/providers.ts): `src/modules/scanner/ocr/providers.ts`
- [Candidate matching](https://github.com/lbragg98/edhapp/blob/main/src/modules/scanner/recognition/resolve-card-candidate.ts): `src/modules/scanner/recognition/resolve-card-candidate.ts`

### Supporting capabilities

- **Postgres**
- [Price snapshots](https://github.com/lbragg98/edhapp/blob/main/src/modules/pricing/infrastructure/prisma-price-snapshot.ts): `src/modules/pricing/infrastructure/prisma-price-snapshot.ts`
- [Playtest PDF](https://github.com/lbragg98/edhapp/blob/main/src/modules/playtest-pdf/application/generate-deck-playtest-pdf.ts): `src/modules/playtest-pdf/application/generate-deck-playtest-pdf.ts`
- [Refresh job worker](https://github.com/lbragg98/edhapp/blob/main/src/server/jobs/infrastructure/run-job-worker.ts): `src/server/jobs/infrastructure/run-job-worker.ts`
- **Scryfall**

### Other components

- **Player**

## Connections

- Player → Card browser: browses
- Card browser → Card search API: searches
- Card search API → Card catalog: queries (evidence: `src/app/api/cards/route.ts`)
- Card catalog → Scryfall catalog: uses
- Player → Collection workspace: manages collection
- Collection workspace → Collection API: updates
- Collection API → Collection services: calls (evidence: `src/app/api/library/route.ts`)
- Collection repository → Scryfall: fetches card data (evidence: `src/modules/library/infrastructure/prisma-library-repository.ts`)
- Collection repository → Postgres: reads and writes (evidence: `src/modules/library/infrastructure/prisma-library-repository.ts`)
- Player → Deck workspace: builds decks
- Deck workspace → Deck API: edits
- Deck API → Deck service: calls (evidence: `src/app/api/decks/route.ts`)
- Deck service → Deck repository: persists decks (evidence: `src/modules/deck/application/deck-service.ts`)
- Deck service → Deck analysis: analyzes composition (evidence: `src/modules/deck/application/deck-service.ts`)
- Deck service → Commander validation: validates (evidence: `src/modules/deck/application/deck-service.ts`)
- Deck repository → Postgres: reads and writes (evidence: `src/modules/deck/infrastructure/prisma-deck-repository.ts`)
- Player → Scanner workspace: scans cards
- Scanner workspace → Scan API: submits scan
- Scan API → Scan pipeline: runs scan
- Scan pipeline → OCR providers: recognizes text
- Scan pipeline → Candidate matching: resolves candidate (evidence: `src/modules/scanner/application/scanner-pipeline-service.ts`)
- Scan confirmation → Collection services: imports holding (evidence: `src/app/api/scanner/confirm/route.ts`)
- Player → Life tracker: tracks game
- Deck workspace → Playtest PDF: exports playtest
- Refresh job worker → Postgres: loads jobs (evidence: `src/server/jobs/infrastructure/run-job-worker.ts`)
- Collection repository → Price snapshots: parses prices (evidence: `src/modules/library/infrastructure/prisma-library-repository.ts`)
- Deck repository → Price snapshots: parses prices (evidence: `src/modules/deck/infrastructure/prisma-deck-repository.ts`)
