Архитектура системы управления библиотекой¶
Обзор¶
Система управления библиотекой представляет собой fullstack приложение, состоящее из frontend (React + TypeScript) и backend (Node.js + Express) компонентов, работающих с PostgreSQL базой данных.
Архитектура следует принципам многослойной архитектуры (Layered Architecture) с разделением на слои: Presentation, Application, Domain, Infrastructure.
Структура проекта¶
library_system/
├── src/ # Frontend приложение (React + TypeScript)
│ ├── components/ # React компоненты
│ │ ├── dialogs/ # Модальные окна
│ │ ├── ui/ # Базовые UI компоненты (shadcn/ui)
│ │ └── ... # Другие компоненты
│ ├── pages/ # Страницы приложения
│ ├── hooks/ # Custom React hooks
│ ├── lib/ # Утилиты и конфигурация
│ └── types/ # TypeScript типы
│
├── server/ # Backend приложение (Node.js + Express)
│ ├── config/ # Конфигурация (DB, LDAP, Swagger)
│ ├── models/ # Sequelize модели (Domain Layer)
│ ├── repositories/ # Слой доступа к данным (Data Access Layer)
│ │ ├── BaseRepository.js
│ │ ├── OperationRepository.js # Главный репозиторий (Facade)
│ │ ├── OperationQueryRepository.js # Методы чтения
│ │ ├── OperationCommandRepository.js # Методы создания/изменения
│ │ └── ... # Другие репозитории
│ ├── services/ # Бизнес-логика (Application Layer)
│ │ ├── operations/ # Сервисы операций
│ │ └── ...
│ ├── routes/ # API маршруты (Presentation Layer)
│ ├── middleware/ # Express middleware
│ ├── validators/ # Валидация данных
│ ├── dto/ # Data Transfer Objects
│ ├── utils/ # Утилиты
│ ├── migrations/ # SQL миграции
│ ├── scripts/ # Вспомогательные скрипты
│ │ ├── process-outbox-events.js
│ │ ├── reconcile-outbox-stuck.js
│ │ └── cleanup-expired-bookings.js
│ └── workers/
│ ├── outboxProcessor.js
│ └── outboxWorker.js
│
└── docs/ # Документация
Frontend архитектура¶
Технологический стек¶
- React 18.3 - UI библиотека
- TypeScript 5.8 - Типизация
- Vite 5.4 - Сборщик и dev-сервер
- React Router 6.30 - Маршрутизация
- TanStack Query 5.83 - Управление серверным состоянием и кэширование
- React Hook Form 7.61 - Управление формами
- Zod 3.25 - Валидация схем
- shadcn/ui - UI компоненты на базе Radix UI
- Tailwind CSS 3.4 - Стилизация
- date-fns 3.6 - Работа с датами
- qrcode 1.5 - Генерация QR-кодов
Структура frontend¶
Компоненты (src/components/)¶
Диалоги (dialogs/):
- books/ - Диалоги работы с книгами (добавление, редактирование, копии)
- operations/ - Диалоги операций (выдача, возврат, списание, продление, массовые операции)
- bookings/ - Диалоги бронирований
- users/ - Диалоги управления пользователями
- reference/ - Диалоги справочников (авторы, жанры, издательства, полки)
- export-import/ - Диалоги экспорта/импорта данных
UI компоненты (ui/):
- Базовые компоненты: Button, Input, Dialog, Table, Select, Calendar и т.д.
- Состояния: LoadingState, ErrorState, EmptyState
- Специальные: QueueProcessingIndicator
Другие компоненты:
- Layout.tsx - Основной layout приложения
- AppSidebar.tsx - Боковая панель навигации
- NotificationCenter.tsx - Центр уведомлений
- DueDateNotifications.tsx - Уведомления о сроках возврата
- QRScanner.tsx - Компонент сканирования QR-кодов
- QRGenerator.tsx - Компонент генерации QR-кодов
- PaginatedList.tsx - Компонент пагинации списков
- ProtectedRoute.tsx - Защита маршрутов
Страницы (src/pages/)¶
Публичные:
- Auth.tsx - Авторизация
- Catalog.tsx - Публичный каталог книг
- BookDetail.tsx - Детальная информация о книге
Для читателей:
- MyLoans.tsx - Мои выдачи (с возможностью продления)
- MyBookings.tsx - Мои бронирования и очереди
Для библиотекарей и администраторов:
- Dashboard.tsx - Главная страница с статистикой
- BookCatalog.tsx - Административный каталог книг
- ActiveLoans.tsx - Активные выдачи (с возможностью продления)
- Bookings.tsx - Управление бронированиями
- Operations.tsx - История операций
- Users.tsx - Управление пользователями
- Authors.tsx - Управление авторами
- Genres.tsx - Управление жанрами
- References.tsx - Справочники
- Inventory.tsx - Инвентаризация
- WriteOffHistory.tsx - История списаний
- Scan.tsx - QR сканирование
- Settings.tsx - Настройки
Маршрутизация¶
Приложение использует React Router с защищенными маршрутами:
/auth- Авторизация (публичный)/- Дашборд (admin, librarian)/catalog- Публичный каталог (все роли)/my-loans,/my-bookings- Личные кабинеты (reader)- Остальные маршруты - Административные функции (admin, librarian)
Управление состоянием¶
- TanStack Query — кэширование и синхронизация данных с сервером; централизованные ключи в
src/lib/queryKeys.ts, инвалидация —src/lib/invalidateLibraryCache.ts - React Hook Form — локальное состояние форм
- React Context — глобальное состояние (тема, доступность)
- Сессия и вкладки —
src/lib/api.ts(ensureSessionFresh, координация refresh между вкладками),src/lib/authSync.ts(синхронный logout черезBroadcastChannel)
Backend архитектура¶
Технологический стек¶
- Node.js - Runtime окружение
- Express 4.18 - Web framework
- Sequelize 6.37 - ORM для PostgreSQL
- PostgreSQL - Реляционная база данных
- Redis 4.7 - Кэширование (опционально)
- JWT - Аутентификация
- Winston 3.15 - Логирование
- Swagger - API документация
Архитектурные слои¶
1. Routes (server/routes/) - Presentation Layer¶
API маршруты, обрабатывающие HTTP запросы:
auth.js- Аутентификация и авторизацияbooks/- Операции с книгами (CRUD, импорт, статистика)create.js,update.js,delete.js,import.js,stats.jsbookCopies.js- Управление экземплярами книгoperations.js- Операции выдачи/возврата/списания/продленияbookings.js- Бронирования и очередьusers.js- Управление пользователямиauthors.js,genres.js,publishers.js- Справочникиqr.js- QR-кодыhealth.js- Health check
Поток запроса:
HTTP Request → Middleware (auth, csrf, validation) → Route Handler → Service → Repository → Database
2. Services (server/services/) - Application Layer¶
Бизнес-логика приложения:
Основные сервисы:
- OperationService.js - Оркестрация операций (фасад)
- BookingService.js - Логика бронирований и очереди
- createBooking() - Создание бронирования
- cancelBooking() - Отмена бронирования
- addToQueue() - Добавление в очередь
- processQueueForBook() - Обработка очереди для книги
- cancelExpiredBookings() - Отмена просроченных бронирований
Сервисы операций (services/operations/):
- LoanService.js - Логика выдачи книг
- ReturnService.js - Логика возврата книг
- WriteOffService.js - Логика списания книг
- RenewalService.js - Логика продления выдач
- OperationStatsService.js - Статистика операций
Другие сервисы:
- BookService.js — логика работы с книгами
- UserService.js — логика работы с пользователями
- ReferenceService.js — справочники: авторы, жанры, издательства, полки, состояния, языки (с кэшем; методы getAll*Sorted, пагинация, CRUD для авторов/жанров/издательств; для инвалидации кэша используется cache.del / cache.invalidate)
- SupplierService.js — поставщики (getAllSorted, getById, create, update, delete); маршрут suppliers.js вызывает только сервис
- SupplyService.js — поставки (getAllWithDetails, getByIdWithDetails, create, update, delete); маршрут supply.js вызывает только сервис
- UserCategoryService.js — категории пользователей (getAllSorted, getById, create, update, delete с проверкой «в использовании»); маршрут userCategories.js вызывает только сервис
- ImportService.js — импорт книг из CSV (оркестрация репозиториев, парсинг по заголовкам, создание/поиск справочников и книг без дубликатов); маршрут books/import.js только проверяет файл и вызывает ImportService.importFromCsv()
3. Repositories (server/repositories/) - Data Access Layer¶
Слой доступа к данным, абстрагирующий работу с БД.
Архитектурный паттерн: Repository Pattern + CQRS (Command Query Responsibility Segregation)
Базовая структура:
- BaseRepository.js - Базовый класс для всех репозиториев
- Предоставляет общие методы: findById(), findAll(), create(), update(), delete()
Специализированные репозитории:
OperationRepository (Facade Pattern):
- OperationRepository.js (~150 строк) - Главный репозиторий, объединяющий Query и Command
- Делегирует вызовы методов в соответствующие репозитории
- Предоставляет единый интерфейс для работы с операциями
- validateRequiredFields() - Утилита валидации
- OperationQueryRepository.js (~650 строк) - Методы чтения (Query)
findAllWithDetails()- Поиск операций с деталямиfindWriteOffs()- Поиск операций списанияfindActiveLoansForReader()- Активные займы читателяfindAllActiveLoans()- Все активные займыfindOverdueLoans()- Просроченные займыfindByIdWithIncludes()- Поиск по ID с включениямиfindAll()- Поиск всех операцийfindActiveLoansForBook()- Активные выдачи для книгиgetActiveLoansStats()- Статистика активных выдач-
getTotalLoansStats()- Статистика всех выдач -
OperationCommandRepository.js (~650 строк) - Методы создания/изменения (Command)
createLoanWithBooking()- Создание выдачи с бронированиемcreateReturn()- Создание возвратаcreateWriteOff()- Создание списанияvalidateRequiredFields()- Валидация обязательных полей
Другие репозитории: - BookRepository.js - Работа с книгами - BookCopyRepository.js - Работа с экземплярами - BookingRepository.js - Работа с бронированиями - BookingQueueRepository.js - Работа с очередью бронирований - UserRepository.js - Работа с пользователями - ReferenceRepository.js - Работа со справочниками - И другие репозитории для справочников
Преимущества разделения: - ✅ Разделение ответственности (чтение vs запись) - ✅ Упрощение поддержки (меньше строк в каждом файле) - ✅ Улучшенная навигация - ✅ Упрощенное тестирование - ✅ Обратная совместимость (единый интерфейс через OperationRepository)
Принципы работы репозиториев:
- ✅ Репозитории НИКОГДА не создают транзакции - Только принимают через options.transaction
- ✅ Репозитории НИКОГДА не коммитят/откатывают транзакции - Управление только в сервисах
- ✅ Валидация обязательности транзакции - Репозитории проверяют наличие транзакции для критических операций
- ✅ Явные границы транзакций - Один сервисный метод = одна транзакция
4. Models (server/models/) - Domain Layer¶
Sequelize модели, описывающие структуру БД:
Основные сущности: - User - Пользователи (profiles) - Book - Книги (book_titles) - BookCopy - Экземпляры книг (book_copies) - Operation - Операции (operations) - Booking - Бронирования (bookings) - BookingQueue - Очередь бронирований - Renewal - Продления выдач
Справочники: - Author, Genre, Publisher, Language, Condition, Shelf - UserCategory - Категории пользователей - OperationType, OperationStatus - Типы и статусы операций - BookingStatus - Статусы бронирований
Служебные: - OutboxEvent - События для асинхронной обработки (Outbox Pattern) - IdempotencyKey - Ключи идемпотентности
5. Middleware (server/middleware/)¶
- auth.js - JWT аутентификация и авторизация
- csrf.js - CSRF защита
- asyncHandler.js - Обработка асинхронных ошибок
- apiVersion.js - Версионирование API
6. Validators (server/validators/)¶
Централизованная валидация входящих данных (express-validator). Все валидаторы реэкспортируются из index.js. Маршруты подключают их и не дублируют инлайн-цепочки body()/param().
Файлы:
- bookValidators.js — создание/редактирование книг, экземпляров (в т.ч. snake_case/camelCase для POST /book-copies)
- userValidators.js — пользователи
- bookingValidators.js — бронирования
- operationValidators.js — операции (выдача, возврат, списание, продление)
- referenceValidators.js — авторы, издательства, жанры, полки
- authValidators.js — вход (login, password)
- userCategoryValidators.js — категории пользователей
- qrValidators.js — QR-операции
- supplyValidators.js, supplierValidators.js — поставки и поставщики
Использование: маршруты users.js, books/crud.js, bookCopies.js, bookings.js, operations.js, authors.js, publishers.js, shelves.js, genres.js, auth.js, userCategories.js, qr.js, supply.js, suppliers.js используют валидаторы из validators/.
7. DTO (server/dto/)¶
Data Transfer Objects для форматирования ответов API. Реэкспорт из index.js.
BaseDTO.js— базовый классBookingDTO.js— форматирование бронированийOperationDTO.js— форматирование операций (в т.ч.OperationDTO.format(operation))UserDTO.js— форматирование пользователей
Сервисы (например, BookingService, UserService) импортируют DTO из ../dto для единообразных ответов.
Разделение валидации и бизнес-логики¶
Текущее состояние
- Валидация (backend): В
server/validators/сосредоточены все цепочки express-validator; маршруты вызывают валидаторы и не дублируют правила в роутах. ДублированиеvalidateRequiredFieldsубрано из модели Operation и оставлено только в OperationCommandRepository. - Валидация (frontend): Схемы форм (Zod) хранятся в
src/lib/validators/:operationSchemas.ts,referenceSchemas.ts,userSchemas.ts,bookSchemas.ts,index.ts. Диалоги и страницы импортируют схемы и используютzodResolver(schema)без дублированияz.object()для тех же сущностей. - Маршруты: Вызывают валидацию → при успехе вызывают сервисы (не репозитории напрямую, где это внедрено). Исключение:
auth.jsпо-прежнему использует UserRepository/UserCategoryRepository для логина/setup; при желании можно вынести в AuthService. - Сервисы: Бизнес-логика, оркестрация, транзакции. Не проверяют формат полей запроса.
Принципы
| Слой | Ответственность | Не должен |
|---|---|---|
| Validators | Формат и обязательность полей запроса | Реализовывать бизнес-правила |
| Routes | Валидация → вызов сервиса → ответ | Дублировать валидацию, содержать бизнес-логику |
| Services | Бизнес-правила, оркестрация, транзакции | Проверять тип/формат полей запроса |
| Repositories | Чтение/запись БД | Проверять «пришли ли нужные поля с фронта» |
База данных¶
PostgreSQL¶
База данных использует PostgreSQL с следующими основными таблицами:
Основные сущности¶
- profiles - Пользователи системы
- book_titles - Названия книг
- book_copies - Экземпляры книг
- operations - Операции (выдача, возврат, списание)
- bookings - Бронирования
- booking_queue - Очередь бронирований
- renewals - Продления выдач
Справочники¶
- authors - Авторы
- genres - Жанры
- publishers - Издательства
- languages - Языки
- conditions - Состояния книг
- shelves - Полки
- user_categories - Категории пользователей
- operation_types - Типы операций
- operation_status - Статусы операций
- booking_status - Статусы бронирований
Связующие таблицы¶
- book_authors - Связь книг и авторов
- book_genres - Связь книг и жанров
- user_roles - Роли пользователей
Служебные таблицы¶
- outbox_events - События для асинхронной обработки (Outbox Pattern)
- idempotency_keys - Ключи идемпотентности
- loan_tokens, return_tokens - Токены для QR-кодов
Миграции¶
База данных управляется через SQL миграции в server/migrations/:
- Последовательные миграции от 001 до 039
init.sql- Инициализационный скриптmigration-runner.js- Скрипт применения миграцийapply-init.js- Применение init.sql
Индексы и оптимизация¶
База данных содержит множество индексов для оптимизации запросов: - Индексы на часто используемые поля - Составные индексы для сложных запросов - GIN индексы для полнотекстового поиска (pg_trgm) - Уникальные индексы для предотвращения дубликатов
Триггеры и ограничения¶
- check_unique_active_loan - Триггер для предотвращения множественных активных выдач
- Учитывает операции возврата
- Проверяет наличие активной выдачи без возврата
- prevent_writeoff_with_active_loans - Предотвращение списания с активными выдачами
- prevent_book_copy_id_change - Предотвращение изменения book_copy_id после создания операции
Асинхронная обработка (Outbox Pattern)¶
Проблема¶
При возврате книги или отмене бронирования нужно обработать очередь надёжно и без заметной задержки для читателя.
Решение: синхронное продвижение + Outbox Pattern¶
Основной путь:
- Возврат —
ReturnServiceвызываетprocessQueueForBookиtriggerOutboxProcessing()сразу после коммита - Отмена брони —
BookingService.cancelBookingсинхронно вызываетprocessQueueForBookпосле коммита - Lazy expiration — просроченные
pendingна экземпляр отменяются приcreateBooking,addToQueueиprocessQueueForBook(не блокируют очередь до cron)
Резервный путь (Outbox):
Компоненты:
- OutboxEvent (таблица
outbox_events) - Хранит события для асинхронной обработки
- Тип события:
CopyBecameAvailable -
Payload:
{ bookCopyId, hasActiveBooking, hasQueue } -
outbox-worker (контейнер Docker,
scripts/run-outbox-daemon.js) - Отдельный процесс; у API по умолчанию
OUTBOX_WORKER_ENABLED=false - Периодически вызывает
processOutboxBatch()(несколько раундов за тик) -
Подстраховка, если синхронный прогон не завершился
-
outboxWorker.js (встроенный воркер в
server/index.js,OUTBOX_WORKER_ENABLED=true) -
Для локального
node index.jsбез отдельного контейнера -
process-outbox-events.js (скрипт / cron)
-
Тот же
processOutboxBatch, для окружений без встроенного воркера или как подстраховка -
outboxProcessor.js
- Блокировка события
FOR UPDATE SKIP LOCKED BookingService.processQueueForBook()— создание брони для головы очереди-
Пометка события обработанным и закрытие дубликатов по
bookCopyId -
Создание событий:
- При возврате книги (
OperationCommandRepository.createReturn) - При отмене бронирования (
BookingService.cancelBooking) - При истечении бронирований (
BookingService.cancelExpiredBookings)
Преимущества: - ✅ Надёжность (события не теряются) - ✅ Идемпотентность (можно повторять обработку) - ✅ Быстрый отклик (синхронное продвижение) + резерв через outbox - ✅ Масштабируемость (воркер можно вынести в отдельный контейнер)
Рекомендуемая настройка production (Docker):
- Контейнер outbox-worker с
OUTBOX_WORKER_ENABLED=true; у backend —false(без дублирования при нескольких репликах) - Профиль
jobs:booking-cleanup— ежедневная массовая отмена просроченных броней (дополнение к lazy expiration) - Опционально: cron
process-outbox-events.js, если воркер отключён
Обработка очереди бронирований¶
Логика обработки¶
- Добавление в очередь:
- Пользователь встает в очередь на недоступную книгу
-
Создается запись в
booking_queueс позицией -
Обработка очереди:
- При освобождении экземпляра (возврат, отмена, lazy expiration) вызывается
BookingService.processQueueForBook() - Дополнительно создаётся
OutboxEventтипаCopyBecameAvailableдля подстраховки -
Для первого в очереди создаётся бронь, запись удаляется из
booking_queue -
Условия обработки:
- Нет активной выдачи на экземпляр
- Нет активного (не просроченного) бронирования на экземпляр
-
Экземпляр не списан; при рассинхроне
is_availableвосстанавливается вprocessQueueForBook -
Пропуск головы очереди (переход к следующему):
- У пользователя уже есть бронь на этот экземпляр
- Достигнут лимит активных броней (5)
-
Учётная запись неактивна
-
Если экземпляр ещё забронирован другим читателем:
- Очередь ждёт отмены/истечения брони; затем новое outbox-событие обрабатывает очередь
Безопасность¶
Аутентификация¶
- JWT токены - Для аутентификации пользователей
- LDAP интеграция - Опциональная интеграция с Active Directory
- bcryptjs - Хеширование паролей
Защита¶
- CSRF токены - Защита от CSRF атак
- Helmet - Защита HTTP заголовков
- Rate Limiting - Ограничение частоты запросов
- Валидация входных данных - На уровне API и БД
- Транзакции - Атомарность операций
- Row-level locking - Предотвращение race conditions
Авторизация¶
Система ролей: - admin - Администратор (полный доступ) - librarian - Библиотекарь (операции с книгами) - reader - Читатель (личный кабинет)
Интеграции¶
LDAP¶
Опциональная интеграция с LDAP/Active Directory для: - Аутентификации пользователей - Автоматического создания профилей - Извлечения информации о группах студентов
Redis¶
Используется для кэширования справочных данных (авторы, жанры, издательства, языки). Модуль server/utils/cache.js экспортирует get, set, invalidate и алиас del (равный invalidate) для совместимости с сервисами, вызывающими cache.del('authors') и т.п. При отсутствии Redis используется in-memory кэш.
QR-коды¶
- Генерация QR-кодов для быстрой выдачи/возврата
- Сканирование QR-кодов через веб-камеру
- Массовая генерация QR-кодов
Импорт книг из CSV¶
Оркестрация в ImportService (server/services/ImportService.js). Маршрут books/import.js принимает файл и вызывает ImportService.importFromCsv(csvContent).
Формат CSV: первая строка — заголовки (порядок колонок не важен, сопоставление по названию). Поддерживаемые колонки:
- Обязательные: Инвентарный номер, Название, Автор(ы) (несколько через
;) - Опциональные: ISBN, Год издания, Страниц, Издание, Язык, Издательство, Жанры (
;), Полка, Состояние, Статус (доступна/выдана), Цена покупки, Текущая стоимость, Описание
Поведение без дубликатов:
- Справочники (авторы, издательства, жанры, полки, языки) — поиск по названию; при отсутствии создаётся запись.
- Книга — поиск по связке название + ISBN + издательство; при отсутствии создаётся книга с авторами и жанрами, затем добавляется экземпляр.
- Экземпляр — по инвентарному номеру; при существующем номере строка пропускается, в ответ добавляется
skippedи запись вerrors.
Ответ API: { success, imported, skipped, errors } (количество успешно импортированных строк, пропущенных из‑за дубликата инв. номера, массив ошибок по строкам).
Паттерны проектирования¶
Repository Pattern¶
Все операции с БД инкапсулированы в репозиториях: - Абстракция от конкретной реализации БД - Переиспользование кода - Упрощение тестирования
CQRS (Command Query Responsibility Segregation): - OperationQueryRepository - Только чтение - OperationCommandRepository - Только запись - OperationRepository - Facade, объединяющий оба
Service Layer¶
Бизнес-логика вынесена в сервисы: - Разделение ответственности - Возможность переиспользования логики - Упрощение тестирования - Управление транзакциями - Сервисы создают и управляют транзакциями - Оркестрация операций - Координация между несколькими репозиториями
DTO Pattern¶
Использование DTO для: - Форматирования данных для API - Контроля структуры ответов - Преобразования внутренних моделей
Outbox Pattern¶
Асинхронная обработка событий: - Надежность (события не теряются) - Идемпотентность - Масштабируемость
Idempotency¶
Система поддерживает идемпотентность операций через:
- idempotency_keys таблицу
- Заголовок Idempotency-Key на критичных POST (бронирование, очередь AddToQueue, выдача и др.)
- Повторная обработка запросов без побочных эффектов
Transaction Management¶
Архитектурный принцип: Services own transactions, repositories never create them
- REPEATABLE READ - Уровень изоляции транзакций для критических операций
- SELECT FOR UPDATE - Row-level locking для предотвращения race conditions
- Транзакции создаются только в сервисах - Репозитории принимают транзакции через
options.transaction - Один сервисный метод = одна транзакция - Четкие границы транзакций
- Нет вложенных транзакций - Все транзакции явно управляются сервисами
- Атомарность операций - Все изменения в рамках одной транзакции
Пример:
// Service создает транзакцию
const transaction = await createTransaction('REPEATABLE READ');
try {
// Передает транзакцию в репозиторий
const result = await Repository.method(data, { transaction });
await transaction.commit();
return result;
} catch (error) {
if (transaction && !transaction.finished) {
await transaction.rollback();
}
throw error;
}
Обработка ошибок¶
Frontend¶
- ErrorBoundary — перехват ошибок React
- ErrorState / EmptyState — разделение ошибки загрузки и пустого списка (каталог, «Мои выдачи», «Мои бронирования») с кнопкой повтора
- Toast уведомления — пользовательские уведомления об ошибках
- Валидация форм — Zod схемы для валидации
Backend¶
DomainError Pattern - Структурированная обработка ошибок:
- DomainError класс - Базовый класс для доменных ошибок
errorCode- Код ошибки (например,BOOK_NOT_FOUND)message- Сообщение об ошибкеstatusCode- HTTP статус код-
details- Дополнительные метаданные -
ERROR_CODES - Стандартизированные коды ошибок:
BOOK_NOT_FOUND,BOOK_COPY_NOT_FOUNDUSER_NOT_FOUND,USER_INACTIVE,USER_LIMIT_EXCEEDEDOPERATION_NOT_FOUND,OPERATION_TYPE_NOT_CONFIGUREDBOOKING_NOT_FOUND,BOOKING_EXPIRED,BOOKING_LIMIT_EXCEEDEDVALIDATION_ERROR,CONFIGURATION_ERROR-
И другие...
-
createDomainError() - Фабрика для создания ошибок с автоматическим маппингом HTTP статусов
-
Централизованная обработка:
errorHandler.js- Middleware для обработки ошибокasyncHandler.js- Обработка асинхронных ошибок с requestId- Автоматическое логирование с контекстом
-
Корректные HTTP статус коды
-
Транзакции - Автоматический откат изменений при ошибках
Производительность¶
Оптимизации¶
- Индексы БД - Оптимизация запросов
- Кэширование - Redis для справочных данных
- Lazy loading - Ленивая загрузка компонентов
- Pagination - Пагинация больших списков
- Debounce - Оптимизация поиска
- Batch operations - Массовые операции
Мониторинг¶
- Winston логи - Структурированное логирование
- Request ID - Трейсинг запросов
- Метрики производительности - Логирование времени выполнения
Развертывание¶
Docker¶
Проект включает Docker конфигурации:
- Dockerfile - Для frontend
- server/Dockerfile - Для backend
- docker-compose.dev.yml — PostgreSQL для локальной разработки
- docker-compose.yml — production (backend, frontend, nginx)
- docker-compose.ssl.yml — дополнение для SSL в Docker
- См. DOCKER.md
Nginx¶
Конфигурация Nginx для: - Раздачи статических файлов frontend - Проксирования API запросов - SSL/TLS терминации
Скрипты¶
Вспомогательные скрипты (server/scripts/):
- outboxWorker.js - Встроенная обработка очереди (env OUTBOX_WORKER_*)
- process-outbox-events.js - Ручной/cron прогон outbox
- reconcile-outbox-stuck.js - Разовая очистка зависших событий
- cleanup-expired-bookings.js - Отмена просроченных броней (cron)
- setup-test-db.js - Настройка тестовой БД
- validate-migrations.js - Валидация миграций
- check-constraint-violations.js - Проверка нарушений ограничений
Тестирование¶
Frontend¶
- TypeScript для статической проверки типов
- ESLint для проверки кода
Backend¶
- Jest - Фреймворк для тестирования
- Supertest - Тестирование HTTP endpoints
- Unit тесты в
server/__tests__/unit/ - Integration тесты в
server/__tests__/integration/
Документация API¶
Swagger¶
API документация доступна по адресу /api-docs (в dev режиме или при ENABLE_SWAGGER=true)
Масштабируемость¶
Система спроектирована с учетом масштабируемости: - Stateless backend - Можно горизонтально масштабировать - Кэширование - Для снижения нагрузки на БД - Асинхронная обработка - Через outbox pattern - Оптимизированные запросы - К БД - Транзакции - Для консистентности данных - Row-level locking - Для предотвращения race conditions
Ключевые особенности архитектуры¶
1. Разделение ответственности¶
- Routes - Обработка HTTP запросов, валидация входных данных
- Services - Бизнес-логика, управление транзакциями, оркестрация операций
- Repositories - Доступ к данным, НЕ создают транзакции
- Models - Структура данных, валидация на уровне модели
2. CQRS для операций¶
- Query Repository - Оптимизирован для чтения
- Command Repository - Оптимизирован для записи
- Facade Repository - Единый интерфейс
3. Асинхронная обработка¶
- Outbox Pattern - Надежная обработка событий
- Очередь бронирований - Автоматическое продвижение
- Cron задачи - Периодическая обработка
4. Надежность¶
- Транзакции - Атомарность операций, управление только в сервисах
- Row-level locking - Предотвращение race conditions (SELECT FOR UPDATE)
- Idempotency - Повторная обработка без побочных эффектов через idempotency_keys
- Валидация - На всех уровнях (Routes, Services, Models, Database)
- DomainError - Структурированная обработка ошибок с кодами и метаданными
- RequestId propagation - Трейсинг запросов через все слои
5. Производительность¶
- Индексы БД - Оптимизация запросов
- Кэширование - Redis для справочных данных
- Пагинация - Для больших списков
- Batch operations - Массовые операции
Поток данных¶
Выдача книги¶
Frontend (LoanDialog)
→ API POST /api/operations/loan
→ Route Handler (operations.js)
→ OperationService.createLoan()
→ LoanService.createLoan()
→ OperationRepository.createLoanWithBooking()
→ OperationCommandRepository.createLoanWithBooking()
→ Database (Transaction)
Возврат книги с обработкой очереди¶
Frontend (ReturnDialog)
→ API POST /api/operations/return
→ ReturnService.createReturn()
→ Database (Transaction) + OutboxEvent.create()
→ Database Commit
→ [Синхронно] BookingService.processQueueForBook()
→ triggerOutboxProcessing() [подстраховка для outbox-worker]
[Резерв]
→ outbox-worker / process-outbox-events.js
→ outboxProcessor.processOutboxBatch()
→ BookingService.processQueueForBook()
Отмена бронирования с обработкой очереди¶
Frontend (MyBookings / BookDetail)
→ API PUT /api/bookings/:id/cancel
→ BookingService.cancelBooking()
→ Database (Transaction) + OutboxEvent.create()
→ Database Commit
→ [Синхронно] BookingService.processQueueForBook()
Бронирование с очередью¶
Frontend (BookDetail)
→ API POST /api/bookings/queue [Idempotency-Key]
→ BookingService.addToQueue()
→ cancelExpiredPendingForCopy() [lazy expiration]
→ BookingQueueRepository.addToQueue()
→ Database (Transaction)
[Когда экземпляр освобождается]
→ processQueueForBook() (синхронно после возврата/отмены)
→ OutboxEvent — резерв для воркера
→ Создаётся бронирование для первого в очереди
Статистика кода¶
Backend¶
- Routes: ~15 файлов
- Services: ~10 файлов
- Repositories: ~22 файла (включая Query/Command разделение)
- Models: ~20 моделей
- Migrations: 39 миграций + init.sql
Frontend¶
- Pages: 19 страниц
- Components: ~89 компонентов
- Dialogs: ~30 диалогов
Заключение¶
Архитектура системы следует принципам: - Многослойная архитектура - Четкое разделение слоев - Repository Pattern - Абстракция доступа к данным - Service Layer - Бизнес-логика в сервисах - CQRS - Разделение чтения и записи - Outbox Pattern - Асинхронная обработка событий - Idempotency - Надежность операций - Transaction Safety - Консистентность данных
Система спроектирована для: - ✅ Масштабируемости - ✅ Надежности - ✅ Поддерживаемости - ✅ Производительности