本文へ移動
cccskills
無料GitHub で公開

clean-architecture

Provides implementation patterns for Clean Architecture, Hexagonal Architecture (Ports & Adapters), and Domain-Driven Design in Python applications with FastAPI or Flask. Use when designing maintainable backends with separation of concerns, implementing repository patterns, creating entities/value objects/aggregates, or structuring domain logic independent of frameworks for testability.

インストール方法を見る

含まれるファイル(3)

  • SKILL.md16.0 KB
  • references/fastapi-implementation.md19.4 KB
  • references/python-clean-architecture.md14.1 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Clean Architecture, DDD & Hexagonal Architecture for Python

Overview

This skill provides comprehensive guidance for implementing Clean Architecture, Hexagonal Architecture (Ports & Adapters), and Domain-Driven Design patterns in Python applications. It focuses on creating maintainable, testable, and framework-independent business logic through proper separation of concerns.

Core Concepts

Layered Architecture (Clean Architecture) - Dependencies flow inward, inner layers know nothing about outer layers:

+-------------------------------------+
|  Infrastructure (Frameworks, DB)   |  <- Outer layer
+-------------------------------------+
|  Adapters (Controllers, Repos)     |
+-------------------------------------+
|  Use Cases (Application Logic)     |
+-------------------------------------+
|  Domain (Entities, Value Objects)  |  <- Inner layer
+-------------------------------------+

Layers:

  • Domain: Entities, value objects, domain events, repository interfaces
  • Use Cases: Application business rules, orchestrate domain objects
  • Adapters: Interface implementations (controllers, repositories, gateways)
  • Infrastructure: Framework configuration, database connections, external clients

Hexagonal Architecture (Ports & Adapters)

  • Ports: Abstract interfaces defining what the application needs
  • Adapters: Concrete implementations of ports
  • Domain Core: Business logic with no external dependencies

Domain-Driven Design Tactical Patterns

  • Entities: Objects with identity and lifecycle
  • Value Objects: Immutable objects defined by attributes
  • Aggregates: Consistency boundaries with aggregate roots
  • Repositories: Persistence abstraction for aggregates
  • Domain Events: Capture significant occurrences in the domain

When to Use

  • Designing new Python backend systems with separation of concerns
  • Refactoring tightly coupled code into layered architectures
  • Implementing domain-driven design with bounded contexts
  • Creating testable business logic independent of frameworks
  • Building applications with FastAPI or Flask using clean patterns
  • Setting up repository patterns with SQLAlchemy or async databases
  • Implementing use case patterns with proper dependency injection

Instructions

1. Define the Project Structure

Create the layered directory structure following the dependency rule:

myapp/
+-- domain/                    # Inner layer - no external deps
|   +-- entities/             # Business entities
|   +-- value_objects/        # Immutable value objects
|   +-- events/               # Domain events
|   +-- repositories/         # Abstract repository interfaces (ports)
+-- use_cases/                # Application layer
+-- adapters/                 # Interface adapters
|   +-- repositories/         # Repository implementations
|   +-- controllers/          # API controllers
+-- infrastructure/           # Framework & external concerns
|   +-- database.py          # Database configuration
|   +-- container.py         # Dependency injection container
|   +-- config.py            # Application settings
+-- main.py                  # Application entry point

2. Implement the Domain Layer

Start from the innermost layer with no external dependencies:

  1. Create Value Objects using frozen dataclasses with validation in __post_init__
  2. Define Entities with identity, behavior, and factory methods (e.g., create())
  3. Define Repository Interfaces (Ports) as abstract base classes with abstract methods
  4. Keep all domain logic in entities - avoid anemic models

3. Implement the Use Cases Layer

Create application-specific business rules:

  1. Define Request/Response dataclasses for input/output
  2. Create Use Case classes that receive repository interfaces via constructor injection
  3. Implement the execute() method that orchestrates domain objects
  4. Handle validation and business errors, returning appropriate responses

4. Implement the Adapter Layer

Create concrete implementations of domain interfaces:

  1. Implement Repository classes that extend domain interfaces
  2. Use SQLAlchemy async sessions or other ORM tools
  3. Map between domain entities and database models
  4. Create Controllers (FastAPI routers) that invoke use cases

5. Implement the Infrastructure Layer

Configure frameworks and external dependencies:

  1. Set up database connections and session management
  2. Configure the dependency injection container
  3. Wire all components together
  4. Define application settings and configuration

6. Create the Application Entry Point

Build the FastAPI or Flask application:

  1. Initialize the DI container and wire modules
  2. Configure application lifespan (startup/shutdown)
  3. Register routers and middleware
  4. Export the application factory function

7. Write Tests

Test each layer in isolation:

  1. Unit test use cases with mocked repositories
  2. Unit test domain entities and value objects
  3. Integration test adapters with test databases
  4. End-to-end test the full application stack

Examples

Example 1: Domain Layer - Value Object & Entity

# domain/value_objects/email.py
from dataclasses import dataclass
import re

@dataclass(frozen=True)
class Email:
    value: str
    def __post_init__(self):
        if not re.match(r'^[\w\.-]+@[\w\.-]+\.\w+$', self.value):
            raise ValueError(f"Invalid email: {self.value}")
    def __str__(self) -> str:
        return self.value


# domain/entities/user.py
from dataclasses import dataclass, field
from datetime import datetime
from uuid import UUID, uuid4
from domain.value_objects.email import Email

@dataclass
class User:
    email: Email
    name: str
    id: UUID = field(default_factory=uuid4)
    is_active: bool = True
    created_at: datetime = field(default_factory=datetime.utcnow)

    def deactivate(self) -> None:
        self.is_active = False

    def can_login(self) -> bool:
        return self.is_active

    @classmethod
    def create(cls, email: Email, name: str) -> "User":
        return cls(email=email, name=name)

Example 2: Repository Port (Interface)

# domain/repositories/user_repository.py
from abc import ABC, abstractmethod
from typing import Optional
from uuid import UUID
from domain.entities.user import User
from domain.value_objects.email import Email

class IUserRepository(ABC):
    @abstractmethod
    async def find_by_id(self, user_id: UUID) -> Optional[User]: ...
    @abstractmethod
    async def find_by_email(self, email: Email) -> Optional[User]: ...
    @abstractmethod
    async def save(self, user: User) -> User: ...
    @abstractmethod
    async def delete(self, user_id: UUID) -> bool: ...

Example 3: Use Case Layer

# use_cases/create_user.py
from dataclasses import dataclass
from typing import Optional
from uuid import UUID
from domain.entities.user import User
from domain.value_objects.email import Email
from domain.repositories.user_repository import IUserRepository

@dataclass
class CreateUserRequest:
    email: str
    name: str

@dataclass
class CreateUserResponse:
    user_id: Optional[UUID]
    success: bool
    error_message: Optional[str] = None

class CreateUserUseCase:
    def __init__(self, user_repository: IUserRepository):
        self._user_repository = user_repository

    async def execute(self, request: CreateUserRequest) -> CreateUserResponse:
        try:
            email = Email(request.email)
        except ValueError as e:
            return CreateUserResponse(None, False, str(e))
        if await self._user_repository.find_by_email(email):
            return CreateUserResponse(None, False, "Email already registered")
        user = User.create(email=email, name=request.name)
        saved = await self._user_repository.save(user)
        return CreateUserResponse(saved.id, True)

Example 4: Adapter Layer - Repository Implementation

# adapters/repositories/sqlalchemy_user_repository.py
from typing import Optional
from uuid import UUID
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from domain.entities.user import User
from domain.value_objects.email import Email
from domain.repositories.user_repository import IUserRepository

class SQLAlchemyUserRepository(IUserRepository):
    def __init__(self, session: AsyncSession):
        self._session = session

    async def find_by_id(self, user_id: UUID) -> Optional[User]:
        result = await self._session.execute(
            select(UserModel).where(UserModel.id == user_id)
        )
        row = result.scalar_one_or_none()
        return self._to_entity(row) if row else None

    async def find_by_email(self, email: Email) -> Optional[User]:
        result = await self._session.execute(
            select(UserModel).where(UserModel.email == str(email))
        )
        row = result.scalar_one_or_none()
        return self._to_entity(row) if row else None

    async def save(self, user: User) -> User:
        model = UserModel(
            id=user.id, email=str(user.email), name=user.name,
            is_active=user.is_active, created_at=user.created_at
        )
        self._session.add(model)
        await self._session.commit()
        return user

    def _to_entity(self, model) -> User:
        return User(
            id=model.id, email=Email(model.email), name=model.name,
            is_active=model.is_active, created_at=model.created_at
        )

Example 5: Dependency Injection Container

# infrastructure/container.py
from dependency_injector import containers, providers
from adapters.repositories.sqlalchemy_user_repository import SQLAlchemyUserRepository
from use_cases.create_user import CreateUserUseCase
from infrastructure.database import get_session

class Container(containers.DeclarativeContainer):
    db_session = providers.Factory(get_session)
    user_repository = providers.Factory(SQLAlchemyUserRepository, session=db_session)
    create_user_use_case = providers.Factory(
        CreateUserUseCase, user_repository=user_repository
    )

Example 6: FastAPI Controller

# adapters/controllers/user_controller.py
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import BaseModel, EmailStr
from use_cases.create_user import CreateUserUseCase, CreateUserRequest
from infrastructure.container import Container
from dependency_injector.wiring import inject, Provide

router = APIRouter(prefix="/users", tags=["users"])

class CreateUserInput(BaseModel):
    email: EmailStr
    name: str

@router.post("/", status_code=status.HTTP_201_CREATED)
@inject
async def create_user(
    data: CreateUserInput,
    use_case: CreateUserUseCase = Depends(Provide[Container.create_user_use_case])
):
    request = CreateUserRequest(email=data.email, name=data.name)
    response = await use_case.execute(request)
    if not response.success:
        raise HTTPException(status_code=400, detail=response.error_message)
    return {"id": str(response.user_id)}

Example 7: Application Entry Point

# main.py
from fastapi import FastAPI
from contextlib import asynccontextmanager
from adapters.controllers import user_controller
from infrastructure.container import Container
from infrastructure.database import init_db

@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_db()
    yield

def create_app() -> FastAPI:
    container = Container()
    container.wire(modules=[user_controller])
    app = FastAPI(title="Clean Architecture API", lifespan=lifespan)
    app.container = container
    app.include_router(user_controller.router)
    return app

app = create_app()

Example 8: Unit Testing Use Cases

# tests/unit/test_create_user_use_case.py
import pytest
from unittest.mock import AsyncMock
from use_cases.create_user import CreateUserUseCase, CreateUserRequest
from domain.entities.user import User
from domain.value_objects.email import Email

@pytest.fixture
def mock_repository():
    return AsyncMock()

@pytest.fixture
def use_case(mock_repository):
    return CreateUserUseCase(user_repository=mock_repository)

@pytest.mark.asyncio
async def test_create_user_success(use_case, mock_repository):
    mock_repository.find_by_email.return_value = None
    mock_repository.save.return_value = User(
        email=Email("test@example.com"), name="Test User"
    )
    request = CreateUserRequest(email="test@example.com", name="Test User")
    response = await use_case.execute(request)
    assert response.success is True
    assert response.user_id is not None

@pytest.mark.asyncio
async def test_create_user_duplicate_email(use_case, mock_repository):
    mock_repository.find_by_email.return_value = AsyncMock()
    request = CreateUserRequest(email="test@example.com", name="Test User")
    response = await use_case.execute(request)
    assert response.success is False
    assert "already registered" in response.error_message

Best Practices

  1. Dependency Rule: Dependencies must always point inward toward the domain - never outward
  2. Immutable Value Objects: Always use frozen dataclasses for value objects with validation in __post_init__
  3. Rich Domain Models: Put business logic in entities, not in services or use cases
  4. Use Cases as Orchestrators: Use cases coordinate workflows but domain objects make decisions
  5. Async by Default: Use async/await for all I/O operations to support modern async frameworks
  6. Pydantic at Boundary: Use Pydantic models only at the API boundary, never in domain layer
  7. Repository per Aggregate: Create one repository per aggregate root, not per entity
  8. Factory Methods: Use @classmethod factory methods like create() for entity construction with invariants
  9. Dependency Injection: Inject dependencies through constructors for testability
  10. Structured Responses: Return structured response objects from use cases, not raw entities

Constraints and Warnings

Architecture Constraints

  • Dependency Rule: Dependencies must always point inward toward the domain - never outward
  • Framework Independence: Domain layer must have no framework dependencies (no FastAPI, SQLAlchemy, Pydantic imports)
  • Interface Segregation: Keep repository interfaces focused and small - avoid god interfaces
  • Repository per Aggregate: Create one repository per aggregate root, not per entity

Implementation Constraints

  • Immutable Value Objects: Always use frozen dataclasses for value objects
  • Rich Domain Models: Put business logic in entities, not in services or use cases
  • Use Cases as Orchestrators: Use cases coordinate workflows but domain objects make decisions
  • Async by Default: Use async/await for all I/O operations to support modern async frameworks
  • Pydantic at Boundary: Use Pydantic models only at the API boundary, not in domain layer

Common Pitfalls to Avoid

  • Anemic Domain Models: Entities with only getters/setters and no behavior violate DDD principles
  • Leaky Abstractions: ORM models leaking into domain layer creates tight coupling
  • Fat Controllers: Business logic in controllers instead of use cases defeats the architecture
  • Missing Abstractions: Direct database calls in use cases break the dependency rule
  • Circular Dependencies: Be careful with imports between layers - use dependency injection to avoid
  • Over-Engineering: Not every CRUD app needs full DDD - evaluate complexity before applying

References

  • references/python-clean-architecture.md - Python-specific patterns including Result type, Specification pattern, Event Bus, and manual DI
  • references/fastapi-implementation.md - Complete FastAPI example with middleware, Docker setup, and integration tests

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Creates new Architecture Decision Record (ADR) documents for significant architectural changes using a consistent template and repository-aware naming and storage guidance. Use when a user or agent decides on an architectural change, needs to document technical rationale, or wants to add a new ADR to the project history.

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

aws-cdk

無料

Provides AWS CDK TypeScript patterns for defining, validating, and deploying AWS infrastructure as code. Use when creating CDK apps, stacks, and reusable constructs, modeling serverless or VPC-based architectures, applying IAM and encryption defaults, or testing and reviewing `cdk synth`, `cdk diff`, and `cdk deploy` changes. Triggers include "aws cdk typescript", "create cdk app", "cdk stack", "cdk construct", "cdk deploy", and "cdk test".

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

Provides advanced AWS CLI patterns for managing EC2, Lambda, S3, DynamoDB, RDS, VPC, IAM, and CloudWatch. Generates bulk operation scripts, automates cross-service workflows, validates security configurations, and executes JMESPath queries for complex filtering. Triggers on "aws cli help", "aws command line", "aws scripting", "aws automation", "aws batch operations", "aws bulk operations", "aws cli pagination", "aws multi-region", "aws profiles", "aws cli troubleshooting".

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

Provides AWS CloudFormation patterns for Auto Scaling including EC2, ECS, and Lambda. Use when creating Auto Scaling groups, launch configurations, launch templates, scaling policies, lifecycle hooks, and predictive scaling. Covers template structure with Parameters, Outputs, Mappings, Conditions, cross-stack references, and best practices for high availability and cost optimization.

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

Provides AWS CloudFormation patterns for Amazon Bedrock resources including agents, knowledge bases, data sources, guardrails, prompts, flows, and inference profiles. Use when creating Bedrock agents with action groups, implementing RAG with knowledge bases, configuring vector stores, setting up content moderation guardrails, managing prompts, orchestrating workflows with flows, and configuring inference profiles for model optimization.

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

Provides AWS CloudFormation patterns for CloudFront distributions, origins (ALB, S3, Lambda@Edge, VPC Origins), CacheBehaviors, Functions, SecurityHeaders, parameters, Outputs and cross-stack references. Use when creating CloudFront distributions with CloudFormation, configuring multiple origins, implementing caching strategies, managing custom domains with ACM, configuring WAF, and optimizing performance.

日本語の概要は準備中です。原文の説明を表示しています。

giuseppe-trisciuoglio/developer-kit3572026年9月10日 更新

giuseppe-trisciuoglio のスキルをすべて見る

このスキルの問題を報告する