Files
court-pilot/docs/architecture.md
T

5.0 KiB

Architecture

This document describes behavior confirmed from the current repository. Proposed changes should be labeled as proposals rather than added here as existing behavior.

System overview

Court Pilot is a local-first Angular/Ionic application packaged as a Tauri desktop application. There is no backend API in the repository. Domain data is persisted in IndexedDB through Dexie.

flowchart TD
    Pages[Angular and Ionic pages] --> Services[Domain and persistence services]
    Pages --> I18n[Translation service and pipe]
    Services --> Dexie[Dexie]
    Dexie --> IndexedDB[IndexedDB in browser or webview]
    Tauri[Tauri desktop shell] --> WebApp[Built Angular application]
    Tauri --> Updates[OS, process, and updater plugins]
    WebApp --> Pages

Frontend structure

The root route lazy-loads the tab routes. The three primary routes are:

Route Source Responsibility
/tabs/pilot src/app/tab1/ Waiting queues, court assignments, drag and drop, and rotation actions
/tabs/umpires src/app/tab2/ Court-official roster management
/tabs/settings src/app/tab3/ Court count, service-judge mode, alerts, and language

Pages are standalone Angular components and directly import the Ionic standalone components they use. Services are root-provided and injected with Angular's inject() function.

Reactive state

Persistence services use Dexie's liveQuery() and Angular's toSignal() to expose reactive data. Pages derive lookup maps, ID sets, filtered lists, and other display state with computed().

Tab1Page currently coordinates multi-service operations such as moving officials between waiting queues and courts. These operations span multiple Dexie tables but are not currently wrapped in database transactions.

Persistence model

db.ts declares a CourtPilot Dexie database at schema version 1.

erDiagram
    UMPIRE ||--o{ COURT_UMPIRE : assigned_as
    UMPIRE ||--o{ COURT_SERVICE_JUDGE : assigned_as
    UMPIRE ||--o{ WAITING_UMPIRE : queued_as
    UMPIRE ||--o{ WAITING_SERVICE_JUDGE : queued_as

    UMPIRE {
        number id PK
        string firstName
        string lastName
        string country
        string gender
        number courtNo
    }
    COURT_UMPIRE {
        number id PK
        number umpireId
        number courtNo
    }
    COURT_SERVICE_JUDGE {
        number id PK
        number umpireId
        number courtNo
    }
    WAITING_UMPIRE {
        number id PK
        number umpireId
        number order
    }
    WAITING_SERVICE_JUDGE {
        number id PK
        number serviceJudgeId
        number order
    }

Settings use a singleton row with ID 1. Defaults are five courts, service judges enabled, and confirmation alerts enabled.

Schema-change policy

Once data may exist in released clients, do not modify schema version 1 in place. Add a new db.version(n) declaration and define upgrade behavior when existing records need transformation.

Localization

src/app/i18n/translations.ts defines the translation catalog and derives TypeScript types from it. English is the fallback language; Hungarian is also supported. The selected language is stored in localStorage under language.

Templates use TranslatePipe. Imperative UI such as Ionic alerts uses TranslationService.

Desktop integration

The Tauri shell loads the Angular build from www/. Its Rust layer registers:

  • OS plugin
  • process plugin
  • updater plugin
  • debug-only logging plugin

At application startup, the Angular root component checks for desktop updates when running under Tauri. It skips the in-app updater on Linux because Snap manages Linux updates.

Windows and macOS updater artifacts are distributed through CrabNebula. Linux is packaged and published as a strict-confinement Snap.

Supported platforms

The supported targets are browser/web, Windows, macOS, and Linux Snap. Android and iOS are not supported or planned at this stage. capacitor.config.ts remains as possible future scaffolding, but agents must not treat mobile builds as active.

Tauri development intentionally expects the Ionic frontend on port 8103. The command used by beforeDevCommand must explicitly or otherwise reliably start the frontend on that port.

Domain invariants affecting architecture

Each court official must be represented in exactly one operational state: not on duty, one waiting queue, or one court assignment. The two role queues are FIFO by numeric order. When service judges rotate off a court, the officials swap role queues. Reducing the court count removes assignments from eliminated courts and leaves those officials not on duty.

Operations that move an official between states should preserve exclusivity even when multiple Dexie tables are involved.

Known architectural questions

  • Should moves spanning multiple tables use Dexie transactions to prevent partial updates?
  • Should domain entities and database setup remain in root-level db.ts, or move under src/app/?