# Architecture & Components

## Execution Flow

```
Telegram Update (Message / CallbackQuery / ChatJoinRequest / ...)
    │
    ▼
Middlewares
    ├── UIDataMiddleware         — extract bot_id, user_id, chat_id from update
    ├── CallbackManagerOuterMiddleware — decode callback_data (inline or cache)
    └── UIContextMiddleware      — load NavigationState, create ExecutionContext
    │
    ▼
AiogramAdapter                   — route to handler by EventType
    │
    ▼
HandlerService                   — match handler, check throttle, evaluate conditions
    │
    ▼
ActionExecutor                   — execute ActionInstruction list sequentially
    │
    ▼
SceneRenderer                    — resolve flags → run on_enter → render content → send/edit message
    │
    ▼
NavigationState saved → Analytics tracked (if enabled)
```

## Core Components

### Schema Layer (`schema.py`)

The single source of truth. All bot UI is defined here as Pydantic models.

- `UIRouter` — top-level container (scenes, global handlers, events, config)
- `Scene` — screen definition (handlers, keyboards, content, flags, on_enter/on_exit)
- `Handler` — event-triggered action list within a scene
- `GlobalHandler` — cross-scene handler with priority
- `ActionInstruction` — discriminated union of 20+ action types (GOTO_SCENE, SEND_MESSAGE, SET_VARIABLE, etc.)
- `Flag` / `FlagGetter` / `ConditionalFlag` — dynamic data sources
- `Keyboard` / `Button` / `DynamicKeyboard` — static and data-driven keyboards with pagination

### Orchestrator (`router.py`)

`UIRouterExecutor` — the main entry point. Initializes all services, provides `get_router()` for aiogram integration, supports hot reload via `reload(new_schema)`.

### State (`state/`)

- `context.py` — `ExecutionContext` (request-scoped state: bot, user_id, chat_id, resolved flags, navigation state; not thread-safe — one per request), `NavigationState` (serializable via `to_dict()`/`from_dict()` with version migration), `NavigationManager`
- `storage.py` — `NavigationStorage` protocol, `InMemoryNavigationStorage`
- `factory.py` — `ContextFactory` (creates `ExecutionContext` with flag resolution)
- `variables.py` — `VariableRepository` protocol, `InMemoryVariableRepository`

### Flag Resolution (`rules/flags.py`)

`FlagResolver` — resolves flags from multiple sources in priority order:
1. Global flags (schema-level)
2. Scene-local flags
3. Variables as flags
4. Conditional flags (rule engine evaluation)

Each subsequent source can override previous values.

### Action Execution (`execution/actions.py`)

- `ActionExecutor` — executes individual actions (goto_scene, send_message, set_variable, etc.)
- `ContentResolver` — resolves text templates, localization (Fluent), media content, keyboards

### Service Layer (`services/`)

| Service | Responsibility |
|---|---|
| `NavigationService` | Scene transitions, history management, back navigation |
| `ContentService` | Content rendering, message sending/editing |
| `LocalizationService` | Fluent bundle management, locale resolution |
| `SceneRenderer` | Full scene entry cycle (flags → on_enter → content → keyboard → send) |
| `HandlerService` | Handler matching, throttling, condition evaluation, action execution |
| `SharedServices` | Container for shared components (storage, events, variables) |

### Callback Data (`execution/callbacks.py`)

Two strategies controlled by `CallbackDataStrategy`:
- **inline** — data embedded in callback_data string: `g|s:scene:handler[:params]` (64-byte limit)
- **cache** — data stored in `CacheStorageProtocol`, reference ID returned

### Rule Engine (`rules/engine.py`)

`RuleEngine` + `ConditionEvaluator` — evaluates declarative conditions using operators: `eq`, `ne`, `gt`, `lt`, `gte`, `lte`, `in`, `not_in`, `contains`, `starts_with`, `ends_with`, `exists`, `is_empty`.

### Events (`events/`)

- `bus.py` — `EventBus` (pub/sub), `EventScheduler` / `InMemoryEventScheduler` (once, cron, interval)
- `dispatcher.py` — `EventDispatcher` (event processing with context restoration)
- `sync.py` — `EventSynchronizer` (subscription management for hot reload)
- Event sources: internal actions, webhooks, scheduled triggers

### Registry (`registry.py`)

`MasterRegistry` contains typed sub-registries:
- `GetterRegistry` — flag getter functions
- `ValidatorRegistry` — input validators
- `ConditionRegistry` — rule conditions
- `ActionRegistry` — custom action handlers
- `BusinessActionsRegistry` — business logic hooks

Functions can be registered programmatically or loaded from import paths via schema's `custom_functions`.

### Storage Protocols

- `NavigationStorage` — state persistence (default: `InMemoryNavigationStorage`)
- `VariableRepository` — typed key-value store with scopes (USER/BOT/CHAT) and TTL (default: `InMemoryVariableRepository`)
- `CacheStorageProtocol` — cache for callback data (default: `InMemoryCacheStorage`)

### Validation (`validator.py`)

`SchemaValidator` checks schema integrity before runtime:
- Broken scene/handler references
- Unreachable scenes
- Dead-end scenes (no outgoing navigation)
- Missing function references in registry
- Event definition consistency

### Exception Hierarchy

```
UIRouterError
├── ConfigurationError
│   ├── SchemaValidationError
│   └── RegistryError
├── ActionError
│   ├── MissingActionFieldError
│   └── UnknownActionTypeError
├── NavigationError
│   └── SceneNotFoundError
├── CallbackDataError
│   ├── CallbackDecodeError
│   └── CallbackExpiredError
├── KeyboardRenderError
├── EventError
│   ├── EventSchedulingError
│   └── EventSyncError
├── ContentError
└── RuleEngineError
```

## Multi-Router Support

Multiple `UIRouterExecutor` instances can run in the same bot. Each uses a `prefix` for storage isolation. Schemas are independent and don't share state.

## Hot Reload

`executor.reload(new_schema)` replaces the schema without restarting. The aiogram `Router` instance stays connected to the Dispatcher. `EventSynchronizer` handles event subscription updates.
