Frontend Engineer · Фев 2024 — Июль 2025
ОхотАктив · Аукционы
React · TypeScript · Redux Toolkit · SSE
Что это
Аукцион — это лот с живыми ставками. Участник поднимает цену на шаг, и его ставка тут же видна всем остальным на странице: без перезагрузки и без кнопки «обновить». У лота есть время старта, таймер и дедлайн, а цена ходит в реальном времени, пока идут торги.
Весь фронтенд аукционов я собрал в одиночку, end-to-end, примерно за 2.5 месяца. Бэкенд вёл отдельный разработчик, так что дальше — честно про границу: где заканчивается фронт и начинается сервер. Фича появилась не из спроса, а по инициативе руководства, и коммерчески себя за год не оправдала — поэтому рассказываю не про бизнес-результат, а про то, что было сложно технически и что из этого целиком на мне.
Фронт real-time
Ставки прилетают на страницу сами — я принимаю поток live-событий по SSE на фронте. Под приём отведён отдельный Redux slice: одно место, где живёт состояние лота и лента ставок, и куда стекаются все входящие события. Само соединение спрятано за класс-обёрткой — API-класс над каналом, который держит подключение, переподключается при обрыве и отдаёт разобранные события в slice. Компоненты не знают про транспорт: они читают состояние из стора, а не из сокета.
Каким протоколом гнать поток — SSE или что-то ещё — и как гарантировать порядок ставок на сервере решал бэкенд: это его зона, вместе с безопасностью и инфраструктурой. Я работал и с WebSocket, они во многом удобнее; здесь на фронте у меня был SSE, и задача была принять этот поток и не соврать зрителю ни в цене, ни в порядке.
Порядок событий на клиенте
Живые события не гарантированно приходят по порядку — сеть может переставить их местами. Каждое событие несёт свой id и id предыдущего, и на клиенте я выстраиваю по ним цепочку: применяю ставку только тогда, когда её «предыдущий» совпадает с текущей вершиной ленты. Событие, прилетевшее раньше времени, ложится в буфер и ждёт, пока заполнится разрыв, — и только потом встаёт на своё место. Зритель всегда видит связную историю торгов, а не мигающие вперёд-назад цифры.
Это именно клиентская сторона гонок. Серверный порядок — очередь запросов, присвоение id и prev id, консистентность самих ставок между всеми участниками — держит бэкенд; такую гарантию фронт не даёт и не подменяет. Моя часть — не рассыпать уже упорядоченный поток на приёме и корректно собрать его обратно, когда события пришли вперемешку.
Оптимистичная ставка и откат
Когда участник жмёт «поставить», я показываю его ставку сразу, оптимистично, — иначе на живых торгах интерфейс ощущается тормозным. Но оптимизм подстрахован: если сервер ставку не подтвердил, я откатываю её обратно к последнему подтверждённому состоянию, чтобы на экране не осталось цены, которой на самом деле нет.
Отдельная мелочь, без которой всё сыпалось, — двойной клик. На кнопку ставки я вешаю лок на время отправки: пока запрос в полёте, повторное нажатие не уходит. Так один и тот же участник не отправляет свою ставку дважды из-за нетерпеливого второго тыка.
Часовые пояса
Самая неочевидно сложная часть — время. Участники заходят из разных регионов, а старт, таймер и дедлайн лота должны читаться в локальном времени каждого: человек должен успеть к торгам и не перепутать, во сколько по-своему они начинаются. Поэтому все моменты лота я показываю в часовом поясе зрителя, а не сервера. Ошибиться тут легко и дорого — сдвиг на пару часов означает, что кто-то приходит к уже закрытому лоту; на этом ушло непропорционально много аккуратной работы.
UI лота
Поверх этого — анимированный адаптивный интерфейс: карточка лота, шаг ставки, лента истории и таймер, которые одинаково живут и на десктопе, и на телефоне. Смена цены, приход новой ставки, тиканье таймера — всё это анимировано так, чтобы поток событий читался глазом, а не мелькал. На торгах внимание зрителя и так на пределе, и UI не должен добавлять шума.
Что осталось на мне
Итого моя зона — весь фронтенд аукционов: приём live-потока по SSE через отдельный slice и класс над соединением, клиентская сборка порядка событий по id и prev id, оптимистичные ставки с откатом и локом кнопки, корректные часовые пояса и живой адаптивный UI. Серверные гарантии — протокол, порядок и консистентность ставок — со стороны бэкенда. Всё, что видит и трогает участник в браузере, собрано мной, соло, за ~2.5 месяца.