# tobymao/18xx architecture

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

- Interactive diagram: https://gitdiagram.com/tobymao/18xx
- Repository: https://github.com/tobymao/18xx

## Overview

#### Purpose and Entry Points
18xx.games is a Ruby application for playing many asynchronous 18xx board-game titles online. Rack starts the server through `config.ru`; HTTP/API wiring is rooted in `api.rb`, with game and user routes under `routes/game.rb` and `routes/user.rb`. The client application is Ruby-rendered through `assets/app/app.rb`.

#### Application Boundary and Persistence
The web layer manages authentication, users, sessions, game creation, and game lifecycle separately from deterministic game rules. Database access/models are in `db.rb`, `models/game.rb`, and `models/action.rb`; migrations define the PostgreSQL schema in `migrate/0001_initial.rb`. Persisted games retain action history, enabling reconstruction, undo/redo, and replay rather than treating the engine as a transient request-only state.

#### Game Management and Action Flow
`assets/app/game_manager.rb` is the client-side coordinator for loading games and submitting interactions. Server-side game routes receive actions, while `lib/engine/action.rb` and concrete action classes such as `lib/engine/action/lay_tile.rb` represent serializable player commands. The engine validates and applies actions against the current round/step, updates state and logs, then the persisted state/action stream is returned to clients.

#### Shared Rules Engine
`lib/engine.rb` is the core domain entry point. `lib/engine/game/base.rb` supplies common game state and lifecycle behavior; domain objects such as `lib/engine/corporation.rb` and `lib/engine/tile.rb` model economic and map state. Generic round orchestration (`lib/engine/round/operating.rb`) delegates legal decisions and mutations to reusable steps (`lib/engine/step/track.rb`, `lib/engine/step/buy_train.rb`). This is the primary boundary: UI and database code should not encode rules.

#### Title Implementations
Each title is a specialization of the shared engine, normally defining metadata, entities, map, and a game class under `lib/engine/game/<title>/`. For example, `lib/engine/game/g_1846/game.rb`, `lib/engine/game/g_1846/entities.rb`, and `lib/engine/game/g_1846/map.rb`. Title-specific steps override or extend shared mechanics, e.g. `lib/engine/game/g_1817/step/loan.rb`. The loader/registry boundary is represented by `lib/engine/game.rb` and `assets/app/game_class_loader.rb`.

#### Client Rendering
The frontend is a Ruby component tree, not a separate JavaScript SPA. `assets/app/view/game.rb` composes game screens; `assets/app/view/game/game_data.rb` bridges engine state into renderable data; map visuals are assembled in `assets/app/view/game/map.rb` and tile-part components such as `assets/app/view/game/part/track.rb`. User gestures select legal engine actions; rendering reflects the authoritative server game state.

#### Runtime and Infrastructure
The system is containerized with application, PostgreSQL, Redis, and nginx components: `Dockerfile`, `db/Dockerfile`, `redis/Dockerfile`, and `nginx/nginx.conf`; Compose definitions include `docker-compose.prod.yml`. Redis-backed messaging/queue concerns are indicated by `lib/bus.rb` and `queue.rb`. Unicorn configuration is at `config/unicorn.rb`; New Relic configuration exists at `config/newrelic.yml`. Mail flows for verification, resets, and turn notifications live in `assets/app/mail/`.

Main entry points: [`routes/game.rb`](https://github.com/tobymao/18xx/blob/master/routes/game.rb) (Game routes), [`lib/engine/round/operating.rb`](https://github.com/tobymao/18xx/blob/master/lib/engine/round/operating.rb) (Operating round), [`db.rb`](https://github.com/tobymao/18xx/blob/master/db.rb) (Game persistence), [`docker-compose.prod.yml`](https://github.com/tobymao/18xx/blob/master/docker-compose.prod.yml) (Production compose)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_client["Ruby Client"]
  node_client_app["Client app<br/>Ruby UI entry<br/>[app.rb]"]
  node_game_manager["Game manager<br/>UI coordinator<br/>[game_manager.rb]"]
  node_game_view["Game screen<br/>UI component<br/>[game.rb]"]
  node_game_data["Game data bridge<br/>UI adapter<br/>[game_data.rb]"]
  node_map_view["Map rendering<br/>UI component<br/>[map.rb]"]
end

subgraph group_web["Web Application"]
  node_rack["Rack entry<br/>[config.ru]"]
  node_api["API root<br/>HTTP API<br/>[api.rb]"]
  node_game_routes["Game routes<br/>HTTP routes<br/>[game.rb]"]
  node_user_routes["User routes<br/>HTTP routes<br/>[user.rb]"]
  node_mail["Mail flows<br/>Notifications"]
end

subgraph group_engine["Rules Engine"]
  node_engine{{"Engine entry<br/>Domain entry<br/>[engine.rb]"}}
  node_game_registry["Title registry<br/>Game loader<br/>[game.rb]"]
  node_base_game["Base game<br/>Shared game state<br/>[base.rb]"]
  node_action["Action model<br/>Serializable command<br/>[action.rb]"]
  node_round["Operating round<br/>Round orchestration<br/>[operating.rb]"]
  node_steps["Shared steps<br/>Rules steps<br/>[track.rb]"]
  node_title_1846["1846 title<br/>Title implementation<br/>[game.rb]"]
  node_title_1817_loan["1817 loan step<br/>Title-specific rule<br/>[loan.rb]"]
end

subgraph group_data["Persistence &amp; Async"]
  node_database[("Game persistence<br/>PostgreSQL models<br/>[db.rb]")]
  node_game_record["Game record<br/>Persistence model<br/>[game.rb]"]
  node_action_record["Action history<br/>Persistence model<br/>[action.rb]"]
  node_bus_queue["Bus and queue<br/>Redis messaging<br/>[bus.rb]"]
end

subgraph group_runtime["Deployment"]
  node_production["Production compose<br/>Container topology"]
end

node_rack -->|"boots"| node_api
node_api -->|"mounts"| node_game_routes
node_api -->|"mounts"| node_user_routes
node_client_app -->|"coordinates"| node_game_manager
node_game_manager -->|"loads and submits actions"| node_game_routes
node_game_manager -->|"drives"| node_game_view
node_game_view -->|"renders from"| node_game_data
node_game_view -->|"composes"| node_map_view
node_game_routes -->|"deserializes"| node_action
node_game_routes -->|"executes rules"| node_engine
node_engine -->|"loads title"| node_game_registry
node_game_registry -->|"selects"| node_title_1846
node_title_1846 -->|"specializes"| node_base_game
node_engine -->|"advances rounds"| node_round
node_round -->|"delegates to"| node_steps
node_round -.->|"uses title override"| node_title_1817_loan
node_action -->|"validated and applied"| node_round
node_game_routes -->|"persists game state"| node_game_record
node_game_routes -->|"appends commands"| node_action_record
node_game_record -->|"stored in"| node_database
node_action_record -->|"stored in"| node_database
node_user_routes -->|"manages users and sessions"| node_database
node_game_routes -.->|"publishes async work"| node_bus_queue
node_bus_queue -.->|"turn notifications"| node_mail
node_production -->|"runs application"| node_rack
node_production -->|"runs PostgreSQL"| node_database
node_production -->|"runs Redis"| node_bus_queue

click node_rack "https://github.com/tobymao/18xx/blob/master/config.ru"
click node_api "https://github.com/tobymao/18xx/blob/master/api.rb"
click node_game_routes "https://github.com/tobymao/18xx/blob/master/routes/game.rb"
click node_user_routes "https://github.com/tobymao/18xx/blob/master/routes/user.rb"
click node_client_app "https://github.com/tobymao/18xx/blob/master/assets/app/app.rb"
click node_game_manager "https://github.com/tobymao/18xx/blob/master/assets/app/game_manager.rb"
click node_game_view "https://github.com/tobymao/18xx/blob/master/assets/app/view/game.rb"
click node_game_data "https://github.com/tobymao/18xx/blob/master/assets/app/view/game/game_data.rb"
click node_map_view "https://github.com/tobymao/18xx/blob/master/assets/app/view/game/map.rb"
click node_engine "https://github.com/tobymao/18xx/blob/master/lib/engine.rb"
click node_game_registry "https://github.com/tobymao/18xx/blob/master/lib/engine/game.rb"
click node_base_game "https://github.com/tobymao/18xx/blob/master/lib/engine/game/base.rb"
click node_action "https://github.com/tobymao/18xx/blob/master/lib/engine/action.rb"
click node_round "https://github.com/tobymao/18xx/blob/master/lib/engine/round/operating.rb"
click node_steps "https://github.com/tobymao/18xx/blob/master/lib/engine/step/track.rb"
click node_title_1846 "https://github.com/tobymao/18xx/blob/master/lib/engine/game/g_1846/game.rb"
click node_title_1817_loan "https://github.com/tobymao/18xx/blob/master/lib/engine/game/g_1817/step/loan.rb"
click node_database "https://github.com/tobymao/18xx/blob/master/db.rb"
click node_game_record "https://github.com/tobymao/18xx/blob/master/models/game.rb"
click node_action_record "https://github.com/tobymao/18xx/blob/master/models/action.rb"
click node_bus_queue "https://github.com/tobymao/18xx/blob/master/lib/bus.rb"
click node_production "https://github.com/tobymao/18xx/blob/master/docker-compose.prod.yml"

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_client_app,node_game_manager,node_game_view,node_game_data,node_map_view toneBlue
class node_rack,node_api,node_game_routes,node_user_routes,node_mail toneAmber
class node_engine,node_game_registry,node_base_game,node_action,node_round,node_steps,node_title_1846,node_title_1817_loan toneMint
class node_database,node_game_record,node_action_record,node_bus_queue toneRose
class node_production toneIndigo
```

## Components

### Ruby Client

Ruby-rendered interactive game UI.

- [Client app](https://github.com/tobymao/18xx/blob/master/assets/app/app.rb): Ruby UI entry · `assets/app/app.rb`
- [Game manager](https://github.com/tobymao/18xx/blob/master/assets/app/game_manager.rb): UI coordinator · Loads games and submits player interactions. · `assets/app/game_manager.rb`
- [Game screen](https://github.com/tobymao/18xx/blob/master/assets/app/view/game.rb): UI component · `assets/app/view/game.rb`
- [Game data bridge](https://github.com/tobymao/18xx/blob/master/assets/app/view/game/game_data.rb): UI adapter · Transforms engine state into renderable data. · `assets/app/view/game/game_data.rb`
- [Map rendering](https://github.com/tobymao/18xx/blob/master/assets/app/view/game/map.rb): UI component · `assets/app/view/game/map.rb`

### Web Application

HTTP, authentication, and game lifecycle boundary.

- [Rack entry](https://github.com/tobymao/18xx/blob/master/config.ru): Rack · `config.ru`
- [API root](https://github.com/tobymao/18xx/blob/master/api.rb): HTTP API · `api.rb`
- [Game routes](https://github.com/tobymao/18xx/blob/master/routes/game.rb): HTTP routes · Receives game lifecycle and action requests. · `routes/game.rb`
- [User routes](https://github.com/tobymao/18xx/blob/master/routes/user.rb): HTTP routes · `routes/user.rb`
- **Mail flows**: Notifications · Verification, reset, and turn notifications.

### Rules Engine

Deterministic shared rules and title implementations.

- [Engine entry](https://github.com/tobymao/18xx/blob/master/lib/engine.rb): Domain entry · `lib/engine.rb`
- [Title registry](https://github.com/tobymao/18xx/blob/master/lib/engine/game.rb): Game loader · Resolves supported game title implementations. · `lib/engine/game.rb`
- [Base game](https://github.com/tobymao/18xx/blob/master/lib/engine/game/base.rb): Shared game state · Common game lifecycle and state behavior. · `lib/engine/game/base.rb`
- [Action model](https://github.com/tobymao/18xx/blob/master/lib/engine/action.rb): Serializable command · Player commands are validated and replayable. · `lib/engine/action.rb`
- [Operating round](https://github.com/tobymao/18xx/blob/master/lib/engine/round/operating.rb): Round orchestration · `lib/engine/round/operating.rb`
- [Shared steps](https://github.com/tobymao/18xx/blob/master/lib/engine/step/track.rb): Rules steps · Reusable legal-decision and mutation rules. · `lib/engine/step/track.rb`
- [1846 title](https://github.com/tobymao/18xx/blob/master/lib/engine/game/g_1846/game.rb): Title implementation · Defines title-specific game, entities, and map. · `lib/engine/game/g_1846/game.rb`
- [1817 loan step](https://github.com/tobymao/18xx/blob/master/lib/engine/game/g_1817/step/loan.rb): Title-specific rule · `lib/engine/game/g_1817/step/loan.rb`

### Persistence & Async

Durable game history and background messaging.

- [Game persistence](https://github.com/tobymao/18xx/blob/master/db.rb): PostgreSQL models · Stores users, games, and action history for replay and undo. · `db.rb`
- [Game record](https://github.com/tobymao/18xx/blob/master/models/game.rb): Persistence model · `models/game.rb`
- [Action history](https://github.com/tobymao/18xx/blob/master/models/action.rb): Persistence model · `models/action.rb`
- [Bus and queue](https://github.com/tobymao/18xx/blob/master/lib/bus.rb): Redis messaging · Supports asynchronous messaging and queued work. · `lib/bus.rb`

### Deployment

Containerized production runtime.

- [Production compose](https://github.com/tobymao/18xx/blob/master/docker-compose.prod.yml): Container topology · Runs app, PostgreSQL, Redis, and nginx. · `docker-compose.prod.yml`

## Connections

- Rack entry → API root: boots
- API root → Game routes: mounts
- API root → User routes: mounts
- Client app → Game manager: coordinates
- Game manager → Game routes: loads and submits actions
- Game manager → Game screen: drives
- Game screen → Game data bridge: renders from
- Game screen → Map rendering: composes
- Game routes → Action model: deserializes
- Game routes → Engine entry: executes rules
- Engine entry → Title registry: loads title
- Title registry → 1846 title: selects
- 1846 title → Base game: specializes
- Engine entry → Operating round: advances rounds
- Operating round → Shared steps: delegates to
- Operating round → 1817 loan step: uses title override
- Action model → Operating round: validated and applied
- Game routes → Game record: persists game state
- Game routes → Action history: appends commands
- Game record → Game persistence: stored in
- Action history → Game persistence: stored in
- User routes → Game persistence: manages users and sessions
- Game routes → Bus and queue: publishes async work
- Bus and queue → Mail flows: turn notifications
- Production compose → Rack entry: runs application
- Production compose → Game persistence: runs PostgreSQL
- Production compose → Bus and queue: runs Redis
