23 KiB
title
| title |
|---|
| Backend — внешние сервисы и стратегия для test/stage/prod |
Backend: внешние сервисы, БД и интеграции
Разбор всех сторонних зависимостей
apps/backend: что они делают, где используются в коде, и что делать в тестовом контуре (K8s) — поднять свой инстанс, поставить заглушку или эмулятор.Связанные документы: K8s + Terraform + ArgoCD + Gitea, локальный контур (
local/, см. корень репозитория), бизнес-сценарии.
0. Сводная матрица решений (test-контур)
| # | Зависимость | Тип | Где в коде | Test-контур | Stage | Prod |
|---|---|---|---|---|---|---|
| 1 | PostgreSQL (основная) | БД | DATABASE_URL, Doctrine ORM |
Новый инстанс (1×) | 1× на stage-сервере | HA: primary + sync replica (§7.6 плана) |
| 2 | PostgreSQL (cabinet) | БД | DATABASE_CABINET_URL, UsrlogController |
Новый инстанс + seed | Отдельный | Prod |
| 3 | MySQL (Bitrix CMS) | БД | DATABASE_BITRIX_URL, BitrixService, SQL views |
Новый инстанс + seed из local/mysql-bitrix |
Отдельный | Prod Bitrix |
| 4 | Redis | Кеш/сессии | REDIS_URL |
Новый инстанс в K8s | Отдельный | Prod Redis |
| 5 | MIS / Инфоклиника (HTTP) | API | MIS_URL, InfoclinicaClientService |
Mock-сервис (WireMock / свой stub) | Test MIS или read-only prod | Prod MIS |
| 6 | Widget API (widget.sovamed.ru) |
API | Hardcoded в Upload*Command | Mock или MIS_URL после рефакторинга |
Stage widget | Prod |
| 7 | Bitrix site (HTTP, картинки) | API | BITRIX_URL, BitrixClientService |
Mock (static files) или seed URLs | Stage | Prod |
| 8 | SMS (sms.ru, sms4b) | API | SMSRU_*, SMS4B_* |
Заглушка (noop) | Заглушка | Реальный провайдер |
| 9 | Почта | API | MAILER_DSN, SendMailService |
Mailpit / null://null | Mailpit или sandbox SMTP | Prod SMTP |
| 10 | Yandex SmartCaptcha | API | SMARTCAPTCHA_*, ServiceController |
Заглушка (always OK) | Заглушка | Реальный ключ |
| 11 | Calltouch | API | CT_*, CalltouchController |
Заглушка (noop, уже частично) | Заглушка | Реальный API |
| 12 | Bitrix24 | — | BITRIX24_URL в .env |
Не используется в коде — игнор | — | При появлении интеграции |
| 13 | JWT / AES / Lock | Локально | env + файлы ключей | Свои ключи test | Свои | Prod secrets |
| 14 | Symfony Messenger | Очередь | MESSENGER_TRANSPORT_DSN |
doctrine:// или sync:// |
doctrine/redis | prod queue |
flowchart TB
subgraph backend [backend pod]
API[Symfony API]
CMD[Console / CronJobs]
end
subgraph own_test [Поднимаем в test — свои инстансы]
PG[(PostgreSQL main)]
PGC[(PostgreSQL cabinet)]
MY[(MySQL Bitrix)]
RD[(Redis)]
end
subgraph stubs [Заглушки в test]
MIS_M[Mock MIS / Widget API]
SMS_M[Noop SMS]
MAIL_M[Mailpit]
CAP_M[Mock SmartCaptcha]
CT_M[Noop Calltouch]
BTX_M[Mock Bitrix HTTP images]
end
subgraph prod_only [Только prod / stage по решению]
MIS_P[Реальная Инфоклиника]
SMS_P[sms.ru / sms4b]
CAP_P[Yandex SmartCaptcha]
CT_P[Calltouch]
end
API --> PG
API --> PGC
API --> MY
API --> RD
API --> MIS_M
API --> SMS_M
API --> MAIL_M
API --> CAP_M
API --> CT_M
API --> BTX_M
CMD --> PG
CMD --> MY
CMD --> MIS_M
MIS_P -.->|stage/prod| API
SMS_P -.->|prod| API
1. Базы данных (с БД — новый инстанс)
1.1. PostgreSQL — основная (DATABASE_URL)
Назначение: все Doctrine-сущности backend (User, Specialist, News, Schedule, …).
Где используется:
config/packages/doctrine.yaml→ connectiondefault- все Repository, миграции
apps/backend/migrations - кеш расписания (
Scheduleв таблице, не Redis) - Symfony Messenger при
MESSENGER_TRANSPORT_DSN=doctrine://default
Test: Helm Bitnami PostgreSQL / CNPG instances: 1 в sova-data-test.
Stage: один PG на отдельном stage-сервере.
Prod: Patroni (2 db-VM) + witness etcd на отдельной VM (не prod-app). Подключение: DATABASE_URL → PgBouncer :6432 (transaction, API); migrate Job и Messenger → :5432 (session). §7.6 плана.
Stage/Prod: отдельные credentials; никогда не указывать prod URL в test.
1.2. PostgreSQL — cabinet (DATABASE_CABINET_URL)
Назначение: read-only доступ к legacy-таблицам cabinet (не Doctrine entities backend).
Где используется:
config/packages/doctrine.yaml→ connectioncabinetUsrlogController—SELECTизpublic.usrlogчерезdoctrine.dbal.cabinet_connection
Test: отдельный PostgreSQL (можно второй database на том же chart), seed из local/postgres/init/03-cabinet-schema-and-seed.sql.
Если usrlog не нужен в test: connection можно оставить на пустую БД; эндпоинт /usrlog/list вернёт пустой список.
1.3. MySQL — Bitrix CMS (DATABASE_BITRIX_URL)
Назначение: чтение таблиц Bitrix (b_iblock_*, b_file) для синхронизации контента и отзывов.
Где используется:
| Компонент | Как |
|---|---|
BitrixService |
прямые SQL через doctrine.dbal.mysql_connection |
BitrixUpdateReviewsCommand |
getReviews() — отзывы по specialist.id |
BitrixUpdateDoctorsCommand |
нормализация dcodes (Bitrix HTTP закомментирован) |
*CrudService::syncFromView* |
INSERT … SELECT FROM public.view_* |
| Миграции | views view_article, view_news, … с JOIN на b_* (через mysql_fdw на prod или упрощённые views в test) |
Test: новый MySQL в K8s (Bitnami MySQL chart), init SQL из local/mysql-bitrix/init/ в корне репозитория. Дополнительно — создать упрощённые view_news, view_promo, … или наполнять таблицы news/promo через CRUD API без sync-команд.
Важно: на prod PG использует mysql_fdw (infrastructure/pgsql + extensions). В test можно:
- Вариант A (рекомендуется): mysql_fdw в test-PG → test-MySQL (повторяет prod);
- Вариант B: отключить sync-команды в test CronJobs, контент только через adminPanel CRUD;
- Вариант C: materialized views только на PG без FDW (дублировать seed в PG).
1.4. Redis (REDIS_URL)
Назначение: Symfony cache, sessions (prod php.ini), потенциально Messenger.
Test: Bitnami Redis в sova-data-test, свой пароль в SealedSecret.
2. HTTP-интеграции
2.1. MIS / Инфоклиника (MIS_URL → InfoclinicaClientService)
Prod URL (пример): https://widget.sovamed.ru
Эндпоинты клиента:
| Метод | Путь | Сценарий |
|---|---|---|
| GET | /api/reservation/intervals?{query} |
Расписание врача |
| GET | /filials/list |
Список филиалов (через client) |
| POST | /api/reservation/anonymous-reserve |
Анонимная запись |
Где вызывается:
GetScheduleMessageHandler→getSchedule()GetAnonymousReserveRequestMessageHandler→anonymousReserve()UploadDoctorsCommand,UploadDepartmentsCommand→$client->request('GET', …)черезInfoclinicaClientServiceInterface
Test — рекомендация: Mock-сервис в K8s
Развернуть WireMock или лёгкий Node/Go stub в namespace sova-mocks:
MIS_URL=http://mis-mock.sova-mocks.svc.cluster.local
Stub должен отдавать JSON в формате, который ждёт InfoclinicaClientService::normalizeSchedule() (см. schedule-cache.md).
Альтернативы:
| Вариант | Когда |
|---|---|
| WireMock + JSON fixtures | Полный контроль, воспроизводимые тесты |
| Отдельный «test MIS» у вендора | Если Infoclinica даёт sandbox (уточнить у владельца интеграции) |
| Read-only прокси на prod | Не рекомендуется — риск записи/нагрузки |
Stage: sandbox MIS или изолированный контур клиники.
2.2. Widget API (hardcoded https://widget.sovamed.ru)
Проблема: часть команд не читает MIS_URL, а хардкодит URL:
| Команда | Путь |
|---|---|
UploadFilialsCommand |
GET /filials/list |
UploadPriceCommand |
GET /pricelist/list |
UploadPriceDepCommand |
GET /pricelist/departments |
Test:
- Краткосрочно: тот же mock-сервис, что для MIS, + рефакторинг команд — вынести
base_uriв envWIDGET_API_URLили переиспользоватьMIS_URL. - До рефакторинга: mock должен слушать на URL, который прописан в коде (или патч через
/etc/hosts+ Ingress) — технический долг.
2.3. Bitrix site HTTP (BITRIX_URL → BitrixClientService)
Prod URL (пример): https://sovamed.ru
Назначение: скачивание файлов врачей с путей вида /upload/iblock/....
Где используется:
GetSpecialistPictureMessageHandler→getSpecialistImage($path)
Test:
| Вариант | Описание |
|---|---|
| Mock HTTP | nginx/static с несколькими .jpg в /upload/iblock/ |
| Отключить handler | Если в test seed картинки уже локальные (specialist/xxx.jpg) |
| Прокси на stage Bitrix | Только read, без записи |
BITRIX_URL=http://bitrix-mock.sova-mocks.svc.cluster.local
2.4. SMS — sms.ru и sms4b (SMSRU_*, SMS4B_*)
Клиенты: SmsruClientService, Sms4bClientService (реализуют SmsClientServiceInterface).
Статус в коде: сервисы зарегистрированы в config/services.yaml, но ни один контроллер/команда их пока не inject'ит. Сущности Record / AlertSms есть, логика отправки SMS не подключена к HTTP-сценариям.
Test (по вашему решению): заглушка
# Symfony: test env — noop implementation
App\Service\Client\Interfaces\SmsClientServiceInterface:
class: App\Service\Client\Stub\NoopSmsClientService
NoopSmsClientService пишет в log «SMS suppressed» и возвращает { "status": "ok", "stub": true }.
Prod: реальные SMSRU_URL / SMS4B_URL и токены из SealedSecret.
Stage: заглушка или отдельный «sandbox»-аккаунт провайдера с whitelist номеров.
2.5. Почта (MAILER_DSN, SendMailService)
Где используется:
ServiceController::sendmail— отправка по query-параметрам, защитаMAILER_ACCESS_TOKEN- Symfony
MailerInterface→SendMailService
Prod .env: MAILER_DSN=null://null (письма фактически не уходят, но endpoint есть).
Test — рекомендация:
| DSN | Зачем |
|---|---|
smtp://mailpit.sova-mocks:1025 |
UI на :8025, видно письма в браузере |
null://null |
Если endpoint /service/sendmail не тестируете |
Безопасность Mailpit Web UI:
Mailpit UI (:8025) не выставлять в интернет без защиты. В тестовых письмах могут быть ФИО, mock-пароли, токены сброса (иногда валидные и на stage).
# Ingress mailpit.test.sova.dev — только Basic Auth
metadata:
annotations:
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: mailpit-basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Mailpit test only"
Альтернатива: UI только cluster-internal (kubectl port-forward), SMTP :1025 — из sova-test по Cluster DNS без Ingress.
Stage: Mailpit или корпоративный sandbox SMTP.
Prod: реальный SMTP / API (SendGrid, Yandex 360, …).
2.6. Yandex SmartCaptcha (SMARTCAPTCHA_URL, SMARTCAPTCHA_KEY)
Где используется:
ServiceController::smartCaptcha→POST /smart-captchaAnonymousReserveRequestDto::$captcha— поле обязательно при записи (валидация на backend)
Test — заглушка:
Mock возвращает { "status": "ok", "message": "" } для любого token (как Yandex при успехе).
Или Symfony binding:
# when@test
App\Service\Client\Interfaces\SmartCaptchaClientServiceInterface:
class: App\Service\Client\Stub\AlwaysValidSmartCaptchaClientService
Prod: реальный https://smartcaptcha.yandexcloud.net + prod key.
Связь с MIS mock: при anonymous-reserve captcha проверяется на backend до вызова MIS; mock captcha достаточен для E2E test.
2.7. Calltouch (CT_URL, CT_PARAMS)
Где используется:
CalltouchController::createLead— вызовCalltouchClientService::requestCreateзакомментирован- Клиент готов:
POST /lead-service/v1/api/request/create
Test — заглушка:
- Оставить как сейчас (echo request) или
NoopCalltouchClientServiceвозвращает{ "data": { "leadId": "test-123" } }
Prod/Stage (маркетинг): реальные CT_URL + region tokens из CT_PARAMS.
2.8. Bitrix24 (BITRIX24_URL)
Статус: переменная есть в .env, использований в PHP-коде backend не найдено. Можно не задавать в test до появления интеграции.
3. Локальная инфраструктура (не внешние SaaS)
| Переменная | Назначение | Test |
|---|---|---|
JWT_* |
Lexik JWT auth | Сгенерировать отдельную пару ключей для test |
AES_SECRET_KEY |
AESCryptService (сущность Record) |
Случайный ключ 32 байта в Secret |
LOCK_DSN |
Symfony Lock | flock:// или postgresql://… advisory lock |
MESSENGER_TRANSPORT_DSN |
Очередь | doctrine://default (таблица messenger_messages) |
CORS_ALLOW_ORIGIN |
adminPanel origins | https://admin.test.sova.dev |
API_BASE_URL |
Self-reference / XML feeds | https://api.test.sova.dev |
MAILER_ACCESS_TOKEN |
Защита /service/sendmail |
Случайный UUID в Secret |
4. Эталон: что уже сделано локально (local/.env.local)
В apps/backend/.env.local для sova-local уже задан паттерн mock URL:
MIS_URL=http://mock-mis.local
BITRIX_URL=http://mock-bitrix.local
SMS4B_URL=http://mock-sms4b.local
MAILER_DSN=null://null
SMARTCAPTCHA_URL=http://mock-smartcaptcha.local
Для K8s test нужно не просто «мёртвые» hostname, а реально поднятые mock-поды + DNS внутри кластера (Service names).
5. Архитектура mock-слоя в Kubernetes (test)
flowchart LR
subgraph sova_test [namespace sova-test]
BE[backend]
end
subgraph sova_mocks [namespace sova-mocks]
MIS[mis-mock :8080]
BTX[bitrix-http-mock :8080]
CAP[captcha-mock :8080]
CT[calltouch-mock :8080]
MP[mailpit :8025]
end
subgraph sova_data [namespace sova-data-test]
PG[(PostgreSQL)]
MY[(MySQL Bitrix)]
RD[(Redis)]
end
BE --> PG
BE --> MY
BE --> RD
BE --> MIS
BE --> BTX
BE --> CAP
BE --> CT
BE --> MP
5.1. Рекомендуемые Helm releases (test)
| Release | Chart / образ | Назначение |
|---|---|---|
mis-mock |
WireMock / custom | расписание, запись, filials, pricelist |
bitrix-http-mock |
nginx + static | /upload/iblock/*.jpg |
captcha-mock |
tiny HTTP server | POST /validate → ok |
calltouch-mock |
WireMock | lead create |
mailpit |
axllent/mailpit | SMTP + Web UI (Basic Auth на Ingress) |
postgresql-test |
bitnami/postgresql | main + cabinet DBs |
mysql-bitrix-test |
bitnami/mysql | Bitrix tables seed |
redis-test |
bitnami/redis | cache |
Namespace sova-mocks общий для всех контуров test (не prod).
5.2. Env backend test (фрагмент SealedSecret)
DATABASE_URL: postgresql://sova_test:***@postgresql-test.sova-data-test:5432/sova_backend_test
DATABASE_CABINET_URL: postgresql://sova_test:***@postgresql-test.sova-data-test:5432/sova_cabinet_test
DATABASE_BITRIX_URL: mysql://bitrix_test:***@mysql-bitrix-test.sova-data-test:3306/sova_bitrix_test
REDIS_URL: redis://:***@redis-test.sova-data-test:6379/0
MIS_URL: http://mis-mock.sova-mocks.svc.cluster.local:8080
BITRIX_URL: http://bitrix-http-mock.sova-mocks.svc.cluster.local:8080
SMARTCAPTCHA_URL: http://captcha-mock.sova-mocks.svc.cluster.local:8080
SMARTCAPTCHA_KEY: test-key-not-used-by-mock
SMSRU_URL: http://noop.invalid
SMS4B_URL: http://noop.invalid
CT_URL: http://calltouch-mock.sova-mocks.svc.cluster.local:8080
MAILER_DSN: smtp://mailpit.sova-mocks.svc.cluster.local:1025
MAILER_ACCESS_TOKEN: "<uuid>"
CORS_ALLOW_ORIGIN: https://admin.test.sova.dev
При внедрении NoopSmsClientService URL sms можно не резолвить.
6. CronJobs и внешние вызовы
CronJob (из scripts/cron.*) |
Внешние зависимости | Test |
|---|---|---|
upload:deps, upload:doctors |
MIS HTTP | mock MIS или отключить |
upload:filials, upload:price* |
widget HTTP (hardcoded) | mock + рефакторинг URL |
bitrix-update-reviews |
MySQL Bitrix | test MySQL + seed |
bitrix-update-doctors |
только PG | OK |
upload:news, upload:promo, … |
PG views → MySQL | test MySQL + views или CRUD only |
ClearScheduleCacheCommand |
только PG | OK |
Рекомендация для первого выката test: включить CronJobs только для очистки кеша расписания; sync-команды — после готовности mock MIS и Bitrix MySQL.
concurrencyPolicy в K8s:
Для всех sync CronJobs обязательно:
spec:
concurrencyPolicy: Forbid # не запускать вторую копию, пока первая не завершилась
jobTemplate:
spec:
activeDeadlineSeconds: 3600
Без Forbid при зависшей БД bitrix-update-reviews через час стартует дубликат — параллельные job'ы начнут мешать друг другу.
7. Stage vs Prod — отличия от test
| Сервис | Test | Stage | Prod |
|---|---|---|---|
| PostgreSQL / Redis / Bitrix MySQL | Изолированные test DB | Изолированные stage DB | Production DB |
| MIS | Mock | Sandbox MIS или mock | Production MIS |
| SMS | Noop | Noop или sandbox | Live |
| Mailpit | Sandbox SMTP | Live | |
| SmartCaptcha | Mock | Mock | Live |
| Calltouch | Noop | Noop | Live |
| Bitrix HTTP (images) | Mock | Stage Bitrix site | Prod |
8. Технический долг (сделать до/во время миграции)
- Hardcoded
widget.sovamed.ruвUploadFilialsCommand,UploadPriceCommand,UploadPriceDepCommand→ envWIDGET_API_URLилиMIS_URL. - Hardcoded
https://api.sovamed.ruвXmlFeedGenerator*→ envAPI_PUBLIC_URL. - Реализовать или явно отключить SMS-клиенты (
NoopSmsClientService+ bind по env). - Calltouch — раскомментировать с env-флагом
CALLTOUCH_ENABLED=falseв test. - BITRIX24_URL — удалить из
.envили документировать при появлении кода. - Mock-сервисы — вынести JSON fixtures в репозиторий
sova-mocks(рядом сsova-deploy).
9. Чек-лист готовности интеграций test-контура
- PostgreSQL test: миграции backend применены
- MySQL Bitrix test: seed + (опционально) mysql_fdw views
- Redis test: ping из backend pod
- MIS mock:
GET /api/reservation/intervalsвозвращает валидный JSON - MIS mock:
POST /api/reservation/anonymous-reserve→ 200 - Captcha mock:
POST /validate→ ok - Mailpit: письмо из
/service/sendmailвидно в UI (Basic Auth или port-forward) - SMS: noop binding, приложение стартует без sms.ru
- Calltouch: lead endpoint не бьёт prod
- CORS: admin.test.sova.dev → api.test.sova.dev
- JWT keys: login работает
- CronJobs sync:
concurrencyPolicy: Forbid - CronJobs: только безопасные включены на первом выкате
10. Связанные файлы в репозитории
| Путь | Содержимое |
|---|---|
apps/backend/.env |
prod-like переменные (образец) |
apps/backend/.env.local |
local mock URLs |
apps/backend/config/services.yaml |
DI клиентов |
apps/backend/config/packages/doctrine.yaml |
3 DB connection |
apps/backend/src/Service/Client/* |
HTTP-клиенты |
apps/backend/src/Service/Bitrix/BitrixService.php |
MySQL Bitrix |
local/mysql-bitrix/init/ |
seed Bitrix tables |
local/postgres/init/ |
seed PG backend + cabinet |
monitoring/prometheus/prometheus.yml |
scrape Bitrix server 192.168.2.11 (prod-only) |
Версия 1.0. При изменении интеграций обновлять матрицу в §0 и env-фрагмент в §5.2.