Conceitos-chave de arquitetura

Acoplamento, coesão, separação de responsabilidades, princípios SOLID aplicados à arquitetura e bounded contexts.

Construir uma linguagem comum

Antes de nos aprofundarmos na arquitetura distribuída, é bom estabelecer um vocabulário comum. Estes termos vão aparecer repetidamente ao longo de toda a plataforma.

Muitas decisões técnicas parecem complexas apenas porque faltam definições compartilhadas. Nesta seção vamos construir essa linguagem comum e os princípios que orientam as decisões arquiteturais.

Domínio

É a área do negócio que o software tenta resolver. Por exemplo: usuários, produtos, pedidos, pagamentos, estoque.

O domínio não é um conceito técnico, e sim de negócio. Entender o domínio é o primeiro passo para projetar uma boa arquitetura.

Subdomínio

Uma parte específica dentro do domínio geral do negócio. Ajuda a decompor o problema em áreas mais gerenciáveis.

Bounded Context

Um limite explícito dentro do qual certos termos, modelos e regras têm um significado concreto.

Por que importam?

Em um sistema grande, a mesma palavra pode significar coisas diferentes dependendo do contexto:

  • “Usuário” na autenticação: e-mail, senha, papéis.
  • “Usuário” no faturamento: nome fiscal, endereço, forma de pagamento.
  • “Usuário” no suporte: histórico de tickets, nível de satisfação.

Cada bounded context tem seu próprio modelo com os atributos de que precisa.

Mapeamento para a arquitetura

┌──────────────────┐  ┌──────────────────┐  ┌──────────────────┐
│   Autenticación  │  │   Facturación    │  │     Soporte      │
│                  │  │                  │  │                  │
│  Usuario:        │  │  Cliente:        │  │  ContactoSoporte:│
│  - email         │  │  - nombre_fiscal │  │  - historial     │
│  - password_hash │  │  - dirección     │  │  - satisfacción  │
│  - roles         │  │  - método_pago   │  │  - prioridad     │
└──────────────────┘  └──────────────────┘  └──────────────────┘

Comunicação entre contextos

  • APIs bem definidas: Contratos explícitos entre contextos.
  • Eventos de domínio: Um contexto publica eventos que outros consomem.
  • Anti-Corruption Layer (ACL): Camada de tradução que protege um contexto de mudanças em outro.

DTO (Data Transfer Object)

Objeto usado para transferir dados entre camadas ou sistemas. Não representa necessariamente o modelo interno do domínio.

Contrato

Definição explícita de como duas partes se comunicam. Pode definir:

  • Estrutura de request e response
  • Campos obrigatórios e opcionais
  • Tipos de dados
  • Erros possíveis
  • Regras de versionamento

Um contrato reduz a ambiguidade e ajuda a evoluir sistemas sem quebrar consumidores.

Evento

Fato relevante que já ocorreu no sistema. Por exemplo: OrderPlaced, UserCreated, PaymentConfirmed.

Os eventos são imutáveis e fundamentais em arquiteturas event-driven.

Integração

Conexão entre sistemas ou componentes distintos, sejam internos ou externos.

Consistência

Grau em que os dados refletem corretamente o estado do sistema.

Consistência forte

Todos veem o mesmo estado imediatamente.

Consistência eventual

Pode haver defasagens temporárias até que o sistema convirja.

Idempotência

Capacidade de executar uma operação várias vezes e obter o mesmo resultado sem efeitos duplicados indesejados.

Acoplamento

O acoplamento mede o quanto um componente depende de outro.

Tipos de acoplamento (do maior ao menor)

  • Acoplamento de conteúdo: Um módulo modifica diretamente os dados internos de outro. É o pior tipo.
  • Acoplamento comum: Dois módulos compartilham variáveis globais ou estado mutável.
  • Acoplamento de controle: Um módulo diz a outro como fazer seu trabalho (passando flags de controle).
  • Acoplamento de dados: Os módulos se comunicam apenas por meio de parâmetros bem definidos. É o tipo desejável.

Exemplo prático

// Alto acoplamiento: el servicio de pedidos conoce
// la estructura interna del servicio de usuarios
function crearPedido(usuarioId) {
  const usuario = db.query('SELECT * FROM usuarios WHERE id = ?', usuarioId);
  if (usuario.saldo_cuenta > 0) {
    // Accede directamente a la tabla de otro dominio
  }
}

// Bajo acoplamiento: comunicación a través de una interfaz
function crearPedido(usuarioId) {
  const puedeComprar = await servicioUsuarios.verificarCapacidadCompra(usuarioId);
  if (puedeComprar) {
    // No conoce los detalles internos del servicio de usuarios
  }
}

Busque baixo acoplamento entre módulos. Cada componente deve poder evoluir de forma independente.

Coesão

A coesão mede o quanto as responsabilidades dentro de um mesmo componente estão relacionadas. Alta coesão significa que tudo o que um módulo faz está estreitamente relacionado.

Tipos de coesão (do menor ao maior)

  • Coincidental: As funções estão juntas por acaso, sem relação lógica.
  • Temporal: As funções são agrupadas porque são executadas ao mesmo tempo.
  • Funcional: Todas as funções contribuem para uma única responsabilidade bem definida. É o tipo ideal.

Exemplo prático

// Baja cohesión: este módulo hace demasiadas cosas no relacionadas
const utils = {
  formatearFecha(fecha) { /* ... */ },
  calcularImpuesto(monto) { /* ... */ },
  enviarEmail(destinatario, asunto) { /* ... */ },
  validarRUT(rut) { /* ... */ },
};

// Alta cohesión: cada módulo tiene una responsabilidad clara
const formatoFechas = {
  formatear(fecha, formato) { /* ... */ },
  parsear(texto, formato) { /* ... */ },
  diferencia(fechaA, fechaB) { /* ... */ },
};

A relação acoplamento-coesão

Estes dois conceitos estão inversamente relacionados: ao aumentar a coesão dentro de um módulo, você naturalmente reduz o acoplamento entre módulos.

Separação de responsabilidades (Separation of Concerns)

Cada parte do sistema deve cuidar de uma única preocupação. Na arquitetura, isso se manifesta em camadas e módulos com responsabilidades claras.

Exemplo em camadas

┌─────────────────────────┐
│   Presentación (UI)     │  → Cómo se muestra la información
├─────────────────────────┤
│   Lógica de negocio     │  → Qué reglas aplican
├─────────────────────────┤
│   Acceso a datos        │  → Dónde y cómo se almacenan los datos
└─────────────────────────┘

Princípios SOLID na arquitetura

Os princípios SOLID, originalmente formulados para o design orientado a objetos, também se aplicam no nível arquitetural:

S — Single Responsibility (Responsabilidade Única)

Cada serviço ou módulo deve ter um único motivo para mudar. Se uma mudança nas regras de faturamento exige modificar o serviço de usuários, há um problema de design.

O — Open/Closed (Aberto/Fechado)

O sistema deve ser extensível sem modificar o que já existe. Plugins, middlewares e event-driven architectures facilitam isso.

L — Liskov Substitution (Substituição de Liskov)

Se um serviço implementa um contrato (API), qualquer implementação alternativa deve ser intercambiável sem quebrar os consumidores.

I — Interface Segregation (Segregação de Interfaces)

Os consumidores não devem depender de interfaces que não usam. O padrão BFF (Backend for Frontend) é um exemplo direto desse princípio.

D — Dependency Inversion (Inversão de Dependências)

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

// Violación: la lógica de negocio depende de PostgreSQL
class ServicioPedidos {
  constructor() {
    this.db = new PostgresClient();
  }
}

// Correcto: depende de una abstracción
class ServicioPedidos {
  constructor(repositorio) {
    this.repositorio = repositorio; // Puede ser Postgres, Mongo, en memoria...
  }
}

Resumo prático

ConceitoPergunta-chaveObjetivo
DomínioQual problema de negócio resolvemos?Entender o contexto
Bounded ContextOnde estão os limites do domínio?Modelos claros e autônomos
ContratoComo as partes se comunicam?Comunicação explícita
AcoplamentoO quanto A depende de B?Minimizar dependências
CoesãoO quão relacionado está o que A faz?Maximizar a relação interna
ConsistênciaOs dados refletem o estado real?Gerenciar expectativas
IdempotênciaPosso repetir sem efeitos duplicados?Operações seguras
SOLIDO design é extensível e sustentável?Flexibilidade a longo prazo

Estes conceitos não são regras absolutas, e sim guias para tomar melhores decisões. Quanto mais claro for este vocabulário, mais fácil será entender decisões de design mais avançadas.