← Капитанский журнал

Одна библиотека чтобы править всемими — как MemoDb стал нашим хребтом

Почему мы перестали жонглировать REST-эндпоинтами и WebSocket-событиями — и заменили всё единым хранилищем, которое само синхронизируется.

В ранние дни Tinker Dice данные жили в двух мирах. REST API отвечал за сохранение. WebSocket-события — за обновления в реальном времени. И каждый раз, когда состояние игры менялось, нам приходилось синхронизировать оба — скучная, ошибкоёмкая рутина, пожиравшая инженерное время и порождающая тонкие баги.

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

MemoDb убила этот цикл. Идея почти агрессивна в своей простоте: единое in-memory хранилище, с MongoDB для персистентности, с Socket.IO для рассылки изменений всем подписанным клиентам. Вызываешь memodb.put(doc) — обновление сохраняется и рассылается. Вызываешь useSubscribeDoc() на клиенте — получаешь реактивные обновления без единого строчки кода обработчиков событий.

Результат? Все игровые данные — графы, сессии, состояния игроков — текут через один механизм. Один источник правды. Один путь для отладки. Когда что-то ломается, есть ровно одно место, куда смотреть.

Мы также получили чистый мост между движком и базой данных бесплатно: GameEngineMemoDbPlugin занимается трансляцией, а сокет-действия вроде create_game и join_game идут через него. Больше никакого ручного кода синхронизации.

Были ли другие варианты? Мы рассматривали Firebase и Supabase, но vendor lock-in — реальная цена, когда строишь что-то настолько кастомное. Мы также думали оставить REST и WebSocket раздельными — но мы уже прожили этот кошмар.

MemoDb — не эффектная штука. Это инфраструктура. Но инфраструктурные решения, принятые рано, отзываются годами. Это дало нам единый, чистый конвейер данных тогда, когда он был нужнее всего.