Skip to content

Сервис-каталог организаций, CRUD приложение на FastAPI, Postgres, PostGIS, Pydantic, SQLAlchemy, alembic, DDD, CQRS, ELK

Notifications You must be signed in to change notification settings

chapppington/organization_catalog_ddd

Repository files navigation

Organization Catalog DDD

Coverage

REST API приложение для справочника организаций, зданий и видов деятельности, построенное на принципах Domain-Driven Design. Работа с геоданными через PostGIS

http://217.25.90.217:8000/api/docs - Live

📋 Описание

Приложение предоставляет API для управления каталогом организаций с поддержкой:

  • 🔍 Поиск организаций по зданию, виду деятельности, названию
  • 📍 Геопоиск по координатам (радиус и прямоугольная область)
  • 🌳 Иерархическая классификация видов деятельности (до 3 уровней вложенности)
  • 🏢 Управление зданиями с адресами и координатами
  • 🔐 Авторизация через JWT токены и API ключи
  • 👤 Управление пользователями (регистрация, аутентификация)
  • 🔑 Генерация API ключей для доступа к защищенным эндпоинтам

🛠 Стек технологий

Backend

  • Python 3.13 — язык программирования
  • FastAPI — веб-фреймворк для REST API
  • Pydantic — валидация данных и настройки
  • PostgreSQL - база данных
  • PostGIS — расширение PostgreSQL для геоданных
  • SQLAlchemy 2.0 — ORM для работы с БД
  • GeoAlchemy 2 - плагин для работы с PostGIS в SQLAlchemy
  • Alembic — миграции базы данных
  • asyncpg — асинхронный драйвер PostgreSQL

Инструменты разработки

  • pytest — тестирование
  • ruff — линтер и форматтер
  • isort — сортировка импортов
  • pre-commit — хуки для проверки кода
  • faker — генерация тестовых данных
  • bcrypt — хеширование паролей
  • authx — JWT аутентификация

Архитектурные паттерны

  • DDD (Domain-Driven Design) — проектирование на основе предметной области
  • CQRS — разделение команд и запросов через Mediator
  • Repository Pattern — абстракция работы с данными
  • Dependency Injection — через punq контейнер

Инфраструктура

  • Docker — контейнеризация
  • PostgreSQL — основная база данных
  • uvicorn — ASGI сервер
  • ELK Stack — сбор, обработка и визуализация логов
    • Elasticsearch — хранилище и поиск по логам
    • Logstash — сбор и обработка логов
    • Kibana — визуализация и анализ логов

Установка и запуск

  1. Клонируйте репозиторий:

    git clone <repository-url>
    cd organization_catalog_ddd
  2. Настройте переменные окружения:

    cp .env.example .env
    # Отредактируйте .env при необходимости
  3. Запустите приложение:

    make all
  4. Примените миграции:

    make migrate
  5. Проверьте работу:

📝 Команды Makefile

Основные команды

Команда Описание
make all Запуск всех сервисов (приложение + хранилища)
make all-down Остановка всех сервисов
make migrate Применение миграций (upgrade head)
make migrations Создание новой миграции (autogenerate)
make test Запуск тестов

Управление приложением

Команда Описание
make app-logs Просмотр логов приложения
make app-shell Подключение к shell контейнера приложения
make app-down Остановка приложения

Управление хранилищами

Команда Описание
make storages Запуск хранилищ (PostgreSQL)
make storages-down Остановка хранилищ
make storages-logs Просмотр логов хранилищ
make postgres Подключение к PostgreSQL через psql

Мониторинг (ELK Stack)

Команда Описание
make monitoring Запуск мониторинга (ELK Stack)
make monitoring-down Остановка мониторинга
make monitoring-logs Просмотр логов всех сервисов мониторинга
make monitoring-restart Перезапуск мониторинга
make elasticsearch-logs Просмотр логов Elasticsearch
make logstash-logs Просмотр логов Logstash
make kibana-logs Просмотр логов Kibana

Разработка

Команда Описание
make precommit Запуск pre-commit проверок для всех файлов

🏗 Архитектура

Проект построен на принципах Domain-Driven Design и разделен на четыре основных слоя:

1. Domain Layer (app/domain/)

Бизнес-логика и правила предметной области

domain/
├── base/                    # Базовые классы
│   ├── entity.py           # Базовая сущность
│   ├── value_object.py     # Базовый объект-значение
│   └── exceptions.py       # Доменные исключения
│
├── organization/           # Контекст организаций
│   ├── entities.py         # Сущности: Organization, Building, Activity
│   ├── value_objects/      # Объекты-значения
│   │   ├── activity.py
│   │   ├── building.py
│   │   └── organization.py
│   ├── services/           # Доменные сервисы
│   │   ├── activity.py
│   │   ├── building.py
│   │   └── organization.py
│   ├── interfaces/         # Интерфейсы репозиториев
│   └── exceptions.py       # Исключения домена
│
└── user/                   # Контекст пользователей
    ├── entities.py         # Сущности: User, APIKey
    ├── value_objects/      # Объекты-значения
    │   └── user.py
    ├── services/           # Доменные сервисы
    │   ├── user.py
    │   └── api_key.py
    ├── interfaces/         # Интерфейсы репозиториев
    └── exceptions.py       # Исключения домена

Ключевые сущности:

  • OrganizationEntity — организация с названием, телефонами, зданием и видами деятельности
  • BuildingEntity — здание с адресом и координатами
  • ActivityEntity — вид деятельности с иерархией (до 3 уровней)
  • UserEntity — пользователь с именем и захешированным паролем
  • APIKeyEntity — API ключ для аутентификации

Value Objects:

  • ActivityNameValueObject — имя деятельности (не пустое, макс. 255 символов)
  • BuildingAddressValueObject — адрес здания (не пустой, макс. 255 символов)
  • BuildingCoordinatesValueObject — координаты здания
  • OrganizationNameValueObject — название организации (не пустое)
  • OrganizationPhoneValueObject — телефон (10-15 цифр)
  • UsernameValueObject — имя пользователя (мин. 3 символа, макс. 255, буквы/цифры/подчеркивания)
  • PasswordValueObject — пароль (bcrypt hash)

2. Application Layer (app/application/)

Бизнес-процессы и сценарии использования

application/
├── mediator.py            # CQRS медиатор
├── commands/              # Команды (изменение состояния)
│   ├── activity.py
│   ├── api_key.py
│   ├── building.py
│   ├── organization.py
│   └── user.py
├── queries/               # Запросы (чтение данных)
│   ├── activity.py
│   ├── api_key.py
│   ├── building.py
│   ├── organization.py
│   └── user.py
└── exceptions/            # Исключения слоя приложения

CQRS через Mediator:

  • Commands — операции записи/изменения (CreateOrganizationCommand, CreateBuildingCommand и т.д.)
  • Queries — операции чтения (GetOrganizationByIdQuery, GetOrganizationsByRadiusQuery и т.д.)
  • Mediator — маршрутизация запросов к соответствующим обработчикам

3. Infrastructure Layer (app/infrastructure/)

Технические детали и внешние зависимости

infrastructure/
├── database/              # Работа с базой данных
│   ├── main.py           # Конфигурация БД и сессий
│   ├── models/           # SQLAlchemy модели
│   │   ├── activity.py
│   │   ├── building.py
│   │   ├── organization.py
│   │   └── user.py
│   ├── repositories/     # Реализации репозиториев
│   │   ├── activity.py
│   │   ├── api_key.py
│   │   ├── building.py
│   │   ├── organization.py
│   │   ├── user.py
│   │   └── dummy/        # InMemory репозитории для тестов
│   ├── converters/       # Конвертеры Entity ↔ Model
│   │   ├── activity.py
│   │   ├── building.py
│   │   ├── organization.py
│   │   └── user.py
│   ├── gateways/         # Шлюзы для работы с БД
│   │   └── postgres.py   # Database класс с сессиями
│   └── migrations/       # Alembic миграции
│       └── versions/     # Файлы миграций
└── logging/              # Логирование
    ├── handler.py        # LogstashHandler
    └── logger.py         # Настройка логгера

Реализации:

  • Database Gateway — управление асинхронными сессиями SQLAlchemy (read-write и read-only)
  • SQLAlchemy репозитории — реализация доменных интерфейсов для PostgreSQL
  • InMemory репозитории — реализации для тестирования без БД
  • Конвертеры — преобразование между Entity и Model
  • Логирование — интеграция с ELK Stack через LogstashHandler

4. Presentation Layer (app/presentation/)

API endpoints и представление данных

presentation/
└── api/                   # REST API endpoints
    ├── main.py           # Создание FastAPI приложения
    ├── auth.py           # Авторизация (JWT, API ключи)
    ├── dependencies.py   # Зависимости для эндпоинтов
    ├── exceptions.py     # Обработка исключений
    └── v1/               # Версия API
        ├── activity/     # Эндпоинты видов деятельности
        ├── building/     # Эндпоинты зданий
        ├── organization/ # Эндпоинты организаций
        └── user/         # Эндпоинты пользователей

Settings (app/settings/)

Конфигурация приложения

  • Настройки через Pydantic Settings
  • Поддержка переменных окружения
  • Валидация конфигурации при старте

Tests (app/tests/)

Тестирование

  • Юнит-тесты доменных сущностей и value objects
  • Интеграционные тесты приложения (commands/queries)
  • API тесты (presentation/api/)
  • InMemory репозитории для изоляции тестов

📚 API Документация

После запуска доступна по адресу: http://localhost:8000/api/docs

🔐 Авторизация

API использует два типа авторизации:

  1. JWT токены (для пользовательских эндпоинтов):

    • Токены хранятся в cookies
    • Access token для коротких операций
    • Refresh token для обновления access token
  2. API ключи (для всех остальных эндпоинтов):

    • Передаются в заголовке Authorization: Bearer <api_key>
    • API ключи создаются через защищенный эндпоинт /api/v1/user/api-key
    • Все эндпоинты для activities, buildings, organizations защищены API ключом

📡 Основные эндпоинты

User (Пользователи) — Без авторизации

  • POST /api/v1/user/register — регистрация нового пользователя
  • POST /api/v1/user/login — аутентификация и получение токенов
  • POST /api/v1/user/token/refresh — обновление access token

User (API ключи) — Требует JWT токен

  • POST /api/v1/user/api-key — создание API ключа для текущего пользователя

Activities (Виды деятельности) — Требует API ключ

  • POST /api/v1/activities — создание вида деятельности
  • GET /api/v1/activities/{activity_id} — получение по ID
  • GET /api/v1/activities — список с фильтрацией (name, parent_id)

Buildings (Здания) — Требует API ключ

  • POST /api/v1/buildings — создание здания
  • GET /api/v1/buildings/{building_id} — получение по ID
  • GET /api/v1/buildings — список с фильтрацией (address, coordinates)

Organizations (Организации) — Требует API ключ

  • POST /api/v1/organizations — создание организации
  • GET /api/v1/organizations/{organization_id} — получение по ID
  • GET /api/v1/organizations?name={name} — поиск по названию
  • GET /api/v1/organizations/by-address?address={address} — поиск по адресу
  • GET /api/v1/organizations/by-activity?activity_name={name} — поиск по виду деятельности
  • GET /api/v1/organizations/by-radius — геопоиск по радиусу
  • GET /api/v1/organizations/by-rectangle — геопоиск по прямоугольной области

📋 Формат ответов API

Все ответы API возвращаются в едином формате ApiResponse:

  • data — данные ответа
  • meta — метаинформация
  • errors — список ошибок (если есть)
{
  "data": {},
  "meta": {},
  "errors": []
}

Примеры ответов:

Успешный ответ (200/201):

{
  "data": {
    "oid": "uuid",
    "name": "Название"
  },
  "meta": {},
  "errors": []
}

Ошибка валидации (400):

{
  "data": {},
  "meta": {},
  "errors": [
    {"message": "Описание ошибки"}
  ]
}

Ошибка валидации запроса (422):

{
  "data": {},
  "meta": {},
  "errors": [
    {
      "message": "body -> name: Field required",
      "type": "missing",
      "field": "body -> name"
    }
  ]
}

Ошибка авторизации (401):

{
  "data": {},
  "meta": {},
  "errors": [
    {"message": "API key is required"}
  ]
}

📊 Логирование

Приложение использует ELK стек для централизованного сбора, обработки и анализа логов.

Архитектура логирования

Application → LogstashHandler → Logstash → Elasticsearch → Kibana

Компоненты

LogstashHandler

Кастомный handler для Python logging, который отправляет логи в Logstash через TCP:

  • Автоматическое переподключение при разрыве соединения
  • Graceful degradation — приложение продолжает работать даже если Logstash недоступен
  • Отправка логов в формате JSON

Настройка

Логирование настраивается через переменные окружения в .env:

LOGSTASH_HOST=logstash      # Хост Logstash (по умолчанию: logstash)
LOGSTASH_PORT=5000          # Порт Logstash (по умолчанию: 5000)
LOGSTASH_PROJECT=organization-catalog  # Имя проекта для индексации

Запуск ELK стека

  1. Запустите мониторинг:

    make monitoring
  2. Настройте Elasticsearch для single-node кластера:

    curl -X PUT "localhost:9200/api-logs/_settings" -H 'Content-Type: application/json' -d'{
      "index": {
        "number_of_replicas": 0
      }
    }'
  3. Проверьте статус:

    make monitoring-logs
  4. Доступ к сервисам:

Просмотр логов

Через Kibana

  1. Откройте Kibana: http://localhost:5601
  2. Перейдите в Discover
  3. Создайте index pattern: api-logs
  4. Просматривайте логи в реальном времени

Через команды Makefile

# Логи всех сервисов мониторинга
make monitoring-logs

# Логи конкретных сервисов
make elasticsearch-logs
make logstash-logs
make kibana-logs

Использование логгера в коде

from infrastructure.logging.logger import get_logger

logger = get_logger()

logger.info(
    "Пользователь выполнил действие",
    extra={
        "user_id": user_id,
        "action": "create_organization",
        "organization_id": org_id,
    }
)

Все логи с дополнительными полями из extra автоматически попадут в Logstash и будут проиндексированы в Elasticsearch.

Остановка мониторинга

make monitoring-down

🧪 Тестирование

Запуск тестов

# Все тесты
make test

Структура тестов

  • Domain tests (app/tests/domain/) — тесты доменных сущностей и value objects
  • Application tests (app/tests/application/) — тесты команд и запросов
  • API tests (app/tests/presentation/api/) — интеграционные тесты API

🚀 Деплой на VPS

Проект настроен для автоматического деплоя на VPS через GitHub Actions.

📋 Обзор процесса деплоя

При деплое выполняются следующие шаги:

  1. Проверка кода — линтинг (ruff, isort) и тесты (pytest)
  2. Подключение к VPS — через SSH
  3. Обновление кода — pull из репозитория
  4. Сборка и запуск — Docker Compose
  5. Миграции БД — автоматическое применение через Alembic
  6. Health check — проверка работоспособности приложения

🔧 Настройка GitHub Secrets

Для работы автоматического деплоя необходимо настроить следующие секреты в GitHub:

Необходимые секреты:

  • VPS_SSH_PRIVATE_KEY — приватный SSH ключ для доступа к VPS
  • VPS_HOST — IP адрес или домен VPS сервера
  • VPS_USER — имя пользователя для SSH подключения (обычно root или ubuntu)
  • VPS_APP_DIR — путь к директории проекта на сервере (например, /opt/organization_catalog_ddd)
  • VPS_APP_URL — (опционально) URL приложения для health check (например, http://your-domain.com)

Подробные инструкции по настройке SSH ключа и добавлению секретов см. ниже в разделе "Подготовка VPS сервера" (шаги 2 и 5).

🖥 Подготовка VPS сервера

  1. Установите необходимые зависимости:

    # Обновление системы
    sudo apt update && sudo apt upgrade -y
    
    # Установка Docker и Docker Compose
    curl -fsSL https://get.docker.com -o get-docker.sh
    sudo sh get-docker.sh
    sudo apt install docker-compose-plugin -y
    
    # Установка Git
    sudo apt install git -y
  2. Настройте SSH ключ для GitHub Actions:

    Шаг 1: Создайте SSH ключ (на вашей локальной машине)

    # Сгенерируйте новый SSH ключ специально для GitHub Actions
    ssh-keygen -t ed25519 -C "github-actions-deploy" -f ~/.ssh/github_actions_deploy
    
    # Или используйте существующий ключ (если у вас уже есть SSH ключ для VPS)
    # В этом случае пропустите этот шаг

    Шаг 2: Скопируйте публичный ключ на VPS сервер

    # Если создали новый ключ
    ssh-copy-id -i ~/.ssh/github_actions_deploy.pub user@your-vps-ip
    
    # Или если используете существующий ключ
    ssh-copy-id user@your-vps-ip
    
    # Проверьте подключение
    ssh -i ~/.ssh/github_actions_deploy user@your-vps-ip
    # или
    ssh user@your-vps-ip

    Шаг 3: Скопируйте приватный ключ для GitHub Secrets

    # Если создали новый ключ
    cat ~/.ssh/github_actions_deploy
    
    # Или если используете существующий ключ (обычно ~/.ssh/id_ed25519 или ~/.ssh/id_rsa)
    cat ~/.ssh/id_ed25519
    # или
    cat ~/.ssh/id_rsa

    Важно: Скопируйте весь вывод команды (включая строки -----BEGIN OPENSSH PRIVATE KEY----- и -----END OPENSSH PRIVATE KEY-----)

  3. Клонируйте репозиторий на сервер:

    cd /opt
    sudo git clone https://github.com/your-username/organization_catalog_ddd.git
    sudo chown -R $USER:$USER organization_catalog_ddd
    cd organization_catalog_ddd
  4. Создайте файл .env на сервере:

    # Скопируйте пример .env и отредактируйте под продакшн
    cp .env.example .env
    nano .env

    Обновите переменные для продакшн окружения:

    API_PORT=8000
    POSTGRES_HOST=postgres
    POSTGRES_PORT=5432
    POSTGRES_DB=organization_catalog
    POSTGRES_USER=postgres
    POSTGRES_PASSWORD=your_secure_password
    # ... остальные переменные
  5. Добавьте SSH ключ в GitHub Secrets:

    Шаг 1: Откройте настройки репозитория в GitHub

    • Перейдите в ваш репозиторий на GitHub
    • Нажмите на Settings (в верхней панели)
    • В левом меню выберите Secrets and variablesActions

    Шаг 2: Добавьте секреты

    Нажмите New repository secret и добавьте каждый секрет:

    • Name: VPS_SSH_PRIVATE_KEY Value: Вставьте весь приватный ключ (который вы скопировали на шаге 2.3)

      • Должен начинаться с -----BEGIN OPENSSH PRIVATE KEY-----
      • И заканчиваться на -----END OPENSSH PRIVATE KEY-----
      • Включая все строки между ними
    • Name: VPS_HOST Value: IP адрес или домен вашего VPS (например, 123.45.67.89 или example.com)

    • Name: VPS_USER Value: Имя пользователя для SSH (обычно root, ubuntu, или debian)

    • Name: VPS_APP_DIR Value: Путь к директории проекта на сервере (например, /opt/organization_catalog_ddd)

    • Name: VPS_APP_URL (опционально) Value: URL вашего приложения для health check (например, http://your-domain.com или https://api.example.com)

    Важно: После добавления секретов они будут зашифрованы и их нельзя будет просмотреть. Убедитесь, что сохранили значения где-то безопасно.

⚙️ Скрипт деплоя

Проект использует скрипт deploy.sh для автоматизации деплоя. Скрипт выполняет:

  1. Обновление кода из репозитория
  2. Проверку наличия .env файла
  3. Остановку старых контейнеров
  4. Сборку и запуск новых контейнеров
  5. Ожидание готовности PostgreSQL
  6. Создание базы данных (если не существует)
  7. Применение миграций Alembic
  8. Очистку старых Docker образов
  9. Проверку статуса контейнеров

Ручной запуск деплоя на VPS:

cd /opt/organization_catalog_ddd
chmod +x deploy.sh
./deploy.sh master  # или другая ветка

🔄 Автоматический деплой

После настройки, деплой будет автоматически запускаться при:

  • Push в ветку master или main
  • Ручном запуске через GitHub Actions (Actions → Deploy to VPS → Run workflow)

Важно: Перед деплоем автоматически выполняются проверки:

  1. Линтинг кода — проверка стиля кода с помощью ruff и isort
  2. Тесты — запуск всех тестов через pytest

Деплой на VPS произойдет только если все проверки пройдут успешно. Это гарантирует, что на продакшн попадает только проверенный код.

🔍 Проверка деплоя

После успешного деплоя проверьте:

  1. Статус контейнеров:

    ssh user@your-vps-ip "cd /opt/organization_catalog_ddd && docker ps"
  2. Health check endpoint:

    curl http://your-vps-ip:8000/healthcheck
  3. Логи приложения:

    ssh user@your-vps-ip "cd /opt/organization_catalog_ddd && docker logs main-app"

About

Сервис-каталог организаций, CRUD приложение на FastAPI, Postgres, PostGIS, Pydantic, SQLAlchemy, alembic, DDD, CQRS, ELK

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages