/Fastapi-EdgeDB-DDD

An experiment in using the concept of clean architecture to replace the web framework and database in use. Based on the application discussed in the book "Architecture Patterns with Python: Enabling Test-Driven Development, Domain-Driven Design, and Event-Driven Microservices"

Primary LanguagePythonOtherNOASSERTION

FastAPI-EdgeDB-DDD v0.0.8

Description

Description: FastAPI-EdgeDB-DDD is a project based on "Architecture Patterns with Python: Enabling Test-Driven Development, Domain-Driven Design, and Event-Driven Microservices" (by Persival Harry and Gregory Bob). The repository, thin view, context managers, and messagebus pattern allow for building an Event-driven architecture. The partition of the application into layers helps reduce the granularity of testing, as each layer can be easily covered with quick unit tests. The goal is not only to replicate the project from the textbook but also to test how such architecture allows for easy replacement of system components - frameworks and databases. Альтернативный текст

🧬 Full Project Diagram

  ┌─────────────────────────┐       ┌──────────────────────────────┐  
┌─► External Message Broker │       │             WEB              ◄───┐
│ └─────┬───────────────────┘       └───────────────────────────┬──┘   │  
│       │                                                       │      │  
│ ┌─────▼─────────┐                        ────              ┌──▼──┐   │  
│ │ Eventconsumer ├──────────────────────►/    \◄────────────┤ API │   │  
│ └─────┬─────────┘                      /      \            └───┬─┘   │
│       │                               /        \               │     │
│       │                              / Bootstrap\              │     │
│       │                              \          /              │     │
│       │                               \        /               │     │
│       │       ┌─────────────────────────      /                │     │
│       │       │                         \────/                 │     │
│       │       │                                                │     │
│       │       │       ┌────────────────────────────────────────┘     │
│       │       │       │                                              │
│    ---│-------│-------│-------------------------------------------   │
│    -  │       │       │                                          -   │
│    -  │       │       │      ┌────────────┐                      -   │
│    -  │       │       │      │ ---------- │                      -   │
│    - ┌▼───────▼───────▼┐     ├────────────┤     ┌──────────────┐ -   │
│    - │    Messagebus   ├────►│  Handlers  ├────►│ unit_of_work │ -   │
│    - └─────────────────┘     ├────────────┤     └───────┬──────┘ -   │
│    -                         │ ---------- │             │        -   │
│    -                         └────────────┘             │        -   │
│    -  Services                                          │        -   │
│    -----------------------------------------------------│---------   │
│                                                         │            │
│                                    ┌────────────────────┘            │
│                                    │                                 │
│    --------------------------------│------------------------------   │
│    -                               │                             -   │
│    - ┌────────────────┐   ┌────────▼────────┐  ┌───────────────┐ -   │
└──────┤ Eventpublisher │   │   Repositories  │  │  Notifications├─────┘
     - └────────────────┘   └─────┬───────┬───┘  └───────────────┘ -
     -                            │       │                        -
     -  Adaptorstions             │       │                        -
     -----------------------------│-------│-------------------------
                                  │       │
         ┌─────────────┐          │       │        ┌─────────────┐
         │             │          │       │        │             │
         │ Domain area ◄──────────┘       └────────►    EdgeDB   │
         │             │                           │             │
         └─────────────┘                           └─────────────┘

Technology

  • FastAPI: A fast asynchronous web framework for Python.
  • EdgeDB: A database with expressive query syntax.
  • Redis Pub/Sub: Publisher-Subscribers mechanism in Redis.
  • Pytest: A powerful framework for writing and running tests.
  • Docker: Containerization for convenient deployment.

Advantages

  • Mastery of DDD and clean architecture patterns.
  • The architecture allowed for easy replacement of Flask with FastAPI and Postgres + SQLAlchemy with EdgeDB.
  • Ease of testing and integration due to the separation of the application into layers.
  • Template project structure suitable for creating your own projects.

Challenges:

  • Approaches from the textbook introduce a significant amount of boilerplate code, which might not be rational for small tasks.
  • Potential integration issues.
  • The need to learn message delivery systems (Kafka, RabbitMQ).

Help Needed

I've encountered an issue with testing parallel transactions that I can't resolve on my own. The original testing code uses the threading module: Original Test Code tests/integration/test_uow.py - original As I use an asynchronous framework, I've modified it: Asynchronous Test Code tests/integration/test_uow.py - asynchrony During testing, I encounter the exception edgedb.errors.TransactionSerializationError: could not serialize access due to concurrent update Tests are only stopped by a keyboard interrupt (KeyboardInterrupt). I suspect my problem stems from the fact that I'm just starting to learn what asynchronous code is. Maybe I'm not opening and closing the asynchronous transaction context manager correctly:: Async Unit of Work context manager src/allocation/services/unit_of_work.py If anyone could assist, I'd be grateful. Feel free to reach out via comments, Telegram, or Twitter.

Growth:

Enhancement of design skills. Application of patterns in other projects. Experience in integrating new libraries and applications.

Installation and Running:

Commands are provided for both Ubuntu and Windows systems

Ubuntu

Project Initialization

  1. Clone the project:
   git clone https://github.com/Gen121/Fastapi-EdgeDB-DDD.git
   cd Fastapi-EdgeDB-DDD
  1. Install dependencies:
   python3 -m venv venv && source venv/bin/activate
   pip install -r requirements.txt
   pip install -e src/
  1. Create a .env file:
 cp env.example .env

This command copies the contents of the env.example file into a new .env file in the root directory, next to the src directory.

  1. Run the Make command:
    make all 

During the execution, several Docker containers will be built, and after the launch, testing will be performed.

Run tests

make test
# or, to run individual test types
make unit
make integration
make e2e
# or, if you have a local virtualenv
make up
pytest tests/unit
pytest tests/integration
pytest tests/e2e
Windows

Project Initialization

  1. Clone the project:
   git clone https://github.com/Gen121/Fastapi-EdgeDB-DDD.git
   cd Fastapi-EdgeDB-DDD
  1. Install dependencies:
   python -m venv venv
   venv\Scripts\activate
   pip install -r requirements.txt
   pip install -e src\
  1. Create a .env file:
   copy env.example .env

This command copies the contents of the env.example file into a new .env file in the root directory, next to the src directory.

  1. Run the .bat script to build and start the container:
   run_app.bat call :all 

During the execution, several Docker containers will be built, and after the launch, testing will be performed.

Run tests

   run_app.bat call :test

# or, to run individual test types
   run_app.bat call :unit-tests
   run_app.bat call :integration-tests
   run_app.bat call:e2e-tests

# or, if you have a local virtualenv
   run_app.bat call :up
   pytest tests/unit
   pytest tests/integration
   pytest tests/e2e

Sample .env file

# .env.example

# Disabling .pyc file creation and output buffering
PYTHONDONTWRITEBYTECODE=1
PYTHONUNBUFFERED=1

APP_NAME="edgedb"
NETWORK_NAME="local_dbs_network"

# EdgeDB 
DB_USER_NAME="edgedb"
DB_NAME="edgedb"
DB_TEST_NAME="edgedb"
DB_ROOT_PASSWORD="edgedb"
DB_HOSTNAME="edgedb"
DB_PORT=5656

## Volume names for database data and schemas
DB_VOLUME_DATA_NAME="${DB_CONTAINER_NAME}_data"
DB_VOLUME_SCHEMA_NAME="${DB_CONTAINER_NAME}_schema"

DB_CONTAINER_NAME="${APP_NAME}"

# API-server
API_HOST="localhost"
API_PORT=5005

# Redis
REDIS_HOST="redis"
REDIS_PORT=6379

# Email sending host
EMAIL_HOST="mailhog"
[на русском]

Описание

Описание: FastAPI-EdgeDB-DDD - Проект на основе "Паттерны разработки на Python: TDD, DDD и событийно-ориентированная архитектура" (Персиваль Гарри и Грегори Боб).Паттерны репозитория, тонких вью, менеджеров контекста и сообщений, позволяют выстроить событийно-управляемую модель. Разделение приложения на слои, позволяет уменьшить гранулярность тестирования, т.к. каждый слой легко покрыть быстрыми юниттестами. Цель не просто воспроизвести проект из учебника, а протестировать насколько такая архитектура позволяет легко заменять компоненты системы - фреймворк и базу данных. Альтернативный текст

Технологии

  • FastAPI: Быстрый асинхронный веб-фреймворк для Python.
  • EdgeDB: База данных с выразительным синтаксисом запросов.
  • Redis Pub/Sub: Механизм Publisher - Subscribers в Redis.
  • Pytest: Мощный фреймворк для написания и запуска тестов.
  • Docker: Контейнеризация для удобного развертывания.

Преимущества

  • Освоение паттернов DDD и чистой архитектуры.
  • Архитектура позволила легко заменить Flask на FastAPI, а связку Postgres + SQLAlchemy на EdgeDB.
  • Легкость тестирования и интеграции благодаря разделению приложения на слои.
  • Шаблонная структура проекта и подходит для создания своих проектов.

Сложности:

  • Подходы из учебника задают высокую константу кода в виде бойлерплейта, что может быть не рациональным для небольших задач.
  • Возможные проблемы интеграции.
  • Необходимость изучить системы доставки сообщений(Kafka, RabbitMQ)

Рост:

Улучшение навыков проектирования. Применение паттернов в других проектах. Опыт интеграции новых библиотек и приложений.

Установка и Запуск:

Представлены команды для ос Ubuntu и Windows

Ubuntu

Инициализация проекта

  1. Клонируйте проект:
   git clone https://github.com/Gen121/Fastapi-EdgeDB-DDD.git
   cd Fastapi-EdgeDB-DDD
  1. Установите зависимости:
   python3 -m venv venv && source venv/bin/activate
   pip install -r requirements.txt
   pip install -e src/
  1. Создайте файл .env:
   cp env.example .env

Эта команда копирует содержимое файла env.example в новый файл .env в корневой директории, по соседству c каталогом src

  1. Запустите команду Make:
   make all 

В процессе запуска будет собрано несколько контейнеров Docker и после запуска выполнено тестирование

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

   make test

   # or, to run individual test types
   make unit
   make integration
   make e2e

   # or, if you have a local virtualenv
   make up
   pytest tests/unit
   pytest tests/integration
   pytest tests/e2e
Windows

Инициализация проекта

  1. Клонируйте проект:
   git clone https://github.com/Gen121/Fastapi-EdgeDB-DDD.git
   cd Fastapi-EdgeDB-DDD
  1. Установите зависимости: Создание и активация виртуальной среды:
   python -m venv venv
   venv\Scripts\activate
   pip install -r requirements.txt
   pip install -e src\
  1. Создайте файл .env:
   copy env.example .env

Эта команда копирует содержимое файла env.example в новый файл .env в корневой директории, по соседству c каталогом src

  1. Запустите сценарий сборки и запуска контейнера:
   run_app.bat call :all 

В процессе будет собрано несколько контейнеров Docker, после их запуска выполнено тестирование сервиса

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

   run_app.bat call :test

   # или для запуска отдельных типов тестов
   run_app.bat call :unit-tests
   run_app.bat call :integration-tests
   run_app.bat call:e2e-tests

   # или, если у вас есть virtualenv
   run_app.bat call :up
   pytest tests/unit
   pytest tests/integration
   pytest tests/e2e

Образец .env файла

# .env.example

# Отключение создания .pyc файлов и буферизации вывода
PYTHONDONTWRITEBYTECODE=1
PYTHONUNBUFFERED=1

APP_NAME="edgedb"
NETWORK_NAME="local_dbs_network"

# EdgeDB 
DB_USER_NAME="edgedb"
DB_NAME="edgedb"
DB_TEST_NAME="edgedb"
DB_ROOT_PASSWORD="edgedb"
DB_HOSTNAME="edgedb"
DB_PORT=5656

## Имена томов для данных и схем БД
DB_VOLUME_DATA_NAME="${DB_CONTAINER_NAME}_data"
DB_VOLUME_SCHEMA_NAME="${DB_CONTAINER_NAME}_schema"

DB_CONTAINER_NAME="${APP_NAME}"

# API-сервер
API_HOST="localhost"
API_PORT=5005

# Redis
REDIS_HOST="redis"
REDIS_PORT=6379

# Хост отправки электронной почты
EMAIL_HOST="mailhog"