Как VoteBoard обновляет голоса директоров в реальном времени
Разобрали голосование совета директоров через WebSocket: мгновенные результаты, атомарный batch для юридической значимости и формат REMOTE с временным окном.

«Если директор нажал “за”, остальные должны увидеть это сразу».
Со стороны пользователя всё выглядит коротко: директор открывает заседание, выбирает вариант голосования и видит, как результаты меняются без обновления страницы. Не надо спрашивать в чате, дошёл ли голос. Не надо ждать, пока кто-то соберёт таблицу руками. В VoteBoard это было главным ожиданием: голосование совета директоров должно ощущаться как общее действие, даже если участники находятся в разных местах.
Инженерная часть начинается там, где у результата появляется юридический вес. Голос — это не реакция в мессенджере. Его нельзя потерять, применить наполовину или показать одному участнику раньше, чем система зафиксировала состояние.
Мгновенный результат через WebSocket
Для обновлений сделали real-time слой на WebSocket. После принятия голоса система отправляет событие подключённым клиентам, и интерфейс обновляет результаты сразу. Пользователь не дёргает polling, не ловит устаревшее состояние после refresh, не сравнивает цифры с соседней вкладкой.
Важный момент: WebSocket здесь отвечает именно за доставку актуального состояния в интерфейс. Решение о том, принят голос или нет, остаётся на стороне серверной логики голосования. Сначала действие должно пройти правила заседания, потом его можно транслировать остальным участникам.
Из-за этого пришлось аккуратно разделить два ощущения:
- пользователь хочет мгновенную реакцию;
- система не имеет права показывать результат, который ещё не считается зафиксированным.
Компромисс простой: обновления летят быстро, но только после принятия операции. Для голосования совета директоров это важнее, чем иллюзия мгновенности в первые миллисекунды.
Batch-голосование без частичных состояний
Отдельная часть — пакетное голосование. В интерфейсе директор может проставить несколько голосов и отправить их одним batch-запросом. Для обычной формы можно было бы принять часть значений, часть подсветить ошибками и попросить пользователя поправить. Здесь так делать нельзя.
Атомарность критична для юридической значимости: пакет либо применяется целиком, либо не применяется как набор голосов. Иначе появляется неприятный сценарий: часть решений уже учтена, часть нет, а пользователь воспринимал отправку как одно действие.
Живая деталь с проекта: спорили именно вокруг поведения при ошибке в одном элементе batch. Самый удобный для интерфейса вариант — вернуть статусы по каждой позиции. Самый безопасный для предметной области — не фиксировать ничего, если весь пакет не проходит проверку. Для VoteBoard выбрали второе, потому что граница действия пользователя — вся отправка, а не отдельная строка внутри неё.
После этого WebSocket тоже становится проще для понимания: клиентам рассылается результат принятого состояния, а не серия промежуточных «три голоса записали, один откатили, ещё два ждут».
REMOTE и временное окно
Ещё один режим — заочный формат REMOTE. Он нужен, когда директора находятся в разных часовых поясах и не обязаны голосовать одновременно. Вместо общего созвона у голосования есть временное окно: внутри него участник может зайти и отдать голос в удобный момент.
Для пользователя это снижает трение: не надо подстраивать календарь под всех участников сразу. Для системы появляются жёсткие границы: голосование доступно только в установленном интервале, а результат должен корректно обновляться для тех, кто уже находится в интерфейсе.
В этом режиме real-time не исчезает. Если один директор проголосовал сейчас, другой, открывший заседание в это же окно, видит обновление. Если кто-то зайдёт позже, он получит уже актуальное состояние, а не историю ручных пересчётов.
Что получилось в архитектуре
В VoteBoard связались три требования, которые плохо живут отдельно:
- WebSocket для мгновенного отображения результатов;
- batch-запрос для отправки набора голосов одним действием;
- атомарное применение batch, потому что речь о юридически значимом голосовании;
- режим
REMOTEс временным окном для участников из разных часовых поясов.
Главная инженерная граница здесь — момент фиксации голоса. До него интерфейс может быть отзывчивым, но не должен выдавать неподтверждённое состояние за результат. После него событие уходит всем подключённым участникам, и цифры на экране меняются без перезагрузки.
Так пользователь видит простую вещь: нажал кнопку — голос учтён, результаты обновились. А система в этот момент проверяет окно голосования, принимает batch целиком и только затем рассылает новое состояние через WebSocket.