Skip to content

eduardohki/python_hexagonal_tdd_example

Repository files navigation

Python Hexagonal & TDD Example

A reference Python project for hexagonal architecture (ports and adapters) with Test-Driven Development.

Project Structure

python_hexagonal_tdd_example/
├── src/
│   └── example_app/
│       ├── domain/                 # The hexagon (core business logic)
│       │   ├── models/             # Domain entities and value objects
│       │   │   └── example_entity.py
│       │   ├── ports/              # Interfaces (abstract base classes)
│       │   │   └── repository.py   # Output port for persistence
│       │   └── use_cases/          # Application-specific business rules
│       │       └── example_use_case.py
│       │
│       └── adapters/               # Infrastructure layer (outside the hexagon)
│           ├── inbound/            # Driving adapters (e.g., REST API, CLI)
│           └── outbound/           # Driven adapters (e.g., database, APIs)
│               └── in_memory_repository.py
│
├── tests/                          # Mirrors src structure, uses pytest markers
│   ├── conftest.py                 # Shared pytest fixtures
│   ├── domain/
│   │   ├── models/
│   │   ├── ports/
│   │   └── use_cases/
│   │       └── test_example_use_case.py  # @pytest.mark.unit
│   └── adapters/
│       ├── inbound/
│       └── outbound/
│           └── test_in_memory_repository.py  # @pytest.mark.integration
│
├── pyproject.toml                  # Project configuration
└── README.md

Hexagonal Architecture Overview

flowchart LR
    subgraph Inbound["Inbound Adapters<br/>(adapters/inbound/)"]
        REST["REST API"]
        CLI["CLI"]
        MQ["Message Queue"]
    end

    subgraph Domain["Domain - The Hexagon<br/>(domain/)"]
        UC["Use Cases<br/>(use_cases/)"]
        Models["Models<br/>(models/)"]
        Ports["Ports<br/>(ports/)"]
        UC --- Models
        Models --- Ports
    end

    subgraph Outbound["Outbound Adapters<br/>(adapters/outbound/)"]
        Repo["Repositories"]
        ExtAPI["External APIs"]
        DB["Database"]
    end

    Inbound --> Domain
    Domain --> Outbound
Loading

Key Concepts

  • Domain Layer: The hexagon containing pure business logic, entities, ports, and use cases. Has no dependencies on external frameworks or infrastructure.
  • Use Cases: Application-specific business rules that orchestrate domain objects and coordinate with adapters through ports.
  • Ports: Interfaces that define how the domain communicates with the outside world.
  • Adapters: Implementations of ports that connect to external systems.

Dependency Rule

Dependencies always point inward:

  • Adapters depend on Ports
  • Use Cases depend on Models and Ports
  • Models have no external dependencies

Installation

uv sync

Running Tests

# Run all tests
pytest

# Run only unit tests (using markers)
pytest -m unit

# Run only integration tests (using markers)
pytest -m integration

# Run tests by path
pytest tests/domain
pytest tests/adapters

# Run with coverage
pytest --cov=example_app --cov-report=html

Test Organization

Tests mirror the source structure and use pytest markers for categorization:

Marker Description Example
@pytest.mark.unit Fast, isolated, no external dependencies Domain model tests
@pytest.mark.integration May require external systems Repository adapter tests

Example:

import pytest

@pytest.mark.unit
class TestMyEntity:
    def test_something(self):
        ...

TDD Workflow

  1. Red: Write a failing test that defines expected behavior
  2. Green: Write the minimum code to make the test pass
  3. Refactor: Improve the code while keeping tests green

License

MIT

About

A reference Python project for hexagonal architecture (ports and adapters) with Test-Driven Development.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages