Acesse o painel da sua conta

Não tem uma conta? Registrar

Entrar em contato

Visite também nosso site craftxp.com.br

  • img
  • img
  • img
  • img
  • img
  • img

Entre em contato

Clean Architecture com Python: Princípios, Padrões e Implementação Prática

Clean Architecture com Python: Princípios, Padrões e Implementação Prática

O que é Clean Architecture?

Criada por Robert C. Martin (Uncle Bob), a Clean Architecture é um conjunto de princípios de design de software que visa criar sistemas com código sustentável, testável e independente de detalhes externos como frameworks, bibliotecas e bancos de dados. A ideia central é organizar o código em camadas concêntricas, onde as regras de negócio ficam no centro e as dependências apontam sempre para dentro.

O conceito é uma evolução de outras arquiteturas como Hexagonal (Ports and Adapters), Onion Architecture e DCI (Data, Context, Interaction). Todas compartilham o mesmo objetivo: proteger o domínio do negócio de vazamentos de infraestrutura.

Por que Clean Architecture em 2026?

Com a crescente complexidade dos sistemas modernos — microsserviços, múltiplos bancos de dados, filas, APIs externas, frontends variados — a necessidade de uma arquitetura que isole o core do negócio nunca foi tão crítica:

  • Manutenibilidade: mudar um framework não quebra as regras de negócio
  • Testabilidade: testes unitários rodam sem banco, sem HTTP, sem dependências externas
  • Independência de frameworks: Django vira detalhe, Flask vira detalhe — o domínio permanece
  • Troca de tecnologia: migre de PostgreSQL para MongoDB ou de REST para GraphQL sem tocar no core
  • Equipes paralelas: times diferentes trabalham em camadas diferentes com contratos claros

As Camadas da Clean Architecture

A arquitetura é visualizada como círculos concêntricos. As setas de dependência sempre apontam de fora para dentro:

  • Camada de Domínio (Entities): o centro. Contém as regras de negócio mais fundamentais e os modelos de dados do domínio. Não depende de nada externo.
  • Camada de Casos de Uso (Use Cases): orquestra o fluxo da aplicação. Coordena entidades e chama portas de saída (interfaces).
  • Camada de Adaptadores (Interface Adapters): converte dados entre o formato dos casos de uso e o formato de agentes externos (controllers, presenters, gateways).
  • Camada de Frameworks e Drivers: o círculo mais externo. Banco de dados, frameworks web, APIs externas, sistemas de arquivos.
# Exemplo: Entidade de Domínio sem dependências externas
from dataclasses import dataclass
from datetime import date
from decimal import Decimal
from enum import Enum

class StatusPedido(Enum):
    PENDENTE = "pendente"
    PAGO = "pago"
    ENVIADO = "enviado"
    ENTREGUE = "entregue"
    CANCELADO = "cancelado"

@dataclass
class Pedido:
    id: int
    cliente_id: int
    itens: list
    total: Decimal
    status: StatusPedido
    criado_em: date

    def calcular_total(self) -> Decimal:
        return sum(item.preco * item.quantidade for item in self.itens)

    def pode_ser_cancelado(self) -> bool:
        return self.status in (StatusPedido.PENDENTE, StatusPedido.PAGO)

Casos de Uso: O Coração da Aplicação

Os casos de uso contêm a lógica de aplicação específica. Eles coordenam entidades e dependem de interfaces (nunca de implementações concretas):

# Interface de saída (port) — definida na camada de domínio
from abc import ABC, abstractmethod

class RepositorioPedidos(ABC):
    @abstractmethod
    def buscar_por_id(self, pedido_id: int) -> Pedido | None: ...

    @abstractmethod
    def salvar(self, pedido: Pedido) -> Pedido: ...


# Caso de uso concreto
class CancelarPedidoUseCase:
    def __init__(self, repo: RepositorioPedidos):
        self._repo = repo  # Depende da abstração, não da implementação

    def executar(self, pedido_id: int) -> Pedido:
        pedido = self._repo.buscar_por_id(pedido_id)
        if not pedido:
            raise ValueError(f"Pedido {pedido_id} não encontrado")

        if not pedido.pode_ser_cancelado():
            raise ValueError(
                f"Pedido {pedido_id} não pode ser cancelado (status: {pedido.status.value})"
            )

        pedido.status = StatusPedido.CANCELADO
        return self._repo.salvar(pedido)

Adaptadores: Fazendo a Ponte

Os adaptadores implementam as interfaces definidas no domínio e traduzem dados entre o mundo externo e os casos de uso:

# Adaptador de repositório — implementa a interface do domínio
import sqlite3

class RepositorioPedidosSQLite(RepositorioPedidos):
    def __init__(self, conn: sqlite3.Connection):
        self._conn = conn

    def buscar_por_id(self, pedido_id: int) -> Pedido | None:
        cursor = self._conn.execute(
            "SELECT id, cliente_id, total, status, criado_em FROM pedidos WHERE id = ?",
            (pedido_id,),
        )
        row = cursor.fetchone()
        if not row:
            return None
        return Pedido(
            id=row[0], cliente_id=row[1], total=Decimal(row[2]),
            status=StatusPedido(row[3]), criado_em=date.fromisoformat(row[4]), itens=[]
        )

    def salvar(self, pedido: Pedido) -> Pedido:
        self._conn.execute(
            "UPDATE pedidos SET status = ? WHERE id = ?",
            (pedido.status.value, pedido.id),
        )
        self._conn.commit()
        return pedido


# Controller HTTP — adaptador de entrada
from flask import Blueprint, jsonify, request

pedidos_bp = Blueprint("pedidos", __name__)

@pedidos_bp.route("/api/pedidos//cancelar", methods=["POST"])
def cancelar_pedido(id: int):
    repo = RepositorioPedidosSQLite(get_db())
    use_case = CancelarPedidoUseCase(repo)

    try:
        pedido = use_case.executar(id)
        return jsonify({"id": pedido.id, "status": pedido.status.value})
    except ValueError as e:
        return jsonify({"erro": str(e)}), 400

Injeção de Dependência: O Padrão Fundamental

A Inversão de Dependência (DIP) é o mecanismo que permite que a camada externa forneça implementações para o centro sem que o centro conheça os detalhes. O Composition Root é o local onde todas as dependências são injetadas:

# Composition Root — único lugar que conhece todas as implementações concretas
def criar_app():
    # Configurar dependências
    conn = sqlite3.connect("pedidos.db")
    repo = RepositorioPedidosSQLite(conn)

    # Criar casos de uso com dependências injetadas
    cancelar_use_case = CancelarPedidoUseCase(repo)

    # Configurar Flask com os casos de uso
    app = Flask(__name__)
    app.config["cancelar_use_case"] = cancelar_use_case
    app.register_blueprint(pedidos_bp)

    return app

Benefícios no Mundo Real

Empresas que adotam Clean Architecture relatam benefícios mensuráveis:

  • Redução de bugs: regras de negócio isoladas são mais fáceis de testar e validar
  • Onboarding mais rápido: novos desenvolvedores entendem o domínio sem precisar conhecer detalhes de infraestrutura
  • Menos regressão: mudar o banco de dados ou o framework web não quebra testes de domínio
  • Facilidade de migração: empresas que migraram de monolitos para microsserviços encontraram a Clean Architecture como facilitadora

Cuidados e Armadilhas

Clean Architecture não é uma bala de prata. É importante evitar alguns desvios comuns:

  • Overengineering: para CRUDs simples ou MVPs, a complexidade extra pode não valer a pena
  • Fanatismo de camadas: nem todo projeto precisa de 4+ camadas; adapte ao tamanho do time e do sistema
  • Acoplamento prematuro: o DIP deve ser usado onde traz valor, não em toda interface imaginável
  • Models anêmicos: entidades sem comportamento viram meros DTOs — o domínio precisa de lógica de verdade

Conclusão

A Clean Architecture é mais que um padrão arquitetural — é uma filosofia de design que coloca o domínio do negócio no centro de tudo. Seus princípios de separação de responsabilidades, inversão de dependências e isolamento de frameworks resultam em sistemas que envelhecem bem, são fáceis de testar e resistem à obsolescência tecnológica. Comece aplicando em um módulo do seu sistema atual, sinta a diferença na testabilidade e, com o tempo, expanda para todo o projeto. Seu eu do futuro agradecerá.

Craft XP
Craft XP