architecture-overview.md 6.4 KB

Архитектура 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)

    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 GetByEmailCreate без транзакции, ошибка подавлена через _
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