Client Personal Demo (Open Source)
Role Solo Creator / Architect
Period Aug 2026
Stack
Go chi pgx/v5 PostgreSQL JWT Telegram Bot API Docker Compose
slotwise — бекенд бронювання прийомів preview

slotwise — бекенд бронювання прийомів

Огляд

Незалежний demo-проєкт: REST API на Go для бронювання прийомів — ресурси, послуги, життєвий цикл бронювання та Telegram-нагадування. Моделює патерн, що повторюється в широкому спектрі бізнесів (клініки, репетиторство, консультації, салони): ресурси бронюються на часові слоти, і єдине правило, яке ніколи не можна порушити — той самий ресурс не можна забронювати двічі.

Результат: гарантія проти подвійного бронювання живе в обмеженні Postgres, не в коді застосунку — і це твердження доведено живим тестом на конкурентність, а не просто заявлено.


Справжня складна задача

Очевидний підхід — перевірити на перетини, тоді вставити, якщо немає — має race window. Два запити на той самий ресурс з перетинним часом можуть обидва пройти перевірку до того, як хоч один вставить запис, і обидва вдадуться. Це реальний клас багів у системах бронювання, не гіпотетичний.

slotwise не робить “перевір-потім-встав”. Таблиця bookings має обмеження Postgres EXCLUDE на (resource_id, during) через btree_gist, де during — це tstzrange. Сам Postgres відхиляє другу вставку, якщо діапазони перетинаються — гарантія живе в тій самій транзакції, що й запис, тож для гонки просто немає вікна.


Архітектура

Домен slotwise та захист від подвійного бронювання


Ключові інженерні рішення

Рішення Чому
EXCLUDE USING gist (resource_id WITH =, during WITH &&) замість app-level перевірки перетинів БД забезпечує це всередині тієї самої транзакції, що й вставка — жодного race window при конкурентних запитах, який завжди має app-level перевірка
Помилка Postgres 23P01 (exclusion_violation) перекладена в типізовану ErrSlotTaken Шар хендлерів ніколи не має знати про коди помилок Postgres; саме доменна помилка поширюється до 409 Conflict
Переходи статусу бронювання перевіряються проти єдиної таблиці легальних ребер Той самий патерн, що й стейт-машина нарядів у fleet-crm: pending → confirmed → completed/cancelled/no_show, нелегальні переходи відхиляються типізованою помилкою, не приймаються мовчки
Відправка нагадування вимагає StatusConfirmed і “ще не відправлено”, перевіряється до виклику notifier Нагадування для непідтвердженого бронювання чи повторне сповіщення клієнта — реальні “footguns” — перевіряється на рівні сервісу, і невдалий виклик notifier ніколи не позначає нагадування як відправлене
Інтерфейс Notifier з фолбеком LogNotifier API повністю запускається без жодних зовнішніх credentials — Telegram bot token не потрібен для білду, тестів чи демо

Результати

  • Повний REST API: JWT-авторизація, ресурси/послуги/бронювання, перевірений життєвий цикл бронювання, Telegram-нагадування
  • Репозиторії та notifier на інтерфейсах всюди — кожен тест сервісу/хендлера працює проти in-memory fake; go test ./... -race проходить чисто, БД не потрібна
  • Гарантія проти подвійного бронювання доведена, не просто побудована: два одночасні запити POST /api/bookings, запущені паралельно проти живого сервера, на той самий ресурс з перетинним часом — підтверджено на живому бінарнику: рівно один 201 і один 409, рівно один запис у БД
  • Перевірено наскрізно через docker compose up: міграції застосовуються (включно з CREATE EXTENSION btree_gist), login засіяного користувача, повний CRUD, легальні/нелегальні переходи статусу, правила нагадувань (409 до confirm, 409 при повторній відправці)

Код: github.com/valpere/slotwise


Стек

Go · chi (router) · pgx/v5 (без ORM) · golang-jwt · bcrypt · PostgreSQL (tstzrange + EXCLUDE USING gist) · Telegram Bot API · Docker Compose · testify