Егор Урванов

Архитектура игры «Аптайм»: C4 и критические пути

Схемы C4 до уровня компонентов и четыре критических пути через sequence: запуск уровня, шаг симуляции потока, правка на паузе и итог с разбором.

«Аптайм» — игра про работу инженера: собираешь сервис из блоков, пускаешь поток пользователей и смотришь, что сломается первым. 43 уровня в пяти группах, от одного сервера до распила монолита. Браузер, без сервера и сборки. Здесь описано, как она устроена: схемы C4 до уровня компонентов и четыре пути, на которых всё держится.

C4: контекст и контейнеры

Уровень 1. Система и её окружение

Игрокмышь, касание«Аптайм»игра в браузереlocalStorageпрогресс, языкGoogle Fontsшрифты, по желаниюТесты в Node43 уровня без экранаиграетзвёзды, черновикишрифтгоняют уровни
Своего сервера нет. Без сети игра работает так же, только шрифт берётся системный. Открыть можно двойным щелчком по index.html.

Уровень 2. Контейнеры

Страница и реестрindex.html, l.jsmanifest.jsСборкаmain.jsИнтерфейсui/*экраны, инспекторПолеrender/board.jsтрубы и баки, SVGСимуляцияsim/*без DOMСодержаниеcontent/*уровни, тексты ru/enЯдроcore.jsU, генераторВидcss/game.cssсветлая и тёмнаягрузит частиstart()new Sim, stepсхема, кадруровеньпрогонклассыобщее U
Симуляция ничего не знает об экране: её рисует поле, и она же целиком работает в Node. Уровни описывают только механику, все слова лежат в двух словарях.

C4 уровня 3: компоненты контекстов

Все части — обычные скрипты с реестром L, как в остальных играх репозитория: L.def объявляет часть, L.use берёт её и выполняет при первом обращении. Цикл зависимостей падает сразу с понятной ошибкой. Общий экспорт части складывают в пространство имён U из core.js.

Симуляция

Simstep, resultМодельmodel.jsАварииfireIncidentsМасштабscale, k8sTickСостояние узловsvcState, dbStateОчереди, сокетыchannels, wsTickИтогresult, evalObjectiveПрогонrun.jsконстанты, ценыпо прошлому тикудо конца
Компоненты симуляции потока
  • Модель хранит каталог из 25 блоков, допустимые связи, цены в месяц, очередь Erlang C и кривую трафика уровня.
  • Sim.step раз в полсекунды игрового времени: аварии, масштабирование, состояние узлов по нагрузке прошлого тика, затем поток сегментов по трубам со стеком вызовов и тайм-аутами.
  • Аварии — 27 видов: всплеск, падение зоны, региона или облака, истёкший сертификат, плохой выпуск, утечка памяти, боты и другие.
  • Итог сводит доступность, ожидание (p95), деньги, проверки уровня и звёзды; Прогон делает то же без экрана и разгоняет трафик в нагрузочном тесте.

Поле

Boardrender, liveБакиrenderNodeТрубыlayoutEdgesГруппыrenderHullsТочки потокаanimateDotsВводперетаскивание, связьSim(другой контекст)читает rt, tick
Компоненты поля
  • Бак — блок с заливкой: уровень показывает загрузку, цвет — справляется ли блок.
  • Труба: толщина — объём потока, пунктир — асинхронно, свой цвет у команд, событий, сокетов и общей базы.
  • Туман: на уровнях про наблюдаемость данные блоков скрыты, пока не поставлены мониторинг, логи или трейсинг.

Интерфейс

appэкраны, цикл кадраГлавная, уровниhome, levelsБрифингпрогнозИнспекторнастройки блокаРазборADR, путь, графикиОбучениеcoachРежимывыживание, песочницаi18nязык, схема настроекU.t, U.CTL
Компоненты интерфейса
  • i18n выбирает язык, переводит по ключу и описывает настройки каждого блока: переключатели, ступени, числа.
  • Инспектор показывает только те настройки, которые уровень уже открыл: сложность прибавляется постепенно.
  • Разбор после забега: решение в виде ADR с плюсами и минусами, путь запроса по частям ожидания и графики.

Четыре критических пути

1. Запуск и уровень

Игрокindex.htmlmainappУровниBoardоткрываетскрипты по спискуstart()главнаяуровеньlevelById, startArchбрифинг, прогнозответnew Board(arch)поле
Прогноз «что сломается» — кнопка старта: игрок сначала делает ставку, потом видит поток.

2. Шаг симуляции

frameSimАварииУзлыОчередиBoardstep()fireIncidentsscale, k8sTickсостояние по прошлому тикусегменты по трубамchannels, wsTickтайм-ауты, повторыtick: av, p95, $live(sim): баки, трубы, точки
Состояние узла берётся из нагрузки прошлого тика, поэтому порядок обхода не важен и цепочки A → B → C → A считаются естественно. Шаг фиксированный, до 80 шагов за кадр на ускорении.

3. Правка на паузе

ИгрокBoardappSimИнспекторпаузащелчок по блокуselect(n)видимые настройкименяетsetCfgupdateStaticдальшеsetArch(arch)прогретое сохраняется
Новая схема подменяется на ходу: работающие машины, очереди и кэши остаются, новые машины загружаются как в жизни.

4. Итог и разбор

frameSimapplocalStorageИгрокdone()finishLevelresult()проверки, звёздыresзвёзды, достиженияитог, прогнозADR, путь запроса
Итог отвечает на два вопроса: выдержала ли система и почему. Разбор раскладывает ожидание по частям и показывает, где было тонко.

Правила, на которых всё держится

  • Симуляция не знает об экране. Ни DOM, ни SVG: поле читает её состояние после шага.
  • Уровень — данные. Трафик, аварии, цели и стартовая схема; слова — в двух словарях с одинаковыми ключами.
  • Каждый уровень доказан. Тест прогоняет наивную схему (должна провалиться) и эталонную (должна пройти на две звезды и выше) для всех 43 уровней и дневных заданий.
  • Один шаг — полсекунды. Фиксированный шаг даёт одинаковый забег на любой машине.
  • Сложность по капле. Новые блоки и настройки открываются уровнем, на котором они нужны.

Слабые места

Где Что не так
Большой интерфейс ui/app.js на 870 строк держит все экраны, инспектор, обучение и режимы в одном замыкании.
Длинный шаг Sim.step около 350 строк: поток, повторы, тайм-ауты и аварии в одном методе.
Общее пространство имён Части пишут экспорт в U, поэтому зависимость видна по L.use, а конкретное имя — только по поиску.
Баланс константами Числа в K подобраны вручную; держит их только тест наивных и эталонных схем.
Тест в браузере по времени Проверка интерфейса ждёт по секундам и на холодном старте браузера иногда опаздывает.

Об игре

Страница игрыАптаймСтроишь сервис. Он ломается. Ты чинишь. Игра про работу инженера
© 2026 Егор Урванов