Toda decisão de arquitetura começa com uma escolha implícita ou explícita de estilo. Você pode chamar de framework mental, de template estrutural, ou simplesmente de "como vamos organizar esse sistema". O nome que a comunidade consolida é estilo arquitetural: um conjunto de decisões estruturais recorrentes que moldam a forma geral de um sistema — como seus elementos são organizados, como se comunicam, e quais propriedades emergem dessa organização.
A confusão mais comum é misturar estilo arquitetural com padrão de design. Um padrão de design (Factory Method, Observer, Strategy) é uma solução para um problema local e repetível — dentro de uma classe, entre duas classes, em um módulo. Um estilo arquitetural opera em escala maior: define a estrutura do sistema inteiro, os papéis dos seus grandes blocos, e as regras de dependência entre eles. Outro nível acima está a decisão de infraestrutura: onde o sistema roda, como é deployado, quais serviços externos usa. As três camadas coexistem e se complementam, mas operam em escalas diferentes.
Os estilos principais e o que os define
Layered (em camadas) pergunta: como separo responsabilidades por nível de abstração? Presentation depende de Application; Application depende de Domain; Domain não depende de ninguém abaixo. O modelo mental é intuitivo, o onboarding é rápido, e o custo é a tendência de as camadas vazarem ao longo do tempo.
Hexagonal (Ports & Adapters) pergunta: como isolo o domínio de tudo que é tecnologia? O domínio fica no centro; HTTP, banco, filas e email são adaptadores. O sistema todo pode ser testado sem banco, sem HTTP, trocando adaptadores reais por fakes.
Clean Architecture pergunta o mesmo que Hexagonal, mas adiciona um anel de Use Cases explícito entre o domínio puro (Entities) e o mundo externo. A contribuição de Robert Martin (2012) foi formalizar que Use Cases são o coração de uma aplicação, e que frameworks, banco, e UI são detalhes.
Microservices pergunta: como permito que times diferentes deployem de forma independente? O custo é alto: comunicação via rede, consistência eventual, debugging distribuído. Microservices não resolvem problema técnico — resolvem problema organizacional.
Event-Driven pergunta: como permito que partes do sistema evoluam de forma desacoplada no tempo? Componentes produzem eventos; outros consomem sem que o produtor precise conhecer o consumidor.
Monolito modular não é um estilo arquitetural formal, mas é uma resposta pragmática: a disciplina de delimitar módulos com interfaces claras dentro de um único processo. A maioria dos sistemas deveria começar aqui.
Os critérios de escolha
Complexidade do domínio. Quando as regras de negócio são ricas e mudam com frequência, o domínio precisa de proteção estrutural. Hexagonal ou Clean Architecture permitem que o domínio evolua sem ser contaminado por decisões de tecnologia.
Acoplamento e mudança. A pergunta prática é: quando um requisito muda, quantos arquivos você precisa tocar? O estilo arquitetural determina em grande parte a topologia de dependências — e, por extensão, a topologia das mudanças.
Testabilidade. O estilo arquitetural define o custo de testar. Em Layered com Domain chamando Infrastructure diretamente, testar o domínio requer banco. Em Hexagonal, o domínio é testável sem nenhuma dependência externa.
Tamanho e estrutura de equipe. Conway's Law: os sistemas que equipes produzem refletem a estrutura de comunicação da equipe. Microservices fazem mais sentido quando times precisam de autonomia de deploy. Monolito modular faz mais sentido quando um time pequeno precisa de velocidade.
Se você não consegue articular qual problema o estilo arquitetural escolhido resolve para o sistema em questão, você está cargo-culting. Estilos são ferramentas de vocabulário e de estruturação — eles existem para resolver problemas específicos, não para demonstrar sofisticação técnica.
Exemplos comparativos: Layered vs Hexagonal no mesmo serviço
// Layered — Domain chama Infrastructure diretamente (acoplamento)
public class PaymentService
{
private readonly PaymentRepository _repo; // classe concreta de Infra
private readonly EmailSender _email;
public async Task ProcessAsync(Payment payment)
{
payment.Validate();
await _repo.SaveAsync(payment); // acoplamento direto à infra
await _email.SendConfirmationAsync(payment.CustomerId);
}
}
// Hexagonal — Domain fala com ports (interfaces), nunca com infra
public interface IPaymentRepository { Task SaveAsync(Payment p); }
public interface INotifier { Task NotifyAsync(CustomerId id, string msg); }
public class PaymentService
{
private readonly IPaymentRepository _repo; // port
private readonly INotifier _notifier; // port
public async Task ProcessAsync(Payment payment)
{
payment.Validate(); // Domain puro
await _repo.SaveAsync(payment);
await _notifier.NotifyAsync(payment.CustomerId, "confirmed");
}
}
// Em teste: injetar FakePaymentRepository e FakeNotifier — sem banco
Em Layered, o Domain importa tipos concretos de Infrastructure — testar a lógica de negócio exige banco real. Em Hexagonal, o Domain só conhece interfaces que ele mesmo definiu; qualquer adapter que satisfaça o contrato serve — incluindo fakes em memória para testes.
# Layered — domínio acoplado à infraestrutura
class PaymentService:
def __init__(self, db_session, smtp_client):
self._db = db_session # infra concreta
self._smtp = smtp_client
async def process(self, payment: Payment) -> None:
payment.validate()
await self._db.save(payment) # acoplamento direto
# Hexagonal — ports como ABCs (contratos do domínio)
from abc import ABC, abstractmethod
class PaymentRepository(ABC):
@abstractmethod
async def save(self, payment: Payment) -> None: ...
class Notifier(ABC):
@abstractmethod
async def notify(self, customer_id: str, msg: str) -> None: ...
class PaymentService:
def __init__(self, repo: PaymentRepository, notifier: Notifier):
self._repo = repo
self._notifier = notifier
async def process(self, payment: Payment) -> None:
payment.validate() # domínio puro
await self._repo.save(payment)
await self._notifier.notify(payment.customer_id, "confirmed")
# Em teste: FakePaymentRepository(PaymentRepository) com lista em memória
Python não tem interfaces nativas — ABCs (Abstract Base Classes) ou typing.Protocol
cumprem o papel de ports. Protocol é mais pythônico (duck typing estrutural, sem herança
obrigatória). ABC é mais explícito sobre o contrato esperado.
// Layered — Service depende de structs concretas de infra
type PaymentService struct {
repo *PostgresPaymentRepo // concreto
email *SMTPEmailSender
}
// Hexagonal — Service depende de interfaces (ports implícitas em Go)
type PaymentRepository interface {
Save(ctx context.Context, p Payment) error
}
type Notifier interface {
Notify(ctx context.Context, customerID string, msg string) error
}
type PaymentService struct {
repo PaymentRepository
notifier Notifier
}
func (s *PaymentService) Process(ctx context.Context, p Payment) error {
if err := p.Validate(); err != nil { return err }
if err := s.repo.Save(ctx, p); err != nil { return err }
return s.notifier.Notify(ctx, p.CustomerID, "confirmed")
}
// Em teste: FakePaymentRepository com map[string]Payment em memória
Go favorece Hexagonal naturalmente: interfaces são satisfeitas implicitamente (duck typing), então qualquer struct com os métodos certos é um adapter válido — sem declaração explícita. Isso torna a criação de fakes para teste especialmente simples.
A armadilha da arquitetura prematura
A tentação em todo projeto novo é decidir a arquitetura antes de entender o domínio. O resultado é comum: uma estrutura grandiosa que acomoda casos de uso que nunca existiram, com abstrações que ninguém consegue explicar sem o contexto original da decisão. A sabedoria consolidada aponta para uma abordagem diferente: comece com o estilo mais simples que serve ao problema atual. Refatore quando sentir o custo do acoplamento.
Referências para aprofundar
- livro Patterns of Enterprise Application Architecture — Martin Fowler. Addison-Wesley, 2002.
- livro Software Architecture in Practice, 4ª ed. — Bass, Clements, Kazman. Addison-Wesley, 2021.
- livro Fundamentals of Software Architecture — Richards, Ford. O'Reilly, 2020.
- livro Clean Architecture — Robert C. Martin. Prentice Hall, 2017.
- artigo Hexagonal Architecture — Alistair Cockburn. alistair.cockburn.us, 2005.
- livro Building Microservices, 2ª ed. — Sam Newman. O'Reilly, 2021.
- artigo Microservices — Martin Fowler. martinfowler.com, 2014.
- paper How do committees invent? — Melvin Conway. Datamation, 1968.
- livro Domain-Driven Design — Eric Evans. Addison-Wesley, 2003.
- livro Working Effectively with Legacy Code — Michael Feathers. Prentice Hall, 2004.
- livro Designing Data-Intensive Applications — Martin Kleppmann. O'Reilly, 2017.
- livro Enterprise Integration Patterns — Hohpe, Woolf. Addison-Wesley, 2003.