Appearance
Partizap Backend — Отчёт о реализации
Дата: 05.02.2026 Этап: Инициализация скелета бэкенда Примечание: Этот отчёт описывает первоначальную конфигурацию на одном VPS (Debian 12, 185.73.215.246). С 10.03.2026 инфраструктура перенесена на два VPS (Ubuntu 24.04). Актуальная архитектура — в dev-prod-infrastructure.md.
1. Конфигурация сервера
Оборудование и ОС
| Параметр | Значение |
|---|---|
| ОС | Debian 12 (bookworm) |
| CPU | 6 ядер |
| RAM | 11 ГБ |
| Swap | 2 ГБ (/swapfile, swappiness=10) |
| IP | 185.73.215.246 |
Установленное ПО
| ПО | Версия | Примечания |
|---|---|---|
| PHP | 8.3.30 (репозиторий Sury) | Расширения: pdo_pgsql, redis, mbstring, xml, curl |
| Composer | 2.9.5 | /usr/local/bin/composer |
| PostgreSQL | 16.11 | Две базы: partizap_prod, partizap_dev |
| Redis | 7.0.15 | Один экземпляр, базы 0–3 |
| PgBouncer | 1.25.1 | Transaction mode, scram-sha-256 |
| Nginx | 1.22.1 | Все server-блоки в /etc/nginx/sites-available/default |
Пулы PHP-FPM
| Пул | Сокет | max_children |
|---|---|---|
partizap-prod | /run/php/php8.3-fpm-prod.sock | 20 |
partizap-dev | /run/php/php8.3-fpm-dev.sock | 5 |
Изоляция окружений
| Ресурс | Production | Development |
|---|---|---|
| Домен | partizap.ru | dev.partizap.ru |
| PostgreSQL | partizap_prod | partizap_dev |
| Redis DB | 0 (префикс: prod:) | 1 (префикс: dev:) |
| PHP-FPM пул | partizap-prod | partizap-dev |
| PgBouncer | порт 6432 | прямое подключение, порт 5432 |
| Каталог приложения | /var/www/partizap/production/ | /var/www/partizap/development/ |
Конфигурация Nginx
- HTTPS с сертификатами Let's Encrypt
try_files $uri /index.php$is_args$args— все запросы направляются в Slim- Basic auth на
dev.partizap.ru(файл/etc/nginx/.htpasswd) - Заголовки безопасности: X-Frame-Options, X-Content-Type-Options
- Блокировка:
.env,.git,composer.json,composer.lock - Исправление: Исходная конфигурация содержала
location ~ /(composer\.(json|lock)|vendor/), которая блокировала API-маршруты/vendor/*. Заменена наlocation ~ /composer\.(json|lock)$в обоих блоках (production и development).
Учётные данные
| Данные | Расположение |
|---|---|
| Пароли PostgreSQL | /var/www/partizap/{production,development}/.env |
| Userlist PgBouncer | /etc/pgbouncer/userlist.txt |
| Basic auth Nginx | /etc/nginx/.htpasswd |
2. Структура Git-репозиториев
Репозиторий бэкенда
Расположение: /var/www/partizap/ (сервер), git@gitlab.partizap.ru:team/partizap.git (remote)
Стратегия ветвления
| Ветка | Окружение | Домен | Назначение |
|---|---|---|---|
main | production | partizap.ru | Стабильные релизы |
develop | development | dev.partizap.ru | Активная разработка |
feature/* | только CI | — | Фича-ветки, сливаются в develop |
Содержимое репозитория (ветка develop)
/var/www/partizap/
├── .gitignore
├── development/ ← Приложение бэкенда (Slim 4)
│ ├── .env ← Реальные данные (в .gitignore)
│ ├── .env.example ← Шаблон
│ ├── .gitignore
│ ├── composer.json
│ ├── composer.lock ← (в .gitignore)
│ ├── vendor/ ← (в .gitignore)
│ ├── var/ ← (в .gitignore: кэш, прокси)
│ ├── public/index.php
│ ├── bin/console
│ ├── config/
│ ├── app/
│ ├── migrations/
│ ├── tests/
│ ├── phpstan.neon
│ ├── phpunit.xml
│ └── .php-cs-fixer.php
└── production/ ← Продакшен-деплой (только ветка main)
├── .env
├── .env.example
└── public/index.phpРепозиторий фронтенда
Ещё не создан. Будет отдельным Git-репозиторием (например, team/partizap-frontend), инициализируется при начале разработки фронтенда. Фронтенд запланирован как приложение на Nuxt 4, взаимодействующее с API бэкенда по HTTPS.
Подключение к GitLab
- Remote:
git@gitlab.partizap.ru:team/partizap.git - Аутентификация: по SSH-ключу
- Ключ хоста добавлен в
~/.ssh/known_hosts - Ветки
mainиdevelopзагружены в origin
3. Архитектура бэкенда
Стек технологий
- Фреймворк: Slim 4.15 + PHP-DI 7.1 (через
php-di/slim-bridge) - ORM: Doctrine ORM 3.6 + DBAL 4.4 + Migrations 3.9
- БД: PostgreSQL 16 (SERIAL PK, не UUID)
- Кэш/Сессии: Redis 7 через нативный
ext-redis - Логирование: Monolog 3.10
- CLI: Symfony Console 7.4
- Тестирование: PHPUnit 11.5
- Статический анализ: PHPStan 2.1 (уровень 8) + phpstan-doctrine
- Стиль кода: PHP-CS-Fixer 3.93
Структура каталогов
development/
├── public/index.php ← Точка входа (bootstrap Slim)
├── bin/console ← CLI (Doctrine Migrations)
├── config/
│ ├── app.php ← Загрузка .env, массив настроек
│ ├── container.php ← Сборка контейнера PHP-DI
│ ├── cors.php ← Настройки CORS
│ ├── doctrine.php ← Фабрика EntityManager
│ ├── redis.php ← Фабрика RedisConnectionFactory
│ ├── logging.php ← Фабрика Monolog
│ ├── middleware.php ← Регистрация middleware
│ ├── migrations.php ← Конфигурация Doctrine Migrations
│ └── routes.php ← 4 группы маршрутов
├── app/
│ ├── Actions/ ← Один класс на эндпоинт
│ │ ├── Store/ ← Публичный API покупателей
│ │ ├── Auth/ ← Аутентификация
│ │ ├── Vendor/ ← Эндпоинты продавцов (требуют авторизации)
│ │ └── Admin/ ← Административные эндпоинты (авторизация + админ)
│ ├── Application/
│ │ ├── Middleware/ ← 8 классов middleware
│ │ ├── Response/JsonResponder.php ← Стандартный билдер JSON-ответов
│ │ └── Exception/ ← Иерархия исключений (5 классов)
│ ├── Domain/
│ │ ├── Entity/User.php ← Doctrine-сущность с PHP 8 атрибутами
│ │ └── Repository/ ← Интерфейсы репозиториев
│ └── Infrastructure/
│ ├── Persistence/ ← Doctrine-реализации репозиториев
│ ├── Redis/ ← RedisConnectionFactory
│ └── Logging/ ← RequestIdGenerator
├── migrations/ ← Сгенерированные миграции Doctrine
├── tests/
│ ├── bootstrap.php
│ ├── Feature/HealthCheckTest.php
│ └── Unit/JsonResponderTest.php
└── var/
├── doctrine/proxies/ ← Прокси-классы Doctrine
└── cache/ ← Кэш компиляции DIКонфигурационный слой (8 файлов)
| Файл | Назначение |
|---|---|
config/app.php | Загрузка .env через phpdotenv, валидация обязательных переменных, возврат массива настроек |
config/cors.php | Разрешённые origins (partizap.ru, dev.partizap.ru, localhost:3000), методы, заголовки |
config/redis.php | Фабрика, возвращающая экземпляр RedisConnectionFactory |
config/doctrine.php | Фабрика, возвращающая EntityManagerInterface (метаданные через атрибуты, каталог прокси) |
config/logging.php | Фабрика, возвращающая LoggerInterface Monolog (файл + stderr) |
config/container.php | ContainerBuilder PHP-DI со всеми определениями сервисов |
config/middleware.php | Регистрация middleware на $app (порядок: ErrorHandler → CORS → Security → JSON → Routing) |
config/routes.php | Регистрация 4 групп маршрутов с привязкой middleware |
Инфраструктурный слой (3 класса)
RedisConnectionFactory — Создаёт экземпляры \Redis для 4 логических баз:
| Константа | БД | Назначение |
|---|---|---|
DB_SESSIONS | 0 | PHP-сессии (критичные данные) |
DB_CACHE | 1 | Кэш приложения (можно сбросить) |
DB_QUEUES | 2 | Транспорт Symfony Messenger |
DB_RATE_LIMITS | 3 | Счётчики rate limiting |
Соединения создаются лениво и кэшируются по номеру базы. Префикс из .env (dev: или prod:).
DoctrineUserRepository — Реализация UserRepositoryInterface с методами findById(), findByEmail(), save().
RequestIdGenerator — Генерация 16-символьных hex-идентификаторов запросов через random_bytes(8).
Прикладной слой
Иерархия исключений
| Класс | HTTP | Код ошибки | Применение |
|---|---|---|---|
AppException | (базовый) | (настраиваемый) | Абстрактный базовый класс |
NotFoundException | 404 | not_found | Ресурс не найден |
ValidationException | 422 | validation_error | Ошибки валидации (содержит ошибки полей) |
AuthenticationException | 401 | authentication_required | Отсутствует/невалидная сессия |
AuthorizationException | 403 | access_denied | Недостаточно прав |
JsonResponder
Стандартизированный билдер JSON-ответов с двумя методами:
respond($data, $status, $meta)→{"data": ..., "meta": {...}}error($code, $message, $status, $details)→{"error": {"code": ..., "message": ..., "details": ...}}
Middleware (8 классов)
| Middleware | Позиция | Поведение |
|---|---|---|
ErrorHandlerMiddleware | Внешний | Перехватывает все исключения → JSON-ответы, логирует ошибки |
CorsMiddleware | 2-й | Добавляет CORS-заголовки, обрабатывает OPTIONS preflight (204) |
SecurityHeadersMiddleware | 3-й | Добавляет X-Content-Type-Options, X-Frame-Options, Referrer-Policy |
JsonContentTypeMiddleware | 4-й | Отклоняет POST/PUT/PATCH без application/json Content-Type (415) |
CsrfMiddleware | Заглушка | Плейсхолдер для будущей CSRF-валидации |
RateLimitMiddleware | Заглушка | Плейсхолдер для будущего rate limiting через Redis |
AuthMiddleware | Группа маршрутов | Проверяет $_SESSION['user_id'], выбрасывает AuthenticationException |
AdminMiddleware | Группа маршрутов | Проверяет $_SESSION['is_admin'], выбрасывает AuthorizationException |
Доменный слой
Сущность User — Doctrine ORM сущность, маппинг на таблицу users:
| Колонка | Тип | Примечания |
|---|---|---|
id | SERIAL (INT IDENTITY) | Автогенерируемый PK |
email | VARCHAR(255) | Уникальный индекс |
password_hash | VARCHAR(255) | Хэш bcrypt |
display_name | VARCHAR(100) | |
phone | VARCHAR(20) | Nullable |
is_active | BOOLEAN | По умолчанию true |
is_admin | BOOLEAN | По умолчанию false |
created_at | TIMESTAMP | Immutable, устанавливается при создании |
updated_at | TIMESTAMP | Immutable, обновляется через @PreUpdate |
Группы маршрутов и экшены (12 эндпоинтов)
| Группа | Маршрут | Метод | Экшен | Статус |
|---|---|---|---|---|
| — | /health | GET | HealthCheckAction | Работает (проверка БД + Redis) |
/store | /store/products | GET | ListProductsAction | Заглушка |
/store | /store/products/{id} | GET | GetProductAction | Заглушка (404) |
/store | /store/categories | GET | ListCategoriesAction | Заглушка |
/auth | /auth/login | POST | LoginAction | Заглушка (501) |
/auth | /auth/register | POST | RegisterAction | Заглушка (501) |
/auth | /auth/logout | POST | LogoutAction | Заглушка (501) |
/auth | /auth/me | GET | MeAction | Заглушка (501) |
/vendor | /vendor/products | GET | ListMyProductsAction | Заглушка (требует авторизации) |
/vendor | /vendor/products | POST | CreateProductAction | Заглушка (требует авторизации) |
/admin | /admin/dashboard | GET | DashboardAction | Заглушка (требует авторизации + админ) |
/admin | /admin/users | GET | ListUsersAction | Заглушка (требует авторизации + админ) |
Точки входа
public/index.php — Последовательность инициализации Slim 4:
- Загрузка автозагрузчика Composer
- Загрузка настроек из
config/app.php(который загружает.env) - Сборка контейнера PHP-DI из
config/container.php - Создание приложения Slim через
DI\Bridge\Slim\Bridge::create() - Регистрация middleware из
config/middleware.php - Регистрация маршрутов из
config/routes.php $app->run()
bin/console — Symfony Console с командами Doctrine Migrations:
- Использует
DependencyFactory::fromEntityManager()с провайдеромExistingEntityManager - Команды зарегистрированы с префиксом
migrations:(неdoctrine:migrations:) - Доступные команды:
diff,migrate,generate,latest,list,status,version,execute,rollup
Инструменты
| Инструмент | Конфигурация | Статус |
|---|---|---|
| PHPUnit 11.5 | phpunit.xml | 8 тестов, 20 ассертов — все проходят |
| PHPStan 2.1 | phpstan.neon (уровень 8) | 0 ошибок |
| PHP-CS-Fixer 3.93 | .php-cs-fixer.php (набор правил PER-CS) | Настроен |
Миграция базы данных
Первая миграция Version20260205195100 сгенерирована и применена:
- Создаёт таблицу
usersсо всеми колонками - Создаёт уникальный индекс на
email - Таблица трекинга миграций:
doctrine_migration_versions
4. Стратегия интеграции фронтенда и бэкенда
Фронтенд (Nuxt 4) ещё не реализован. Этот раздел описывает, как бэкенд подготовлен к интеграции с фронтендом и планируемую архитектуру.
Структура репозиториев
Планируются два отдельных Git-репозитория:
| Репозиторий | Стек | Стратегия ветвления |
|---|---|---|
team/partizap | Бэкенд (Slim 4) | main → production, develop → development |
team/partizap-frontend (планируется) | Фронтенд (Nuxt 4) | main → production, develop → development |
Репозиторий фронтенда будет создан при начале разработки фронтенда.
Готовность API бэкенда
Бэкенд предоставляет JSON REST API, готовый к потреблению фронтендом:
Фронтенд (Nuxt 4) → HTTPS → Nginx → PHP-FPM → Slim 4 APIФормат запросов:
- Content-Type:
application/json - CSRF: заголовок
X-CSRF-TOKEN(значение из cookieCSRF_TOKEN) - SSR: Nitro будет проксировать cookie браузера через
useRequestHeaders(['cookie'])
Формат ответов:
- Успех:
{"data": T, "meta": {"has_more": bool, "next_cursor": string|null}} - Ошибка:
{"error": {"code": string, "message": string, "details?": object}}
Конфигурация CORS
Уже настроена в config/cors.php для приёма запросов от фронтенда:
Разрешённые origins: partizap.ru, dev.partizap.ru, localhost:3000
Разрешённые методы: GET, POST, PUT, PATCH, DELETE, OPTIONS
Credentials: включены (для session cookies)Планируемый рабочий процесс разработки
После инициализации фронтенда:
- Бэкенд работает на
dev.partizap.ruчерез Nginx + PHP-FPM (всегда активен на сервере) - Фронтенд запускается на
localhost:3000черезnpm run dev(Nuxt dev-сервер, на машине разработчика) - Обёртка
$fetchфронтенда отправляет API-запросы наdev.partizap.ru - CORS разрешает origin
localhost:3000
Деплой бэкенда
Development (ветка develop)
git push origin develop
↓
GitLab CI (опционально: тесты, PHPStan)
↓
Деплой в /var/www/partizap/development/
↓
composer install --optimize-autoloader
php bin/console migrations:migrate --no-interaction
↓
Доступно на dev.partizap.ru (за basic auth)Production (ветка main)
git checkout main
git merge develop
git push origin main
↓
GitLab CI (полный набор тестов)
↓
Деплой в /var/www/partizap/production/
↓
composer install --no-dev --optimize-autoloader
php bin/console migrations:migrate --no-interaction
↓
Доступно на partizap.ruСтратегия тестирования
| Уровень | Инструмент | Статус | Что тестируется |
|---|---|---|---|
| Unit бэкенд | PHPUnit | Реализовано | JsonResponder, сервисы, валидаторы, доменная логика |
| Feature бэкенд | PHPUnit | Реализовано | Классы экшенов с замоканными зависимостями |
| Статический анализ бэкенд | PHPStan уровень 8 | Реализовано | Типобезопасность всей кодовой базы |
| Unit фронтенд | Vitest | Планируется | Composables, логика хранилищ, утилиты |
| Компонентные фронтенд | @nuxt/test-utils | Планируется | Vue-компоненты с мок-API |
| E2E фронтенд | TBD | Планируется | Полные пользовательские сценарии на dev.partizap.ru |
Контракты API
Контракты API задокументированы в каталоге docs проекта:
docs/autoparts-contracts-v5.md— Связи сущностей и модели данныхdocs/partizap_mvp-design.md— Полная спецификация API с форматами запросов/ответовdocs/partizap_DB-structure.json— Схема базы данных (27 таблиц)
При реализации фронтенда Zod-схемы и правила валидации бэкенда должны строиться на основе этих контрактов для синхронизации.
5. Известные проблемы и замечания
| Проблема | Подробности |
|---|---|
| Префикс CLI Doctrine | Команды: migrations:diff, не doctrine:migrations:diff (автономный DependencyFactory) |
Мокирование final-классов | RedisConnectionFactory изменён с final на обычный класс для тестируемости в PHPUnit |
https_proxy на сервере | Используйте --noproxy '*' с curl для локального тестирования |
| Несоответствие в документации | dev-prod-infrastructure.md ссылается на PHP 8.2, реально установлен 8.3 |
| Nginx production | Правило блокировки vendor/ исправлено (блокировало API-маршруты /vendor/*) |
| Воркеры очередей | systemd-сервисы включены, но НЕ запущены (нет задач в очереди) |
| Middleware-заглушки | CsrfMiddleware и RateLimitMiddleware — pass-through, будут реализованы позже |