> ## Documentation Index
> Fetch the complete documentation index at: https://285e39fd5e337e58f16290.sightscreen.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Layer architecture

> How the backend is structured internally — layers, dependency flow, the event-driven pipeline, and the factory-based injection pattern.

## Backend layer diagram

The backend follows a layered architecture with clear dependency boundaries. LA v2 introduces a **Providers** layer for event detection, display state building, and delivery coordination.

```mermaid theme={null}
graph TB
    subgraph Entry["Entry points"]
        Routes["Routes<br/>(HTTP handlers)"]
        Jobs["Jobs<br/>(MatchOrchestrator, ScorePoller,<br/>MarqueePoller, FixtureSyncJob)"]
    end

    subgraph Core["Core logic"]
        Services["Services<br/>(business logic, HubDataService)"]
        Providers["Providers<br/>(event pipeline, display, delivery)"]
    end

    subgraph Pipeline["Event detection pipeline (Providers)"]
        MatchEventBus["MatchEventBus"]
        Detectors["Tier0EventDetector<br/>Tier1EventDetector"]
        Filters["CooldownFilter<br/>StalenessFilter"]
        DSB["DisplayStateBuilder<br/>(builds UnifiedDisplayState)"]
        LACoordinator["LACoordinator<br/>(APNs delivery coordinator)"]
        APNS["ApnsDeliveryService"]
        LSP["LivescoreProvider"]
        LMR["LiveMatchReconciler"]
    end

    subgraph Data["Data access"]
        Repositories["Repositories<br/>(ActiveMatchRepository,<br/>LiveActivityRepository, ...)"]
        Clients["Clients<br/>(SportMonks API)"]
    end

    subgraph Storage["Storage"]
        DynamoDB["DynamoDB"]
        SportMonks["SportMonks API"]
    end

    subgraph Cross["Cross-cutting"]
        Middleware["Middleware<br/>(auth, logging)"]
        Schemas["Schemas<br/>(Zod validation)"]
        Factories["Factories<br/>(DI container)"]
    end

    Middleware --> Routes
    Routes --> Services
    Jobs --> Services
    Jobs --> Providers
    Services --> Repositories
    Services --> Providers
    Providers --> Clients
    Providers --> Repositories
    Clients --> SportMonks
    Repositories --> DynamoDB

    MatchEventBus --> Detectors
    Detectors --> Filters
    Filters --> DSB
    DSB --> LACoordinator
    LACoordinator --> APNS

    Schemas -.->|validates| Routes
    Schemas -.->|validates| Services
    Factories -.->|wires up| Services
    Factories -.->|wires up| Repositories
    Factories -.->|wires up| Providers
    Factories -.->|wires up| Clients
```

## Event-driven pipeline

The core of LA v2. Data flows through the pipeline on every poll tick:

```mermaid theme={null}
graph LR
    Pollers["ScorePoller<br/>MarqueePoller"] --> MEB["MatchEventBus"]
    MEB --> T0["Tier0EventDetector<br/>(wickets, milestones)"]
    MEB --> T1["Tier1EventDetector<br/>(score changes, overs)"]
    T0 --> CF["CooldownFilter"]
    T1 --> CF
    CF --> SF["StalenessFilter"]
    SF --> Priority["Priority assignment"]
    Priority --> LAC["LACoordinator"]
    LAC --> APNs["ApnsDeliveryService<br/>→ APNs"]
```

**Tier0 events** are high-priority (wickets, centuries, hat-tricks) and get `apns-priority: 10` for immediate delivery. **Tier1 events** are routine updates (score ticks, over completions) and get `apns-priority: 5`.

**CooldownFilter** prevents event flooding (e.g., suppressing repeated wicket alerts within a window). **StalenessFilter** drops events derived from data that has not actually changed since the last push.

## Layer responsibilities

### Routes

HTTP handlers that parse requests, validate input with Zod schemas, call the appropriate service, and return responses. Routes contain no business logic.

### Middleware

Runs before route handlers. Handles:

* **Auth** — verifies Cognito JWT tokens, extracts user context
* **Logging** — request/response logging with correlation IDs

### Services

Business logic for API-facing operations. Services orchestrate between repositories, providers, and other services. **HubDataService** builds data for the admin UI (Pavilion).

### Providers

The event detection and delivery layer. This is where the LA v2 pipeline lives:

| Provider                | Responsibility                                         |
| ----------------------- | ------------------------------------------------------ |
| **MatchEventBus**       | Routes raw poll data through the detection pipeline    |
| **LACoordinator**       | Coordinates APNs delivery with priority and batching   |
| **Tier0EventDetector**  | Detects high-priority events (wickets, milestones)     |
| **Tier1EventDetector**  | Detects routine score changes and over completions     |
| **CooldownFilter**      | Suppresses repeated events within a cooldown window    |
| **StalenessFilter**     | Drops events when underlying data has not changed      |
| **DisplayStateBuilder** | Builds UnifiedDisplayState from match data             |
| **ApnsDeliveryService** | Clean interface for sending APNs pushes                |
| **LivescoreProvider**   | Fetches and normalizes live score data from SportMonks |
| **LiveMatchReconciler** | Reconciles matches that drop off SportMonks livescores |

### Clients

Thin wrappers around external HTTP APIs. Handle request formatting, auth headers, rate limiting, and response parsing. The SportMonks client lives here, wrapped in Cockatiel resilience policies.

### Repositories

CRUD abstractions over DynamoDB tables. Each repository owns one table and exposes typed methods (`getById`, `query`, `put`, `delete`). Key repositories in LA v2:

* **ActiveMatchRepository** — owns the `active-matches` table (match processing state, previous scores, cooldowns)
* **LiveActivityRepository** — owns the `live-activities` table (LA sessions, update tokens)

### Schemas

Zod schemas for request validation, response shaping, and internal data type definitions. Shared across layers.

### Jobs

Background tasks that run on schedules or triggers:

| Job                   | Responsibility                                                                                   |
| --------------------- | ------------------------------------------------------------------------------------------------ |
| **MatchOrchestrator** | Manages full match lifecycle (replaces prematch job). Starts/stops pollers, handles transitions. |
| **ScorePoller**       | Reusable poller with Cockatiel resilience. Runs for live and replay matches.                     |
| **MarqueePoller**     | Polls the marquee league at higher frequency with ball-by-ball data.                             |
| **FixtureSyncJob**    | Syncs fixture lists from SportMonks on a schedule.                                               |

## Dependency injection via factories

<Accordion title="How factories.ts works">
  The backend uses a factory-based dependency injection pattern defined in `factories.ts`. The composition order is:

  1. **`createRepositories()`** — instantiates all repository instances (ActiveMatchRepository, LiveActivityRepository, etc.)
  2. **`createClients()`** — instantiates external API clients (SportMonks)
  3. **`createServices()`** — instantiates services with repository and client dependencies
  4. **`createProviders()`** — instantiates providers (MatchEventBus, LACoordinator, detectors, filters, DisplayStateBuilder) with service and repository dependencies

  Each factory function creates an instance and injects the required dependencies via constructor parameters. Routes and jobs receive fully-wired service and provider instances from the factories.

  This means:

  * No layer creates its own dependencies
  * Swapping implementations (e.g., for testing) only requires changing the factory
  * Circular dependencies are caught at startup, not at runtime
  * The full dependency graph is visible in one file
</Accordion>

<Warning>
  If you add a new service, repository, or provider, you must register it in `factories.ts`. The app will not pick it up automatically.
</Warning>

## Rules of thumb

* **Routes** never import repositories or clients directly
* **Services** never make HTTP calls — that is what clients and providers are for
* **Repositories** never contain business logic or call other repositories
* **Providers** own the event detection pipeline — services call providers, not the other way around
* **Jobs** always go through services or providers, never bypass them
* If you are unsure where code belongs, ask: "Does this make a decision?" If yes, it belongs in a service. "Does this detect or deliver?" If yes, it belongs in a provider.
