
Время операционной — самый дефицитный ресурс клиники, но планируется оно чаще всего через общие таблицы и звонки. Пересекающиеся брони проскакивают, ни у кого нет живой картины дня, а каждое изменение запускает новый круг ручной координации — трение, которое тихо растёт по мере роста клиники.
Kliniq — платформа планирования, написанная с нуля: бэкенд на Kotlin / Spring Boot 3 с jOOQ поверх PostgreSQL и фронтенд на SvelteKit. Пересекающиеся брони не просто отсекаются в коде приложения — ограничение Postgres EXCLUDE делает их физически невозможными для вставки. Server-Sent Events держат каждое открытое расписание живым, drag-and-drop переносит бронь на месте, а самостоятельно развёрнутый стек аутентификации (Argon2id + WebAuthn passkeys) тянет всё это без сторонних провайдеров. Построено и выпущено в одиночку, от и до.
Двойных броней возможно
Ограничение Postgres EXCLUDE
Роли RBAC
Admin · Manager · Staff
Расписание в реальном времени
Server-Sent Events
тестов бэкенда
и ещё 8 e2e на Playwright

Ограничение Postgres EXCLUDE USING gist по каждой операционной и tstzrange делает две пересекающиеся брони физически невозможными для вставки — целостность гарантируется базой данных, а не проверками-надеждами в коде приложения.
Каждая бронь проходит детерминированный жизненный цикл — Scheduled → In-Progress → Completed / Cancelled — с защищёнными переходами в доменном слое, которые отклоняют недопустимые скачки до того, как они достигнут БД.
Server-Sent Events транслируют каждое создание, изменение и отмену на все подключённые дашборды — расписание остаётся живым по всей клинике без поллинга и ручного обновления.
Хеширование Argon2id, WebAuthn passkeys, сессии в Redis, защита от CSRF и rate-limiting по IP — полноценный стек идентификации, написанный с нуля, без Auth0 и внешних провайдеров.
Живое демо
Демо работает на kliniq.izotov.dev: три операционные и брони на две недели вперёд. Зайди под любым демо-хирургом — [email protected], пароль DemoSurgeon2026! — и подвинь бронь; вторая вкладка увидит изменение без перезагрузки.

С удовольствием объясню, почему защита от двойных броней живёт в базе, а не в интерфейсе, и чего это стоило в других местах.