Контекст: Карта архитектуры и технического долга проекта PhotoPlaces. Используется для планирования рефакторинга и оценки жизнеспособности.
Статус: Заметка актуализирована 2026-06-24 после code review. Предыдущая версия содержала устаревшие P0, которые уже исправлены в коде.
Проект — fullstack приложение (Next.js + Go) для карты мест фотосъёмок, аренды студий и найма фотографов. Использует PostgreSQL + PostGIS, Redis, MinIO. Развертывание через Docker Compose.
docker-compose.yml (postgres, redis, minio, backend, frontend)deploy/docker-compose.prod.yml + Caddy reverse proxyCI/CD: GitHub Actions (dependabot.yml)
graph TB
FB[Frontend Next.js 14] --> API[Go API :8080]
API --> PG[PostgreSQL + PostGIS]
API --> RD[Redis]
API --> S3[MinIO / S3]
FB --> FBStatic[Next.js Static]
CADDY[Caddy] --> FB
CADDY --> API
CADDY --> S3
| Проблема (из старой заметки) | Статус | Где исправлено |
|---|---|---|
| Refresh tokens не хранятся в БД | ✅ Исправлено | services/auth.go — SHA-256, ротация, DeleteIfExists |
| Rate limiter только in-memory | ✅ Исправлено | main.go — Redis + fallback, fail-open/closed |
| Нет валидации входных данных | ✅ Исправлено | validator/validator.go — go-playground/validator |
| Нет тестов | ✅ Частично | auth_test.go, places_test.go, place_repo_test.go |
| Проблема | Файл | Описание |
|---|---|---|
| Package-level mutable state | handlers/errors.go:13 |
var AppEnv string — глобальная переменная, невозможно тестировать хендлеры |
| TOCTOU race при регистрации | services/auth.go:85-86 |
GetByEmail → Create без транзакции, ошибка подавлена через _ |
| Mutex без defer | middleware/ratelimit.go:61 |
rl.mu.Lock() без defer Unlock() — deadlock при панике |
| Координаты карты: [lng,lat] vs [lat,lng] | lib/map.ts |
Интерфейс init() принимает [lng, lat], setCenter() — [lat, lng] |
| Проблема | Файл | Описание |
|---|---|---|
| N+1 запросы в GetByID | services/places.go:112-136 |
3 отдельных запроса вместо JOIN |
| Лишние запросы при пагинации | services/places.go:148-168 |
Tags/features подгружаются для отбрасываемого limit+1 элемента |
| Подавление ошибок API на фронте | components/PlaceForm.tsx:42-43 |
.catch(() => {}) — пользователь не видит ошибок загрузки тегов |
| Retry loop на /auth/logout | lib/api.ts:71 |
!path.includes('/auth/') не исключает /auth/logout |
| Хрупкий парсинг bounds | handlers/places.go:55-59 |
fmt.Sscanf без проверки ошибки |
| Проблема | Описание |
|---|---|
| ErrorBoundary молчит в production | console.debug в проде не вызывается — ошибки не логируются |
| Полная перерисовка маркеров | Все маркеры удаляются и создаются заново при каждом изменении places |
| node_modules volume в Go backend | docker-compose.yml:88 — не имеет смысла |
CSP с unsafe-inline и unsafe-eval |
Caddyfile — ослабляет XSS-защиту |
Мало линтеров в .golangci.yml |
Нет gosec, bodyclose, gocritic, revive |
#architecture #backend #frontend #security #technical-debt #moc