{"id":50531803,"url":"https://github.com/merrazeal/python-hexagonal-architecture-example","last_synced_at":"2026-06-03T14:04:09.347Z","repository":{"id":361066980,"uuid":"1252530765","full_name":"merrazeal/python-hexagonal-architecture-example","owner":"merrazeal","description":"Payment service example built with Hexagonal Architecture in Python","archived":false,"fork":false,"pushed_at":"2026-05-29T03:26:19.000Z","size":96,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-29T05:16:11.330Z","etag":null,"topics":["asyncio","clean-architecture","ddd","dependency-injection","fastapi","faststream","hexagonal-architecture","outbox-pattern","ports-and-adapters","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/merrazeal.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-28T16:01:21.000Z","updated_at":"2026-05-29T03:26:23.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/merrazeal/python-hexagonal-architecture-example","commit_stats":null,"previous_names":["merrazeal/python-hexagonal-architecture-example"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/merrazeal/python-hexagonal-architecture-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merrazeal%2Fpython-hexagonal-architecture-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merrazeal%2Fpython-hexagonal-architecture-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merrazeal%2Fpython-hexagonal-architecture-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merrazeal%2Fpython-hexagonal-architecture-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/merrazeal","download_url":"https://codeload.github.com/merrazeal/python-hexagonal-architecture-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/merrazeal%2Fpython-hexagonal-architecture-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33867876,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asyncio","clean-architecture","ddd","dependency-injection","fastapi","faststream","hexagonal-architecture","outbox-pattern","ports-and-adapters","python"],"created_at":"2026-06-03T14:04:08.417Z","updated_at":"2026-06-03T14:04:09.340Z","avatar_url":"https://github.com/merrazeal.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Python Hexagonal Architecture Example\n\nПрактический пример модуля обработки платежей, построенного на принципах Clean Architecture (Hexagonal Architecture).\nВ примере показано:\n- как реализовать надёжную консистентную доставку событий через Outbox pattern;\n- как снижать стоимость изменений и добавления новых интеграций за счёт изоляции бизнес-логики от инфраструктуры через порты и адаптеры;\n- как упростить E2E тестирование - нестабильные внешние интеграции можно подменять Fake реализациями на уровне окружения с помощью DI контейнеров;\n- как безопасно обрабатывать платежи без двойных списаний через SELECT FOR UPDATE;\n\nСервис написан как живой код к статье о том, почему инфраструктурные детали не должны протекать в бизнес-логику, и как это решается через контракты, адаптеры и правильное разделение слоёв.\n\nСтатья: https://habr.com/ru/articles/1034758/\n\n## Что внутри\n\n### Стек\n\n- **FastAPI** — REST API\n- **FastStream + RabbitMQ** — асинхронная обработка сообщений (worker)\n- **APScheduler** — планировщик задач (scheduler)\n- **SQLAlchemy CORE (async) + PostgreSQL** — хранение данных\n- **Alembic** — миграции\n- **Dishka** — dependency injection container\n\n### Структура проекта\n\n```\nsrc/\n├── domain/          # Доменные сущности и их локальные правила (статусы, исключения, допустимые инварианты и т.д)\n├── ports/           # Контракты: интерфейсы репозиториев, UoW, gateway, publisher\n├── usecases/        # Бизнес-сценарии: create, get, process, dispatch\n├── adapters/        # Реализации контрактов: SQLAlchemy, RabbitMQ, HTTP, payment gateway\n├── handlers/        # Точки входа: REST routes, FastStream tasks, cron jobs\n└── boot/\n    ├── dev/         # DI-контейнер и entrypoint'ы для разработки (внешние Stub-реализации)\n    └── test/        # DI-контейнер и entrypoint'ы для тестов (внешние Fake-реализации)\n```\n\n### Полный flow платежа\n\n```\nPOST /api/v1/payments\n        │\n        ▼\n  CreatePaymentUseCase\n  ├── сохраняет Payment в Postgres\n  └── сохраняет запись в Outbox\n        │\n        ▼ (каждые N секунд)\n  Scheduler → DispatchPaymentEventsUseCase\n  └── публикует событие payment.created → RabbitMQ\n        │\n        ▼\n  Worker → ProcessPaymentUseCase\n  ├── списывает средства через IPaymentGateway\n  ├── обновляет статус Payment (SUCCEEDED / FAILED)\n  └── доставляет webhook на webhook_url\n```\n\nOutbox pattern гарантирует доставку событий в RabbitMQ даже при падении сервиса между созданием платежа и публикацией.\n\n---\n\n## Запуск для разработки\n\n```bash\ndocker compose -f docker-compose.dev.yml up --build\n```\n\nAPI будет доступен на `http://localhost:9090`.\n\nПример запроса:\n\n```bash\ncurl -X POST http://localhost:9090/api/v1/payments \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-API-Key: dev-secret-api-key\" \\\n  -H \"Idempotency-Key: $(uuidgen)\" \\\n  -d '{\n    \"amount\": \"100.00\",\n    \"currency\": \"RUB\",\n    \"description\": \"Order #1\",\n    \"metadata\": {\"order_id\": 1},\n    \"webhook_url\": \"https://example.com/webhook\"\n  }'\n```\n\n---\n\n## Тесты\n\n### Юнит-тесты (необходимо настроить локальное окружение)\n\nТестируют use cases в полной изоляции — без базы данных, без брокера, без HTTP. Все зависимости заменены hand-rolled mock'ами.\n\n```bash\nmake unit\n```\n\n### E2E-тесты\n\nПоднимают полный стек в контейнерах: реальный PostgreSQL, реальный RabbitMQ, web + worker + scheduler на Fake-реализациях внешних вызовов.\n\nТест создаёт платёж через REST API, ждёт пока scheduler опубликует событие в RabbitMQ, worker его обработает и обновит статус в базе, затем проверяет финальное состояние через GET.\n\n```bash\nmake e2e\n```\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerrazeal%2Fpython-hexagonal-architecture-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmerrazeal%2Fpython-hexagonal-architecture-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmerrazeal%2Fpython-hexagonal-architecture-example/lists"}