Padrões de Design Python para Arquitetura Limpa

Construa aplicativos Python mantíveis com padrões de design SOLID

Arquitetura Limpa revolucionou a forma como os desenvolvedores constroem aplicações escaláveis e mantíveis, enfatizando a separação de preocupações e o gerenciamento de dependências.

Em Python, esses princípios combinam-se com a natureza dinâmica da linguagem para criar sistemas flexíveis e testáveis que evoluem com as necessidades do negócio, sem se tornarem dívida técnica.

sala de conferência tecnológica vibrante

Entendendo a Arquitetura Limpa em Python

A Arquitetura Limpa, introduzida por Robert C. Martin (Uncle Bob), organiza o software em camadas concêntricas onde as dependências apontam para dentro da lógica de negócios central. Este padrão arquitetural garante que as regras críticas do negócio da sua aplicação permaneçam independentes de frameworks, bancos de dados e serviços externos.

Filosofia Central

O princípio fundamental é simples, mas poderoso: a lógica de negócios não deve depender da infraestrutura. Suas entidades de domínio, casos de uso e regras de negócio devem funcionar independentemente de você estar usando PostgreSQL ou MongoDB, FastAPI ou Flask, AWS ou Azure.

Em Python, esta filosofia se alinha perfeitamente com a linguagem’s “duck typing” e programação orientada a protocolos, permitindo uma separação limpa sem a cerimônia necessária em linguagens tipadas estaticamente.

As Quatro Camadas da Arquitetura Limpa

Camada de Entidades (Domínio): Objetos de negócios puros com regras de negócios de toda a empresa. Estes são POPOs (Plain Old Python Objects) sem dependências externas.

Camada de Casos de Uso (Aplicação): Regras de negócio específicas da aplicação que orquestram o fluxo de dados entre entidades e serviços externos.

Camada de Adaptores de Interface: Converte dados entre o formato mais conveniente para casos de uso e entidades e o formato exigido por agências externas.

Camada de Frameworks e Drivers: Todos os detalhes externos, como bancos de dados, frameworks web e APIs externas.

Princípios SOLID em Python

Os princípios SOLID formam a base da arquitetura limpa. Vamos explorar como cada princípio se manifesta em Python. Para uma visão abrangente dos padrões de design em Python, veja o Guia de Padrões de Design em Python.

Princípio da Responsabilidade Única (SRP)

Cada classe deve ter um único motivo para mudar:

# Ruim: Múltiplas responsabilidades
class UserManager:
    def create_user(self, user_data):
        # Criar usuário
        pass
    
    def send_welcome_email(self, user):
        # Enviar e-mail
        pass
    
    def log_creation(self, user):
        # Registrar em arquivo
        pass

# Bom: Responsabilidades separadas
class UserService:
    def __init__(self, repository, email_service, logger):
        self.repository = repository
        self.email_service = email_service
        self.logger = logger
    
    def create_user(self, user_data):
        user = User(**user_data)
        self.repository.save(user)
        self.email_service.send_welcome(user)
        self.logger.info(f"Usuário criado: {user.id}")
        return user

Princípio Aberto/Fechado (OCP)

Entidades de software devem estar abertas para extensão, mas fechadas para modificação:

from abc import ABC, abstractmethod
from typing import Protocol

# Usando Protocol (Python 3.8+)
class PaymentProcessor(Protocol):
    def process_payment(self, amount: float) -> bool:
        ...

class CreditCardProcessor:
    def process_payment(self, amount: float) -> bool:
        # Lógica de cartão de crédito
        return True

class PayPalProcessor:
    def process_payment(self, amount: float) -> bool:
        # Lógica do PayPal
        return True

# Fácil de estender sem modificar o código existente
class CryptoProcessor:
    def process_payment(self, amount: float) -> bool:
        # Lógica de criptomoeda
        return True

Princípio da Substituição de Liskov (LSP)

Objetos devem ser substituíveis por seus subtipos sem que o programa quebre:

from abc import ABC, abstractmethod

class DataStore(ABC):
    @abstractmethod
    def save(self, key: str, value: str) -> None:
        pass
    
    @abstractmethod
    def get(self, key: str) -> str:
        pass

class PostgreSQLStore(DataStore):
    def save(self, key: str, value: str) -> None:
        # Implementação do PostgreSQL
        pass
    
    def get(self, key: str) -> str:
        # Implementação do PostgreSQL
        return ""

class RedisStore(DataStore):
    def save(self, key: str, value: str) -> None:
        # Implementação do Redis
        pass
    
    def get(self, key: str) -> str:
        # Implementação do Redis
        return ""

# Ambos podem ser usados de forma intercambiável
def process_data(store: DataStore, key: str, value: str):
    store.save(key, value)
    return store.get(key)

Princípio da Segregação de Interfaces (ISP)

Clientes não devem ser forçados a depender de interfaces que não usam:

# Ruim: Interface pesada
class Worker(ABC):
    @abstractmethod
    def work(self): pass
    
    @abstractmethod
    def eat(self): pass
    
    @abstractmethod
    def sleep(self): pass

# Bom: Interfaces segregadas
class Workable(Protocol):
    def work(self) -> None: ...

class Eatable(Protocol):
    def eat(self) -> None: ...

class Human:
    def work(self) -> None:
        print("Trabalhando")
    
    def eat(self) -> None:
        print("Comendo")

class Robot:
    def work(self) -> None:
        print("Trabalhando")
    # Método de comer não necessário

Princípio da Inversão de Dependência (DIP)

Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações:

from typing import Protocol

# Abstração
class EmailSender(Protocol):
    def send(self, to: str, subject: str, body: str) -> None:
        ...

# Módulo de baixo nível
class SMTPEmailSender:
    def send(self, to: str, subject: str, body: str) -> None:
        # Implementação SMTP
        pass

# Módulo de alto nível depende da abstração
class UserRegistrationService:
    def __init__(self, email_sender: EmailSender):
        self.email_sender = email_sender
    
    def register(self, email: str, name: str):
        # Lógica de registro
        self.email_sender.send(
            to=email,
            subject="Bem-vindo!",
            body=f"Olá {name}"
        )

Padrão de Repositório: Abstraindo o Acesso aos Dados

O Padrão de Repositório fornece uma interface semelhante a uma coleção para acessar objetos de domínio, ocultando os detalhes do armazenamento de dados.

Implementação Básica de Repositório

from abc import ABC, abstractmethod
from typing import List, Optional
from dataclasses import dataclass
from uuid import UUID, uuid4

@dataclass
class User:
    id: UUID
    email: str
    name: str
    is_active: bool = True

class UserRepository(ABC):
    @abstractmethod
    def save(self, user: User) -> User:
        pass
    
    @abstractmethod
    def get_by_id(self, user_id: UUID) -> Optional[User]:
        pass
    
    @abstractmethod
    def get_by_email(self, email: str) -> Optional[User]:
        pass
    
    @abstractmethod
    def list_all(self) -> List[User]:
        pass
    
    @abstractmethod
    def delete(self, user_id: UUID) -> bool:
        pass

Implementação com SQLAlchemy

from sqlalchemy import create_engine, Column, String, Boolean
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session

Base = declarative_base()

class UserModel(Base):
    __tablename__ = 'users'
    
    id = Column(PGUUID(as_uuid=True), primary_key=True)
    email = Column(String, unique=True, nullable=False)
    name = Column(String, nullable=False)
    is_active = Column(Boolean, default=True)

class SQLAlchemyUserRepository(UserRepository):
    def __init__(self, session: Session):
        self.session = session
    
    def save(self, user: User) -> User:
        user_model = UserModel(
            id=user.id,
            email=user.email,
            name=user.name,
            is_active=user.is_active
        )
        self.session.add(user_model)
        self.session.commit()
        return user
    
    def get_by_id(self, user_id: UUID) -> Optional[User]:
        user_model = self.session.query(UserModel).filter(
            UserModel.id == user_id
        ).first()
        
        if not user_model:
            return None
        
        return User(
            id=user_model.id,
            email=user_model.email,
            name=user_model.name,
            is_active=user_model.is_active
        )
    
    def get_by_email(self, email: str) -> Optional[User]:
        user_model = self.session.query(UserModel).filter(
            UserModel.email == email
        ).first()
        
        if not user_model:
            return None
        
        return User(
            id=user_model.id,
            email=user_model.email,
            name=user_model.name,
            is_active=user_model.is_active
        )
    
    def list_all(self) -> List[User]:
        users = self.session.query(UserModel).all()
        return [
            User(
                id=u.id,
                email=u.email,
                name=u.name,
                is_active=u.is_active
            )
            for u in users
        ]
    
    def delete(self, user_id: UUID) -> bool:
        result = self.session.query(UserModel).filter(
            UserModel.id == user_id
        ).delete()
        self.session.commit()
        return result > 0

Repositório em Memória para Testes

class InMemoryUserRepository(UserRepository):
    def __init__(self):
        self.users: dict[UUID, User] = {}
    
    def save(self, user: User) -> User:
        self.users[user.id] = user
        return user
    
    def get_by_id(self, user_id: UUID) -> Optional[User]:
        return self.users.get(user_id)
    
    def get_by_email(self, email: str) -> Optional[User]:
        for user in self.users.values():
            if user.email == email:
                return user
        return None
    
    def list_all(self) -> List[User]:
        return list(self.users.values())
    
    def delete(self, user_id: UUID) -> bool:
        if user_id in self.users:
            del self.users[user_id]
            return True
        return False

Camada de Serviço: Orquestrando a Lógica de Negócio

A Camada de Serviço implementa casos de uso e orquestra o fluxo entre repositórios, serviços externos e lógica de domínio.

from typing import Optional
from uuid import uuid4

class UserAlreadyExistsError(Exception):
    pass

class UserNotFoundError(Exception):
    pass

class UserService:
    def __init__(
        self,
        user_repository: UserRepository,
        email_service: EmailSender,
        event_publisher: 'EventPublisher'
    ):
        self.user_repository = user_repository
        self.email_service = email_service
        self.event_publisher = event_publisher
    
    def register_user(self, email: str, name: str) -> User:
        # Verificar se o usuário já existe
        existing_user = self.user_repository.get_by_email(email)
        if existing_user:
            raise UserAlreadyExistsError(f"Usuário com e-mail {email} já existe")
        
        # Criar novo usuário
        user = User(
            id=uuid4(),
            email=email,
            name=name,
            is_active=True
        )
        
        # Salvar no repositório
        user = self.user_repository.save(user)
        
        # Enviar e-mail de boas-vindas
        self.email_service.send(
            to=user.email,
            subject="Bem-vindo!",
            body=f"Olá {user.name}, bem-vindo à nossa plataforma!"
        )
        
        # Publicar evento
        self.event_publisher.publish('user.registered', {
            'user_id': str(user.id),
            'email': user.email
        })
        
        return user
    
    def deactivate_user(self, user_id: UUID) -> User:
        user = self.user_repository.get_by_id(user_id)
        if not user:
            raise UserNotFoundError(f"Usuário {user_id} não encontrado")
        
        user.is_active = False
        user = self.user_repository.save(user)
        
        self.event_publisher.publish('user.deactivated', {
            'user_id': str(user.id)
        })
        
        return user

Injeção de Dependência em Python

A natureza dinâmica do Python torna a injeção de dependência direta sem a necessidade de frameworks pesados.

Injeção por Construtor

class OrderService:
    def __init__(
        self,
        order_repository: 'OrderRepository',
        payment_processor: PaymentProcessor,
        notification_service: 'NotificationService'
    ):
        self.order_repository = order_repository
        self.payment_processor = payment_processor
        self.notification_service = notification_service
    
    def place_order(self, order_data: dict):
        # Usar dependências injetadas
        pass

Contêiner de Dependência Simples

from typing import Dict, Type, Callable, Any

class Container:
    def __init__(self):
        self._services: Dict[Type, Callable] = {}
        self._singletons: Dict[Type, Any] = {}
    
    def register(self, interface: Type, factory: Callable):
        self._services[interface] = factory
    
    def register_singleton(self, interface: Type, instance: Any):
        self._singletons[interface] = instance
    
    def resolve(self, interface: Type):
        if interface in self._singletons:
            return self._singletons[interface]
        
        factory = self._services.get(interface)
        if factory:
            return factory(self)
        
        raise ValueError(f"Nenhuma inscrição encontrada para {interface}")

# Uso
def create_container() -> Container:
    container = Container()
    
    # Registrar serviços
    container.register_singleton(
        Session,
        sessionmaker(bind=create_engine('postgresql://...'))()
    )
    
    container.register(
        UserRepository,
        lambda c: SQLAlchemyUserRepository(c.resolve(Session))
    )
    
    container.register(
        EmailSender,
        lambda c: SMTPEmailSender()
    )
    
    container.register(
        UserService,
        lambda c: UserService(
            c.resolve(UserRepository),
            c.resolve(EmailSender),
            c.resolve(EventPublisher)
        )
    )
    
    return container

Arquitetura Hexagonal (Portas e Adaptores)

A Arquitetura Hexagonal coloca a lógica de negócios no centro com adaptadores lidando com a comunicação externa.

Definindo Portas (Interfaces)

# Porta de Entrada (Primária)
class CreateUserUseCase(Protocol):
    def execute(self, request: 'CreateUserRequest') -> 'CreateUserResponse':
        ...

# Porta de Saída (Secundária)
class UserPersistencePort(Protocol):
    def save(self, user: User) -> User:
        ...
    
    def find_by_email(self, email: str) -> Optional[User]:
        ...

Implementando Adaptores

from pydantic import BaseModel, EmailStr

# Adaptador de Entrada (API REST)
from fastapi import FastAPI, Depends, HTTPException

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

class CreateUserResponse(BaseModel):
    id: str
    email: str
    name: str

app = FastAPI()

@app.post("/users", response_model=CreateUserResponse)
def create_user(
    request: CreateUserRequest,
    user_service: UserService = Depends(get_user_service)
):
    try:
        user = user_service.register_user(
            email=request.email,
            name=request.name
        )
        return CreateUserResponse(
            id=str(user.id),
            email=user.email,
            name=user.name
        )
    except UserAlreadyExistsError as e:
        raise HTTPException(status_code=400, detail=str(e))

# Adaptador de Saída (Banco de Dados)
# Já implementado como SQLAlchemyUserRepository

Padrões de Design Orientado a Domínio

Objetos de Valor

Objetos imutáveis definidos por seus atributos:

from dataclasses import dataclass
from typing import Pattern
import re

@dataclass(frozen=True)
class Email:
    value: str
    
    EMAIL_PATTERN: Pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
    
    def __post_init__(self):
        if not self.EMAIL_PATTERN.match(self.value):
            raise ValueError(f"E-mail inválido: {self.value}")
    
    def __str__(self):
        return self.value

@dataclass(frozen=True)
class Money:
    amount: float
    currency: str
    
    def __post_init__(self):
        if self.amount < 0:
            raise ValueError("Quantia não pode ser negativa")
        if self.currency not in ['USD', 'EUR', 'GBP']:
            raise ValueError(f"Moeda não suportada: {self.currency}")
    
    def add(self, other: 'Money') -> 'Money':
        if self.currency != other.currency:
            raise ValueError("Não é possível adicionar moedas diferentes")
        return Money(self.amount + other.amount, self.currency)

Agregados

Cluster de objetos de domínio tratados como uma única unidade:

from dataclasses import dataclass, field
from typing import List
from datetime import datetime

@dataclass
class OrderItem:
    product_id: UUID
    quantity: int
    price: Money
    
    def total(self) -> Money:
        return Money(
            self.price.amount * self.quantity,
            self.price.currency
        )

@dataclass
class Order:
    id: UUID
    customer_id: UUID
    items: List[OrderItem] = field(default_factory=list)
    status: str = "pending"
    created_at: datetime = field(default_factory=datetime.now)
    
    def add_item(self, product_id: UUID, quantity: int, price: Money):
        item = OrderItem(product_id, quantity, price)
        self.items.append(item)
    
    def remove_item(self, product_id: UUID):
        self.items = [
            item for item in self.items 
            if item.product_id != product_id
        ]
    
    def total(self) -> Money:
        if not self.items:
            return Money(0, "USD")
        
        return sum(
            (item.total() for item in self.items),
            Money(0, self.items[0].price.currency)
        )
    
    def confirm(self):
        if not self.items:
            raise ValueError("Não é possível confirmar um pedido vazio")
        if self.status != "pending":
            raise ValueError("Pedido já processado")
        self.status = "confirmed"

Eventos de Domínio

Eventos de domínio permitem acoplamento fraco entre componentes e suportam arquiteturas orientadas a eventos. Para sistemas orientados a eventos em escala de produção, considere a implementação de streaming de eventos com serviços como AWS Kinesis—veja Construindo Microserviços Orientados a Eventos com AWS Kinesis para um guia detalhado.

from dataclasses import dataclass
from datetime import datetime
from typing import List, Callable

@dataclass
class DomainEvent:
    occurred_at: datetime = field(default_factory=datetime.now)

@dataclass
class OrderConfirmed(DomainEvent):
    order_id: UUID
    customer_id: UUID
    total: Money

class EventPublisher:
    def __init__(self):
        self._handlers: Dict[Type, List[Callable]] = {}
    
    def subscribe(self, event_type: Type, handler: Callable):
        if event_type not in self._handlers:
            self._handlers[event_type] = []
        self._handlers[event_type].append(handler)
    
    def publish(self, event: DomainEvent):
        event_type = type(event)
        handlers = self._handlers.get(event_type, [])
        for handler in handlers:
            handler(event)

Recursos Modernos do Python para Arquitetura Limpa

As funcionalidades modernas do Python tornam a implementação da arquitetura limpa mais elegante e segura em termos de tipos. Se você precisa de uma referência rápida para a sintaxe e recursos do Python, consulte o Python Cheatsheet.

Dicas de Tipo e Protocolos

from typing import Protocol, runtime_checkable

@runtime_checkable
class Serializable(Protocol):
    def to_dict(self) -> dict:
        ...
    
    @classmethod
    def from_dict(cls, data: dict) -> 'Serializable':
        ...

def serialize(obj: Serializable) -> dict:
    return obj.to_dict()
`

### Pydantic para Validação

```python
from pydantic import BaseModel, Field, validator
from typing import Optional

class CreateUserDTO(BaseModel):
    email: EmailStr
    name: str = Field(..., min_length=2, max_length=100)
    age: Optional[int] = Field(None, ge=0, le=150)
    
    @validator('name')
    def name_must_not_contain_numbers(cls, v):
        if any(char.isdigit() for char in v):
            raise ValueError('Name cannot contain numbers')
        return v
    
    class Config:
        frozen = True  # Torna imutável
`

### Async/Await para Operações de E/S

A sintaxe async/await do Python é particularmente poderosa para operações limitadas por E/S na arquitetura limpa, permitindo interações não bloqueantes com bancos de dados e serviços externos. Ao implantar aplicações Python em plataformas sem servidor, entender as características de desempenho se torna crucialveja [AWS lambda performance: JavaScript vs Python vs Golang](https://www.glukhov.org/pt/post/2024/08/aws-lambda-golang-python-js/ "Desenvolvendo aplicações sem servidor na AWS com sam usando JavaScript, Python e Golang") para insights sobre otimização de funções Python sem servidor.

```python
from typing import List
import asyncio

class AsyncUserRepository(ABC):
    @abstractmethod
    async def save(self, user: User) -> User:
        pass
    
    @abstractmethod
    async def get_by_id(self, user_id: UUID) -> Optional[User]:
        pass

class AsyncUserService:
    def __init__(self, repository: AsyncUserRepository):
        self.repository = repository
    
    async def register_user(self, email: str, name: str) -> User:
        user = User(id=uuid4(), email=email, name=name)
        return await self.repository.save(user)
    
    async def get_users_batch(self, user_ids: List[UUID]) -> List[User]:
        tasks = [self.repository.get_by_id(uid) for uid in user_ids]
        results = await asyncio.gather(*tasks)
        return [u for u in results if u is not None]
`

## Boas Práticas para Estrutura de Projeto

Uma organização adequada do projeto é essencial para manter a arquitetura limpa. Antes de configurar a estrutura do seu projeto, certifique-se de estar usando ambientes virtuais do Python para isolar dependências. O [venv Cheatsheet](https://www.glukhov.org/pt/post/2025/05/python-venv-cheatsheet/ "venv Cheatsheet") aborda tudo o que você precisa saber sobre gerenciamento de ambientes virtuais. Para projetos modernos do Python, considere usar [uv - New Python Package, Project, and Environment Manager](https://www.glukhov.org/pt/post/2025/06/uv-new-python-package-project-and-environment-manager/ "UV é um novo gerenciador de pacotes, projetos e ambientes do Python."), que oferece gerenciamento de pacotes e configuração de projetos mais rápidos.

```text
my_application/
├── domain/                 # Regras de negócio empresariais
   ├── __init__.py
   ├── entities/
      ├── __init__.py
      ├── user.py
      └── order.py
   ├── value_objects/
      ├── __init__.py
      ├── email.py
      └── money.py
   ├── events/
      ├── __init__.py
      └── user_events.py
   └── exceptions.py

├── application/            # Regras de negócio da aplicação
   ├── __init__.py
   ├── use_cases/
      ├── __init__.py
      ├── create_user.py
      └── place_order.py
   ├── services/
      ├── __init__.py
      └── user_service.py
   └── ports/
       ├── __init__.py
       ├── repositories.py
       └── external_services.py

├── infrastructure/         # Interfaces externas
   ├── __init__.py
   ├── persistence/
      ├── __init__.py
      ├── sqlalchemy/
         ├── models.py
         └── repositories.py
      └── mongodb/
          └── repositories.py
   ├── messaging/
      ├── __init__.py
      └── rabbitmq_publisher.py
   ├── external_services/
      ├── __init__.py
      └── email_service.py
   └── config.py

├── presentation/           # Camada de UI/API
   ├── __init__.py
   ├── api/
      ├── __init__.py
      ├── dependencies.py
      ├── routes/
         ├── __init__.py
         ├── users.py
         └── orders.py
      └── schemas/
          ├── __init__.py
          └── user_schemas.py
   └── cli/
       └── commands.py

├── tests/
   ├── unit/
   ├── integration/
   └── e2e/

├── main.py                 # Ponto de entrada da aplicação
├── container.py            # Configuração de injeção de dependência
├── pyproject.toml
└── README.md
`

## Testando Arquitetura Limpa

### Teste Unitário da Lógica de Domínio

```python
import pytest
from uuid import uuid4

def test_user_creation():
    user = User(
        id=uuid4(),
        email="test@example.com",
        name="Test User"
    )
    assert user.email == "test@example.com"
    assert user.is_active is True

def test_order_total_calculation():
    order = Order(id=uuid4(), customer_id=uuid4())
    order.add_item(
        uuid4(),
        quantity=2,
        price=Money(10.0, "USD")
    )
    order.add_item(
        uuid4(),
        quantity=1,
        price=Money(5.0, "USD")
    )
    assert order.total().amount == 25.0
`

### Teste de Integração com Repositório

```python
@pytest.fixture
def in_memory_repository():
    return InMemoryUserRepository()

def test_user_repository_save_and_retrieve(in_memory_repository):
    user = User(
        id=uuid4(),
        email="test@example.com",
        name="Test User"
    )
    
    saved_user = in_memory_repository.save(user)
    retrieved_user = in_memory_repository.get_by_id(user.id)
    
    assert retrieved_user is not None
    assert retrieved_user.email == user.email
`

### Teste da Camada de Serviço

```python
from unittest.mock import Mock

def test_user_registration():
    # Arrange
    mock_repository = Mock(spec=UserRepository)
    mock_repository.get_by_email.return_value = None
    mock_repository.save.return_value = User(
        id=uuid4(),
        email="test@example.com",
        name="Test"
    )
    
    mock_email = Mock(spec=EmailSender)
    mock_events = Mock(spec=EventPublisher)
    
    service = UserService(mock_repository, mock_email, mock_events)
    
    # Act
    user = service.register_user("test@example.com", "Test")
    
    # Assert
    assert user.email == "test@example.com"
    mock_repository.save.assert_called_once()
    mock_email.send.assert_called_once()
    mock_events.publish.assert_called_once()
`

## Armadilhas Comuns e Como Evitá-las

### Superdimensionamento

Não implemente a arquitetura limpa para aplicações simples de CRUD. Comece simples e refatore conforme a complexidade cresce.

### Abstrações Vazias

Certifique-se de que as entidades do domínio não contenham anotações de banco de dados ou código específico de framework:

```python
# Ruim
from sqlalchemy import Column

@dataclass
class User:
    id: Column(Integer, primary_key=True)  # Código do framework infiltrando no domínio

# Bom
@dataclass
class User:
    id: UUID  # Objeto puro do domínio
`

### Dependências Circulares

Use injeção de dependência e interfaces para quebrar dependências circulares entre camadas.

### Ignorar o Contexto

A arquitetura limpa não é uma solução universal. Ajuste a rigidez das camadas com base no tamanho do projeto e na expertise da equipe.

## Links Úteis

- [Arquitetura Limpa por Robert C. Martin](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)
- [Documentação de Dicas de Tipo do Python](https://docs.python.org/3/library/typing.html)
- [Documentação do Pydantic](https://docs.pydantic.dev/)
- [Documentação Oficial do FastAPI](https://fastapi.tiangolo.com/)
- [Documentação ORM do SQLAlchemy](https://docs.sqlalchemy.org/)
- [Biblioteca de Injeção de Dependência](https://python-dependency-injector.ets-labs.org/)
- [Referência de Design Orientado a Domínio](https://www.domainlanguage.com/ddd/reference/)
- [Padrões de Arquitetura com Python](https://www.cosmicpython.com/)
- [Blog de Martin Fowler sobre Arquitetura](https://martinfowler.com/architecture/)
- [Guia de Padrões de Projeto em Python](https://refactoring.guru/design-patterns/python)
- [Python Cheatsheet](https://www.glukhov.org/pt/post/2024/08/python-cheat-sheet/ "Python Cheatsheet")
- [venv Cheatsheet](https://www.glukhov.org/pt/post/2025/05/python-venv-cheatsheet/ "venv Cheatsheet")
- [uv - New Python Package, Project, and Environment Manager](https://www.glukhov.org/pt/post/2025/06/uv-new-python-package-project-and-environment-manager/ "UV é um novo gerenciador de pacotes, projetos e ambientes do Python.")
- [AWS lambda performance: JavaScript vs Python vs Golang](https://www.glukhov.org/pt/post/2024/08/aws-lambda-golang-python-js/ "Desenvolvendo aplicações sem servidor na AWS com sam usando JavaScript, Python e Golang")
- [Construindo Microserviços Event-Driven com AWS Kinesis](https://www.glukhov.org/pt/post/2025/11/service-oriented-and-microservices-with-aws-kinesis/ "Guia abrangente para implementar uma arquitetura de microserviços event-driven escalável usando AWS Kinesis Data Streams, incluindo processamento de dados em tempo real, desacoplamento de serviços e melhores práticas para implantações em produção.")