Skip to main content

Hexagonal Architecture Generator for FastAPI

Generate FastAPI CRUD modules following hexagonal (ports & adapters) architecture principles.

Features

  • Complete CRUD Generation: Generate domain models, DTOs, repositories, use cases, schemas, and API routes
  • Hexagonal Architecture: Enforces proper layer separation (Domain, Application, Infrastructure)
  • SQLAlchemy 2.0: Uses modern Mapped types and select() statements
  • Pydantic V2: Includes Field validations and OpenAPI documentation
  • Unit of Work Pattern: Transaction management abstraction
  • Architecture Validation: Check compliance with hexagonal principles

Quick Start

Installation

pip install -e .

CLI Usage

# Generate a CRUD module (always written under ./generated_project)
python code_generator.py crud School

# Generate CRUD with specific actions
python code_generator.py crud Product --actions create list retrieve

# Copy a built-in application
python code_generator.py builtin user

# Verbose output
python code_generator.py crud Order -v

Generated Structure

generated_project/
└── src/
    └── school/
        ├── domain/                    # Business logic & entities
        │   ├── entities.py            # Domain entity + Create/Update DTOs (dataclasses)
        │   ├── exceptions.py          # Domain-level exceptions
        │   ├── repository.py          # Repository interface (port)
        │   └── unit_of_work.py        # Transaction interface (port)
        ├── application/                # Use cases & orchestration
        │   ├── schemas.py              # Pydantic schemas (API contracts)
        │   └── use_cases/               # One class per CRUD action
        │       ├── create.py
        │       ├── retrieve.py
        │       ├── list.py
        │       ├── update.py
        │       └── delete.py
        └── infrastructure/             # External adapters
            ├── models.py               # SQLAlchemy ORM model
            ├── database.py             # Repository implementation
            ├── unit_of_work.py         # SQLAlchemy UoW implementation
            ├── web.py                  # FastAPI routes
            └── exception_handlers.py   # Maps domain exceptions to HTTP responses

Architecture Principles

Domain Layer (Core)

  • Pure business logic
  • No external dependencies (no Pydantic, FastAPI, SQLAlchemy in DTOs)
  • Defines interfaces (ports) for infrastructure
  • Uses simple dataclasses for DTOs

Application Layer (Use Cases)

  • Orchestrates domain logic
  • Defines API contracts (Pydantic schemas)
  • Maps between layers
  • Depends only on Domain layer

Infrastructure Layer (Adapters)

  • Implements domain interfaces
  • Handles external concerns (database, web, etc.)
  • Depends on Domain and Application layers
  • Injects dependencies via FastAPI

Example: Generated Code

Domain DTO (Pure Python)

from dataclasses import dataclass

@dataclass
class CreateSchoolDTO:
    name: str
    address: str
    principal_name: str
    student_capacity: int

Domain Model (SQLAlchemy 2.0)

from sqlalchemy.orm import Mapped, mapped_column

class School(Base):
    __tablename__ = "schools"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(200))
    address: Mapped[str]
    principal_name: Mapped[str]
    student_capacity: Mapped[int]

Application Schema (Pydantic V2)

from pydantic import BaseModel, Field

class CreateSchoolRequest(BaseModel):
    name: str = Field(..., min_length=1, max_length=200)
    address: str = Field(..., min_length=1)
    principal_name: str = Field(..., min_length=1)
    student_capacity: int = Field(..., ge=0)

Use Case (Application Layer)

The repository (port) and the UnitOfWork (transaction boundary) are injected separately — both FastAPI dependencies resolve to the same AsyncSession per request, so a unit_of_work.commit() after a repository call commits that call's changes:

class CreateUseCase:
    def __init__(self, *, unit_of_work: UnitOfWork, school_repository: SchoolRepository):
        self.unit_of_work = unit_of_work
        self.school_repository = school_repository

    async def execute(self, *, data: CreateSchoolData) -> School:
        school = await self.school_repository.create(data=data)
        await self.unit_of_work.commit()
        return school

Design Patterns Used

  • Hexagonal Architecture: Clean separation of concerns
  • Repository Pattern: Data access abstraction
  • Unit of Work: Transaction management
  • Dependency Injection: FastAPI Depends()
  • Mapper Pattern: Layer-to-layer conversion
  • Factory Pattern: Generator creation

Architecture Validation

The generator includes a validator that checks:

  • Domain doesn't import Application/Infrastructure
  • Application doesn't import Infrastructure
  • No SQLAlchemy Session in use cases
  • Proper use of DTOs in domain layer
  • Dependency inversion respected
from hexagon_generator.utils import ArchitectureValidator
validator = ArchitectureValidator(Path("generated_project"))
result = validator.validate_module("school")

Docker Usage

# Build image
docker build -f generator.dockerfile -t hexagon-generator:latest .

# Run generator
docker run --name hexagon-generator -p 8069:8069 \
  -v "${PWD}:/mounted_project" \
  hexagon-generator:latest

Project Structure

.
├── code_generator.py        # CLI entry point
├── hexagon_generator/       # Core generator
│   ├── core/               # Generation logic
│   ├── templates/          # Jinja2 templates
│   └── utils/              # Validators, path builders
└── generated_project/      # Default output directory

Customizing Templates

Templates are real Jinja2 .j2 files in hexagon_generator/templates/crud/. Edit them to customize generated code.

They use <<...>> for variables and <%...%> for blocks (instead of Jinja's default {{ }} / {% %}) because the generated output is Python/FastAPI code that itself uses {} and [] constantly (dicts, f-strings, route paths, list[...]); the usual delimiters would collide with that and force escaping.

Documentation

Contributing

Contributions welcome! Areas to improve:

  • Additional generators (GraphQL, gRPC, etc.)
  • Template customization options
  • Test generation
  • Documentation generation

License

MIT — see LICENSE.

Acknowledgments

Built with:

  • FastAPI
  • SQLAlchemy 2.0
  • Pydantic V2
  • Jinja2

Metadata

Release files for fastapi-hexagon 0.3.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fastapi-hexagon 0.3.1
File Size Uploaded Provenance Actions
fastapi_hexagon-0.3.1.tar.gz 114.9 kB Without provenance Details for fastapi_hexagon-0.3.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for fastapi-hexagon 0.3.1
File Interpreter ABI Platform Actions
fastapi_hexagon-0.3.1-py3-none-any.whl Python 3 none any Details for fastapi_hexagon-0.3.1-py3-none-any.whl

Total release size: 271.6 kB

Release files / fastapi_hexagon-0.3.1.tar.gz

Download URL fastapi_hexagon-0.3.1.tar.gz
Size 114.9 kB
Tags Source
SHA-256 checksum
How to use checksums
74c3c8f109d730d0fd147e3e5f403306ffab870d297f7971a17fdf8720ef597b
BLAKE2b-256 checksum
How to use checksums
593206f3be5591001be254de078fb86ef388ade2e13d1d5a848c3c3565fde341
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / fastapi_hexagon-0.3.1-py3-none-any.whl

Download URL fastapi_hexagon-0.3.1-py3-none-any.whl
Size 156.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
da945bd87f578c211825e09db0f3b025c8217431f98da99d92a5e4abfb93c589
BLAKE2b-256 checksum
How to use checksums
bc5ce5f5501975ca6059c51487daad846c476922757207ec53a0e5b4df8ff896
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.11.29 {"installer":{"name":"uv","version":"0.11.29","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Security for fastapi-hexagon 0.3.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page