geek.moscowдокументация

Архитектура

Платформа состоит из восьми бизнес-сервисов и Gateway. Браузер обращается к Gateway по HTTP, WebSocket и SSE; внутренние вызовы выполняются по gRPC поверх TLS. PostgreSQL хранит предметные данные, RabbitMQ доставляет события и задания, Redis обслуживает восстанавливаемое состояние, объектное хранилище — фотографии и логотипы.

Объяснение выбора стека, устройства Module Federation, логирования и возможностей параллельной разработки — в разделе «Технологии и командная разработка». Правила доступа для участников и операторов собраны в ролевой модели.

Источники: итоговая архитектура, возможности сервисов, контракты и хранилища. Ниже описано согласованное устройство; наличие архитектурного требования не заменяет проверку конкретной реализации. Отдельные условия выпуска документов 29 и 30 указаны в индексе.

Общая схема

flowchart LR
    UI["Продуктовый интерфейс"] --> GW["Gateway: public"]
    ADMIN["Admin UI"] --> AGW["Gateway: admin"]
    subgraph SERVICES["Бизнес-сервисы, gRPC / TLS"]
        ID["Identity / Accounts"]
        C["Candidates"]
        E["Employers"]
        O["Opportunities"]
        R["Recruitment"]
        CH["Chat"]
        N["Notifications"]
        B["Backoffice"]
    end
    GW --> SERVICES
    AGW -->|"Разрешённые административные методы"| SERVICES
    SERVICES --> PG[("PostgreSQL: база каждого сервиса")]
    SERVICES -->|"Outbox"| MQ["RabbitMQ: события и задания"]
    MQ --> SERVICES
    SERVICES -.-> REDIS[("Redis")]
    C --> S3[("Объектное хранилище")]
    E --> S3
    C --> FSP["Адаптер реестра ФСП"]
    ID --> SMTP["Postfix / SMTP: письма безопасности"]

Принципы

  • Один владелец данных. Сервис имеет собственную логическую базу PostgreSQL и роль доступа. Чужие таблицы не читаются напрямую; используются RPC или допустимые проекции событий.
  • Предметные проверки у владельца. Gateway маршрутизирует запрос, проверяет токен, формирует доверенные метаданные, преобразует JSON и protobuf, применяет лимиты и собирает составной ответ. Правила публикации, офера или раскрытия остаются в сервисах.
  • Три вида взаимодействия. Внутри сервиса — вызов модуля; для актуальных проверок и чтений — синхронный gRPC; для длительной работы и доставки изменений — очередь и worker.
  • Worker не становится отдельным доменным сервисом. API, издатель outbox, обработчики очередей и планировщик одной области используют общие модули и базу, но могут запускаться разными процессами.
  • Разрешения проверяет сервер. Кнопка в интерфейсе, старый токен, результат подбора или кэш не заменяют актуальные проверки состояния аккаунта, членства и согласия на раскрытие.

Владельцы данных

Сервис Чем владеет Основные взаимодействия
Identity / Accounts Аккаунты, email, хеши паролей, подтверждение почты, восстановление доступа, сессии, JWT/JWKS, состояние и тип аккаунта, служебные права, согласия с документами Выдача и проверка полномочий, блокировка, координация удаления, письма безопасности
Candidates Профиль инженера, контакты, фото, анкеты, достижения, привязка ФСП, выбор достижений, видимость анкет, блокировки компаний Безопасное представление анкеты, импорт ФСП, события для поиска, проверка раскрытия через Recruitment
Employers Карточки компаний, членства, роли OWNER/HR, приглашения Проверка полномочий представителей, управление организацией
Opportunities Вакансии, публикация, публичная или закрытая видимость, назначения HR Обновление поиска; проверка адресного доступа инженера через Recruitment
Recruitment Поисковые проекции, объяснения подбора, избранное, интересы, оферы, снимки условий, разрешения на раскрытие Проверки владельцев, создание событий для Chat и Notifications
Chat Диалоги оферов, сообщения, отметки доставки и прочтения Проверки доступа к диалогу и компании, актуальное отображение личности, доставка в реальном времени
Notifications Уведомления, настройки, прочтение и повторная доставка Обработка событий интереса, офера, ответа и сообщения
Backoffice Жалобы, обращения, назначения операторов, решения, попытки исполнения, аудит Получение допустимых материалов и выполнение решений через владельцев объектов

Страница компании собирается из Employers и Opportunities, анкета — из Candidates и Recruitment, чат — из Chat и разрешённого контекста Recruitment. Составление ответа не обходит проверки владельца и не создаёт общий кэш персональных данных.

Аутентификация и сессии

По решению DEC-28 используется собственная аутентификация по email и паролю внутри Identity. Отдельный внешний провайдер входа не требуется.

Для корпоративной поставки описан вариант подключения Keycloak и других совместимых OIDC-провайдеров. Это расширение через адаптер Identity/Gateway с привязкой внешней личности и сохранением внутренних сессий; в текущую email/password-реализацию готовый OIDC-вход не включён.

Gateway обслуживает /auth/* на том же origin, что и интерфейс. Непрозрачный refresh-токен находится в HttpOnly cookie gm_session, короткоживущий access JWT — в памяти frontend. Контракт предусматривает EdDSA, срок access-токена до пяти минут, aud=geek-api, идентификатор сессии sid и время аутентификации auth_time. Identity публикует JWKS; сервисы проверяют подпись, алгоритм, issuer и audience.

Пароли хранятся как хеши Argon2id. Ссылки подтверждения почты и сброса пароля одноразовые, их секреты хешируются в базе и передаются во фрагменте URL. Refresh-токен меняется при использовании; повторное применение старого токена отзывает всю семью сессии.

Для служебных писем настроен Postfix: Identity создаёт задание, worker отправляет письмо по SMTP. Настройка и отправка подтверждены владельцем проекта; сценарии и ссылки описаны в разделе почты.

Административный профиль использует отдельные cookie, ADMIN-семьи и aud=geek-admin-api. По решению D-12 каждый ADMIN-вызов проверяет сессию через основную базу Identity. PRODUCT-вызовы сохраняют локальную проверку JWT с ограниченным сроком действия до 300 секунд после отзыва; это не отменяет актуальных проверок блокировки аккаунта, ресурсов и членства.

Обязательный account_type при регистрации и сборка /me только для соответствующей стороны — требования выпуска документа 29. Подробности: спецификация входа, Gateway, Identity.

Внешний API

Предметные REST-маршруты имеют префикс /api/v1; /auth/* обслуживается отдельно. Неизвестные поля запросов отклоняются, значение me определяется из проверенного токена.

Группа путей Владелец
/me, /legal-documents, /me/legal-acceptances, /me/deletion Identity
/me/profile, /me/questionnaires, /questionnaires, /me/achievements, /me/fsp, /reference-data, /me/company-blocks Candidates
/companies, /me/companies Employers
/vacancies/{id}, /companies/{id}/vacancies Opportunities
/search/*, /matches/*, /me/favorites/*, /me/interests, /companies/{id}/interests, /offers, /me/offers, /me/disclosures Recruitment
/chats, /chats/{id}/messages, /chats/{id}/read, /chats/{id}/delivery Chat
/me/notifications, /me/notification-settings Notifications
/complaints, /support-requests Backoffice, пользовательская подача

Чтение и изменение обычно возвращают 200, создание — 201, принятая фоновая операция — 202 с объектом Operation, идемпотентное удаление или отзыв — 204. Переходы состояния возвращают обновлённый ресурс с version. WebSocket чата и SSE уведомлений завершаются на Gateway, который получает внутренние потоки gRPC.

В базовом плане тестирования учтены 143 RPC и 119 уникальных публичных маршрутов, включая auth, health/readiness и потоковые каналы. Это счётчики покрытия указанной версии контракта, а не подтверждение исполнения всех тестов и не неизменный размер API после расширений.

Сквозные операции

Публикация анкеты. Candidates одной транзакцией сохраняет состояние и событие outbox. Издатель отправляет событие в RabbitMQ, обработчик Recruitment обновляет поисковую проекцию. Владелец сразу видит сохранённую анкету; каталог может обновиться позже. При выдаче результатов повторно проверяется актуальная видимость.

Интерес. Recruitment проверяет владельца и доступность анкеты через Candidates, вакансию через Opportunities, ограничения и блокировки. Сохранение интереса и события выполняется совместно. Notifications уведомляет компанию; интерес не раскрывает контакты и не создаёт офер автоматически.

Офер. Проверяются аккаунт сотрудника, членство в компании, вакансия, анкета, блокировки, защита от повторного обращения и условия предложения. Проверка дубля и сохранение выполняются локальной транзакцией. Событие отдельно получают Chat для создания диалога и Notifications для уведомления инженера.

Принятие и раскрытие. Ответ инженера и разрешённый компании объём данных фиксируются одной транзакцией Recruitment. При последующем чтении Candidates заново получает актуальный объём разрешения и проверяет права компании. Старое принятие офера не даёт бессрочного обхода отзыва согласия.

Сообщение. Chat сохраняет сообщение вместе с событием. Повтор запроса после потери сети не создаёт дубль. Redis Pub/Sub передаёт межпроцессные сигналы; история остаётся в PostgreSQL и позволяет восстановить состояние после переподключения. Сохранение, доставка и прочтение — разные состояния; подтверждение брокера не равно прочтению человеком.

Уведомление. Обработчик проверяет получателя, настройки и текущий доступ. Уведомление и отметка обработки фиксируются до подтверждения RabbitMQ. Основной пользовательский канал — внутри приложения; email, push и Telegram описаны как дальнейшие адаптеры. Письма безопасности Identity являются отдельным механизмом.

Модерация. Backoffice хранит решение и управляет исполнением; публикацию скрывает Candidates или Opportunities, аккаунт блокирует Identity. Решение считается применённым после подтверждения владельца. Подробности — в администрировании.

Очереди, события и фоновые задания

Transactional Outbox связывает изменение предметных данных с событием: обе записи попадают в одну транзакцию PostgreSQL. Отдельный издатель отправляет устойчивые сообщения с publisher confirms и обязательной маршрутизацией, после чего отмечает отправку.

Каждый сервис-подписчик имеет свою очередь. Несколько обработчиков одного сервиса конкурируют внутри неё; два разных сервиса не должны делить одну очередь событий. Задание направляется владельцу операции, событие получают все заинтересованные подписчики.

Владелец Примеры фоновой работы
Candidates Импорт и обновление ФСП, обработка фото, события анкеты и блокировок
Employers Обработка логотипа, изменения карточки и полномочий
Opportunities Изменение публикации и видимости вакансии
Recruitment Индексация, пересчёт подбора, события интереса, офера, ответа и раскрытия
Chat Создание диалога, события сохранения и прочтения сообщений
Notifications Создание уведомлений и доставка обновлений интерфейсу
Identity Служебные операции аккаунта, блокировки, координация стирания
Backoffice Повтор исполнения решений и сверка результатов

Доставка выполняется как минимум один раз. Получатель одной транзакцией сохраняет результат и отметку обработки; повтор сообщения не должен повторять бизнес-эффект. Длительный внешний вызов не удерживает транзакцию БД. Временные ошибки получают ограниченные повторы с задержкой, окончательные — запись в очередь неуспешных сообщений (DLQ) с причиной. Для устойчивых очередей предусмотрен тип quorum.

События содержат необходимые идентификаторы и минимальные данные, без контактов и полных текстов переписки. Планировщик хранит время следующего запуска и идентификатор операции в PostgreSQL и создаёт задание через outbox. Контракты: обмен сообщениями.

Хранение и поиск

Логические базы: identity, candidates, employers, opportunities, recruitment, chat, notifications, backoffice. Предметные записи, операции, outbox и отметки обработки находятся у своего сервиса. RabbitMQ — транспорт, Redis — восстанавливаемый кэш, сигналы и счётчики лимитов.

Recruitment использует индексы и полнотекстовый поиск PostgreSQL. Поисковая проекция содержит профессиональные сведения и безопасные факты достижений; контакты остаются в Candidates. Кэш ограничен временем и контекстом пользователя, компании и версии данных; закрытые вакансии, контакты и неопубликованные анкеты не должны попадать в общую выдачу. Фотография до раскрытия не должна быть доступна по угадываемой публичной ссылке.

Поведение при сбоях

Сбой Требуемое поведение
RabbitMQ недоступен Сохранённые изменения и outbox остаются в PostgreSQL; доставка и проекции догоняют после восстановления
Издатель outbox остановлен События накапливаются; возраст неотправленных записей должен наблюдаться
Worker упал до commit Неподтверждённое сообщение доставляется повторно
Worker упал после commit, до ack Повтор обнаруживается по отметке обработки и не создаёт второй эффект
Redis недоступен Предметные данные и история сообщений сохраняются; история используется для восстановления сигналов. Защитные лимитеры могут отклонять операции по своему контракту
ФСП недоступна Профиль работает с последними импортированными фактами и видимым временем синхронизации
Chat недоступен История сохраняется; интерфейс показывает недоступность переписки
Notifications недоступен События ждут в очереди до восстановления
Проверка прав недоступна Закрытые данные и ограниченные действия не выдаются по устаревшему кэшу
PostgreSQL недоступен Зависимые записи останавливаются; несохранённый результат не подтверждается брокеру
Сообщение попало в DLQ Причина доступна оператору; повтор запускается после устранения причины

Архитектура предусматривает наблюдение за ошибками и задержками API, возрастом outbox, очередями, повторами, DLQ, лагом поиска, синхронизацией ФСП и потоковыми соединениями. Фактически описанное покрытие стенда приведено в инфраструктуре. Контакты, токены и тексты переписки не включаются в обычные логи.

Прикладные логи формирует Pino в JSON: Gateway записывает запросы, статусы и длительность, диагностика gRPC и очередей сохраняет контекст ошибок. Идентификаторы помогают сопоставить обращение с обработкой в сервисах. Prometheus собирает и предметные показатели — регистрации, подтверждения почты, результаты административных операций, — и ресурсы приложений и узла. Подробнее: формат логов и подключение Kibana, бизнес- и ресурсные метрики, дашборды Grafana.

Технологии и запуск

Backend использует TypeScript, NestJS и Fastify; protobuf определяет gRPC-контракты. Frontend построен на React и Vite с пакетом @geek.moscow/design-sys. Локальная инфраструктура запускается через Compose, кластерная — через k3s/Kubernetes. Образы Docker отмечаются первыми восемью символами SHA коммита, манифесты собираются kustomize. Структура кода описана в репозиториях.


Возможности платформы · Все разделы · Репозитории и структура кода

Обновлено 2026-10-10 Команда nubble · ЛЦТ-2026, трек ФСП Все разделы