Python
Skill para criar e manter código Python/FastAPI seguindo arquitetura hexagonal rigorosa.
Princípios Invioláveis
- •Máximo 300 linhas por arquivo - Se exceder, refatorar imediatamente
- •Separação de camadas - Domain nunca importa Infrastructure
- •Dependências apontam para dentro - Infrastructure → Application → Domain
- •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:
- • Criar schema Pydantic em
schemas/(request e response separados) - • Criar/verificar use case em
application/use_cases/ - • Criar/verificar interface de repositório em
domain/repositories/ - • Implementar repositório em
infrastructure/repositories/ - • Criar rota em
presentation/api/v1/routes/ - • Configurar dependency injection em
dependencies/ - • 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