python-fastapi-ddd-skill

Guides FastAPI backend design using Domain-Driven Design (DDD) and Onion Architecture in Python. Use when structuring a FastAPI app (routes/handlers, Pydantic schemas, Depends-based DI), modeling domain Entities/Value Objects, defining repository interfaces, implementing SQLAlchemy infrastructure adapters, or writing use cases, based on the dddpy reference.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "python-fastapi-ddd-skill" with this command: npx skills add iktakahiro/python-fastapi-ddd-skill/iktakahiro-python-fastapi-ddd-skill-python-fastapi-ddd-skill

FastAPI + Python DDD & Onion Architecture Design Guide

Guides FastAPI backend design using DDD principles and Onion Architecture, based on the dddpy reference implementation (FastAPI + SQLAlchemy + Python 3.13+).

Architecture Overview

Four concentric layers with dependencies pointing inward:

Presentation  →  UseCase  →  Infrastructure  →  Domain (innermost)

Key rule: Inner layers never depend on outer layers. The Domain layer has zero external dependencies.

LayerResponsibilityExamples
DomainCore business logic, no framework depsEntities, Value Objects, Repository interfaces, Exceptions
InfrastructureExternal integrationsDB repos, DTOs, DI config, SQLAlchemy models
UseCaseApplication workflowsOne class per use case with execute()
PresentationHTTP API surfaceFastAPI routes, Pydantic schemas, error messages

For detailed architecture guide: See ARCHITECTURE.md

Directory Structure

project/
├── main.py
├── app/
│   ├── domain/
│   │   └── {aggregate}/
│   │       ├── entities/
│   │       ├── value_objects/
│   │       ├── repositories/
│   │       └── exceptions/
│   ├── infrastructure/
│   │   ├── di/
│   │   │   └── injection.py
│   │   └── sqlite/
│   │       └── {aggregate}/
│   │           ├── {aggregate}_dto.py
│   │           └── {aggregate}_repository.py
│   ├── usecase/
│   │   └── {aggregate}/
│   │       └── {action}_{aggregate}_usecase.py
│   └── presentation/
│       └── api/
│           └── {aggregate}/
│               ├── handlers/
│               ├── schemas/
│               └── error_messages/
└── tests/

Quick Reference

1. Entity

Entities have unique identifiers, mutable state, and encapsulated business logic. Equality is based on identity, not attribute values.

class Todo:
    def __init__(self, id: TodoId, title: TodoTitle, status: TodoStatus = TodoStatus.NOT_STARTED):
        self._id = id
        self._title = title
        self._status = status

    def __eq__(self, obj: object) -> bool:
        if isinstance(obj, Todo):
            return self.id == obj.id
        return False

    def start(self) -> None:
        self._status = TodoStatus.IN_PROGRESS

    @staticmethod
    def create(title: TodoTitle) -> "Todo":
        return Todo(TodoId.generate(), title)

Detailed guide: See ENTITIES.md

2. Value Object

Immutable objects defined by their values, not identity. Use @dataclass(frozen=True) with validation in __post_init__.

@dataclass(frozen=True)
class TodoTitle:
    value: str

    def __post_init__(self):
        if not self.value:
            raise ValueError("Title is required")
        if len(self.value) > 100:
            raise ValueError("Title must be 100 characters or less")

Detailed guide: See VALUE_OBJECTS.md

3. Repository Interface

Define abstract interfaces in the Domain layer. Infrastructure implements them.

class TodoRepository(ABC):
    @abstractmethod
    def save(self, todo: Todo) -> None: ...

    @abstractmethod
    def find_by_id(self, todo_id: TodoId) -> Optional[Todo]: ...

    @abstractmethod
    def find_all(self) -> List[Todo]: ...

    @abstractmethod
    def delete(self, todo_id: TodoId) -> None: ...

Detailed guide: See REPOSITORIES.md

4. UseCase

One class per use case. Abstract interface + concrete implementation + factory function.

class CreateTodoUseCase(ABC):
    @abstractmethod
    def execute(self, title: TodoTitle) -> Todo: ...

class CreateTodoUseCaseImpl(CreateTodoUseCase):
    def __init__(self, todo_repository: TodoRepository):
        self.todo_repository = todo_repository

    def execute(self, title: TodoTitle) -> Todo:
        todo = Todo.create(title=title)
        self.todo_repository.save(todo)
        return todo

def new_create_todo_usecase(repo: TodoRepository) -> CreateTodoUseCase:
    return CreateTodoUseCaseImpl(repo)

Detailed guide: See USECASES.md

Best Practices

  1. Keep Domain Layer Pure: No framework imports (no FastAPI, no SQLAlchemy) in domain code
  2. Use DTOs at Layer Boundaries: Convert between domain entities and infrastructure models via to_entity() / from_entity() methods
  3. Dependency Injection: Use FastAPI's Depends() to wire session → repository → usecase → handler
  4. One UseCase = One Responsibility: Each UseCase has exactly one public execute method
  5. Validate in Value Objects: Business rules live in __post_init__ of frozen dataclasses
  6. Domain Exceptions: Create specific exception classes for business rule violations (e.g., TodoNotFoundError, TodoAlreadyCompletedError)
  7. Factory Functions: Expose new_* factory functions for creating implementations, keeping concrete classes as implementation details

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

python-fastapi-ddd-testing-skill

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

python-fastapi-ddd-presentation-skill

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

commit

No summary provided by upstream source.

Repository SourceNeeds Review