Архитектура проекта

C4 Component — компоненты API

C4Component
  title Component diagram — Photo API

  Container_Boundary(api, "Photo API") {
    Component(routes, "API Routes", "FastAPI routers")
    Component(auth, "Auth Service", "VKAuthService")
    Component(photos, "Photo Service", "PhotoService")
    Component(groups, "Group Service", "GroupService")
    Component(db, "Database Layer", "SQLAlchemy async")
    Component(storage, "Storage Layer", "MinIOStorage")
    Component(kafka_prod, "Kafka Producer", "aiokafka")
    Component(metrics, "Metrics", "Prometheus client")
    Component(errors, "Error Handler", "AppException")
    Component(config, "Config", "pydantic-settings")
  }

  Rel(client, routes, "HTTP")
  Rel(routes, auth, "call")
  Rel(routes, photos, "call")
  Rel(routes, groups, "call")
  Rel(photos, db, "SQLAlchemy")
  Rel(photos, storage, "MinIO SDK")
  Rel(photos, kafka_prod, "aiokafka")
  Rel(groups, db, "SQLAlchemy")
  Rel(auth, db, "SQLAlchemy")
  Rel(routes, metrics, "record")

ER Diagram — база данных

erDiagram
  tokens {
    string data_token PK
    string auth_token UK
    string vk_owner_user_id
    datetime created_at_time
    datetime expires_at
  }
  groups {
    int id PK
    string id_string UK
    string owner_data_token FK
    datetime created_at
    bool is_identity_group
    bool is_private
    string standart_hash
  }
  photos {
    int id PK
    string id_string UK
    string object_key
    string preview_key
    datetime load_time
    bigint photo_size
    enum status
    int attempts
    int last_error_code
    string last_error_message
    bool is_private
    string owner_data_token FK
    int duplicate_group_id FK
    int identity_photo_group_id FK
  }
  photo_analysis {
    int photo_id PK, FK
    int faces_count
    int eyes_closed_count
    bool is_blurred
    float blur_score
    int quality_metric
    int light_level
    string dominant_color
    string tags
    string model_version
    string perceptual_hash
    string sha256_hash
    datetime analysis_at
  }

Sequence — поток загрузки и анализа фото

Пользователь отправляет файл → API валидирует и сохраняет в MinIO → публикует событие в Kafka → Worker потребляет событие, читает из MinIO, вызывает gRPC анализатор → сохраняет результаты в БД, проверяет дубликаты.

Поток загрузки фото

  1. Клиент отправляет файл через POST /api/v1/photos/
  2. API валидирует: тип (image/jpeg, image/png), размер (≤3MB), magic bytes
  3. API сохраняет запись в PostgreSQL со статусом uploading
  4. API загружает файл в MinIO по ключу photos/{id}/original.{ext}
  5. API обновляет статус на pending
  6. API публикует событие в Kafka (топик photo-uploaded)
  7. Клиенту возвращается 202 Accepted

Анализ фото (Worker)

  1. Worker потребляет событие из Kafka
  2. Worker захватывает фото (optimistic lock)
  3. Worker читает оригинал из MinIO
  4. Worker вызывает внешний gRPC анализатор: детекция лиц, оценка размытия, перцептивный хеш, доминантный цвет, теги
  5. Worker вычисляет SHA256 и перцептивный хеш
  6. Worker генерирует preview (JPEG, max 800px width)
  7. Worker загружает preview в MinIO
  8. Worker сохраняет результаты анализа в БД
  9. Worker проверяет дубликаты (перцептивный хеш, расстояние Хэмминга < 20) и идентичные фото (SHA256)