# Архитектура PhotoPlaces — Обзор (MOC) **Контекст**: Карта архитектуры и технического долга проекта PhotoPlaces. Используется для планирования рефакторинга и оценки жизнеспособности. **Статус**: Заметка актуализирована 2026-06-24 после code review. Предыдущая версия содержала устаревшие P0, которые уже исправлены в коде. ## Суть Проект — fullstack приложение (Next.js + Go) для карты мест фотосъёмок, аренды студий и найма фотографов. Использует PostgreSQL + PostGIS, Redis, MinIO. Развертывание через Docker Compose. ## Основные компоненты ### Backend (Go 1.22) - **Router**: Chi v5 - **DB**: pgx/v5 (PostgreSQL + PostGIS) - **Auth**: JWT (access 15min + refresh 30d, SHA-256 hash в БД, ротация) - **Rate Limiting**: Redis (sliding window) + in-memory fallback - **Validation**: go-playground/validator + кастомные правила - **Architecture**: Layered (handlers → services → repository) - **Migrations**: golang-migrate (7 миграций) ### Frontend (Next.js 14 App Router) - **State**: React Context (AuthProvider) - **API**: Custom fetch wrapper с auto-refresh токена - **Maps**: Leaflet (OSM dark tiles via CartoDB) - **Styling**: Tailwind CSS - **Testing**: Vitest + @testing-library/react ### Infrastructure - **Local**: `docker-compose.yml` (postgres, redis, minio, backend, frontend) - **Prod**: `deploy/docker-compose.prod.yml` + Caddy reverse proxy - **CI/CD**: GitHub Actions (dependabot.yml) ```mermaid 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 ``` ## Что было исправлено с момента первой версии Obsidian-заметок | Проблема (из старой заметки) | Статус | Где исправлено | |---|---|---| | 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` | ## Актуальные проблемы (на 2026-06) ### P0 — Критические | Проблема | Файл | Описание | |---|---|---| | **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]` | ### P1 — Высокие | Проблема | Файл | Описание | |---|---|---| | 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` без проверки ошибки | ### P2 — Умеренные | Проблема | Описание | |---|---| | 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` | ## Архитектурные решения (Decision Records) - [[decision-jwt-refresh-storage]] — Почему refresh tokens должны храниться в БД - [[decision-rate-limiter-redis]] — Redis-based rate limiting с fallback - [[decision-validation-library]] — go-playground/validator - [[decision-validation-error-production]] — Скрытие деталей ошибок в production - [[decision-test-strategy]] — Стратегия тестирования ## Связанные заметки - [[backend-auth-security]] — JWT + refresh token реализация - [[backend-rate-limiting]] — Rate limiting (Redis + in-memory) - [[backend-validation]] — Валидация - [[frontend-api-client]] — API client на фронте - [[deploy-production-readiness]] — Production readiness - [[database-migrations]] — Схема миграций - [[atomic-global-appenv-package-var]] — Проблема глобальной переменной - [[atomic-toctou-race-registration]] — TOCTOU race - [[atomic-map-coordinates-inconsistency]] — Координаты карты - [[atomic-error-swallowing-frontend]] — Подавление ошибок - [[atomic-redis-rate-limiter-failopen-failclosed]] — Fail-open/closed - [[atomic-csp-hardening]] — CSP hardening ## Теги #architecture #backend #frontend #security #technical-debt #moc