AgentSkillsCN

python

以六边形架构创建并重构 Python 控制器/服务。适用于创建 FastAPI 端点、重构大型文件(超过 300 行),或按模块整理代码时使用。确保各层分离,文件结构简洁明了。

SKILL.md
--- frontmatter
name: python
description: Cria e refatora controllers/services Python com arquitetura hexagonal. Use quando criar endpoints FastAPI, refatorar arquivos grandes (>300 linhas), ou organizar código em módulos. Garante separação de camadas e arquivos enxutos.

Python

Skill para criar e manter código Python/FastAPI seguindo arquitetura hexagonal rigorosa.

Princípios Invioláveis

  1. Máximo 300 linhas por arquivo - Se exceder, refatorar imediatamente
  2. Separação de camadas - Domain nunca importa Infrastructure
  3. Dependências apontam para dentro - Infrastructure → Application → Domain
  4. Um motivo para mudar - Cada classe tem responsabilidade única

Estrutura de Camadas

code
src/
├── domain/                    # Regras de negócio puras
│   ├── entities/              # Entidades e Value Objects
│   ├── repositories/          # Interfaces (ABC) de repositórios
│   ├── services/              # Serviços de domínio
│   └── exceptions/            # Exceções de domínio
├── application/               # Casos de uso
│   ├── use_cases/             # Um arquivo por caso de uso
│   ├── dtos/                  # Data Transfer Objects
│   └── interfaces/            # Ports (interfaces para infra)
├── infrastructure/            # Implementações concretas
│   ├── repositories/          # Implementações de repositórios
│   ├── external/              # APIs externas, serviços third-party
│   └── persistence/           # SQLAlchemy models, migrations
└── presentation/              # Controllers/Routers FastAPI
    ├── api/
    │   └── v1/
    │       ├── routes/        # Arquivos de rotas
    │       └── schemas/       # Pydantic schemas (request/response)
    └── dependencies/          # Dependency injection FastAPI

Regras de Refatoração

Quando um arquivo excede 300 linhas

Antes:

code
presentation/api/v1/routes/produtos.py  (450 linhas)

Depois:

code
presentation/api/v1/routes/produtos/
├── __init__.py                # Re-exporta o router
├── router.py                  # Router principal, apenas inclui sub-routers
├── listar.py                  # GET /produtos
├── criar.py                   # POST /produtos
├── atualizar.py               # PUT /produtos/{id}
├── deletar.py                 # DELETE /produtos/{id}
└── schemas.py                 # Schemas específicos do módulo (se necessário)

Padrão de elevação de métodos a classes

Quando um método é complexo demais (>50 linhas ou muitas dependências):

python
# Antes: método grande dentro de um service
class ProdutoService:
    def processar_venda_complexa(self, ...):  # 80 linhas
        ...

# Depois: extrair para use case próprio
# application/use_cases/produtos/processar_venda.py
class ProcessarVendaUseCase:
    def __init__(self, produto_repo, estoque_service, fiscal_service):
        ...
    
    def execute(self, comando: ProcessarVendaCommand) -> ProcessarVendaResult:
        ...

Checklist de Criação de Controller

Ao criar um novo controller/router:

  1. Criar schema Pydantic em schemas/ (request e response separados)
  2. Criar/verificar use case em application/use_cases/
  3. Criar/verificar interface de repositório em domain/repositories/
  4. Implementar repositório em infrastructure/repositories/
  5. Criar rota em presentation/api/v1/routes/
  6. Configurar dependency injection em dependencies/
  7. Verificar se arquivo não excede 300 linhas

Padrões de Código

Router FastAPI

python
# presentation/api/v1/routes/produtos/listar.py
from fastapi import APIRouter, Depends, Query
from src.application.use_cases.produtos import ListarProdutosUseCase
from src.presentation.dependencies import get_listar_produtos_use_case
from .schemas import ProdutoResponse, ListarProdutosParams

router = APIRouter()

@router.get("/", response_model=list[ProdutoResponse])
async def listar_produtos(
    params: ListarProdutosParams = Depends(),
    use_case: ListarProdutosUseCase = Depends(get_listar_produtos_use_case)
):
    return await use_case.execute(params)

Use Case

python
# application/use_cases/produtos/listar.py
from dataclasses import dataclass
from src.domain.repositories import ProdutoRepositoryInterface

@dataclass
class ListarProdutosUseCase:
    repository: ProdutoRepositoryInterface
    
    async def execute(self, params: ListarProdutosParams) -> list[ProdutoDTO]:
        produtos = await self.repository.listar(
            filtros=params.to_filtros(),
            paginacao=params.to_paginacao()
        )
        return [ProdutoDTO.from_entity(p) for p in produtos]

Entity

python
# domain/entities/produto.py
from dataclasses import dataclass, field
from decimal import Decimal
from uuid import UUID, uuid4

@dataclass
class Produto:
    nome: str
    preco: Decimal
    id: UUID = field(default_factory=uuid4)
    
    def aplicar_desconto(self, percentual: Decimal) -> None:
        if percentual < 0 or percentual > 100:
            raise ValueError("Percentual deve estar entre 0 e 100")
        self.preco = self.preco * (1 - percentual / 100)

Comandos de Verificação

Antes de finalizar qualquer alteração:

bash
# Verificar arquivos com mais de 300 linhas
find src -name "*.py" -exec wc -l {} + | awk '$1 > 300 {print}'

# Verificar imports circulares
python -c "import src" 2>&1 | grep -i circular

# Verificar se domain não importa infrastructure
grep -r "from src.infrastructure" src/domain/ && echo "VIOLAÇÃO: Domain importando Infrastructure!"

Decisão: Refatorar ou Criar Novo?

code
Arquivo existe?
├── Não → Criar seguindo estrutura hexagonal
└── Sim → Verificar linhas
    ├── < 250 linhas → Adicionar código normalmente
    ├── 250-300 linhas → Adicionar com cautela, considerar split
    └── > 300 linhas → OBRIGATÓRIO refatorar antes de adicionar

Anti-patterns a Evitar

  • ❌ Controller chamando repositório diretamente
  • ❌ Lógica de negócio em rotas/controllers
  • ❌ Entity com dependência de infraestrutura
  • ❌ Use case retornando SQLAlchemy model
  • ❌ Arquivo "utils.py" ou "helpers.py" genérico (criar módulos específicos)
  • ❌ Imports circulares entre camadas