Skip to content

Architecture

Ferran Buireu edited this page Oct 17, 2026 · 16 revisions

Architecture

Both the web and mobile apps follow the same DDD-ish layered architecture. The -ish is deliberate and defined: the layers, the vocabulary, the ports and the typed failures are non-negotiable, while the tactical patterns are applied only where they pay, so the code does not carry abstractions nothing reads. ADR 0025 states the test and the calls it produced. The dependency direction is strict: domain knows nothing about anything else; everything points inward toward it. The web's ui may import the domain alone, so a component that needs a use case's result gets it from the page; the app's ui also reaches application, and infrastructure only through the composition root in ui/di/. The web's pages is its composition root and may import every layer.

---
config:
  look: handDrawn
  layout: dagre
---
flowchart RL
    application --> domain
    infrastructure --> domain
    ui --> domain
    ui -->|"app only"| application
    ui -->|"app only, through ui/di"| infrastructure
    pages["pages, web only"] --> ui
    pages --> application
    pages --> infrastructure
    pages --> domain
Loading

The rules the code is reviewed against are in CODING_STANDARDS.md; each layer's colocated AGENTS.md holds what an implementer working there needs.


Layers (web)

Layer Role
domain Pure business core: value objects, entities, failures, geometry. No Astro, no Cloudflare, no fetch.
application Curried use cases that orchestrate the domain, plus Failure → HTTP mapping.
infrastructure GitHub HTML scraping, the SVG string renderer, logging. Implements domain interfaces.
ui Astro components, client interactivity, styles.
pages Routes, the only layer that wires everything together (the composition root).

Where the detail lives

This page is the shape, not the rules. The rules are in CODING_STANDARDS.md and each layer's guide sits next to its code; the docs-consistency test holds both to the tree, and nothing would check a copy here.

Question Guide
What is a value object here, and how does each one fail? web/src/domain/AGENTS.md · app/lib/domain/AGENTS.md
What use cases are there, and what maps a Failure to a status? web/src/application/AGENTS.md · app/lib/application/AGENTS.md
How is GitHub scraped, and how is the SVG drawn? web/src/infrastructure/AGENTS.md · app/lib/infrastructure/AGENTS.md
What does each error mean to a caller? API Reference · Troubleshooting
Why are the layers this shape at all? ADR 0003

Two of those rules shape every layer in both clients: repositories are interfaces in domain/ that infrastructure/ implements, and failures are a sealed set matched without a wildcard, which the web returns as values and the app throws (ADR 0004).


Shared design tokens

Palettes, shapes, and suggested usernames are defined once in shared/*.json and consumed by both apps. The web imports them via the @shared alias at build time; the Flutter app bundles generated copies under app/assets/. See Project Structure.


See also

Clone this wiki locally