Aula 03 · 50–65 minutos

Compor na borda

Terceira aula: componha dependências na borda em Go.

Resultado e mapa da aula

Você aprenderá a reconhecer o composition root, explicar por que ele executa apenas na partida e montar a mesma regra com infraestrutura real ou um fake. O objetivo não é esconder criação de objetos; é deixar a escolha concreta explícita e localizada.

  1. Distinga a fase de inicialização da requisição em operação.
  2. Monte o grafo de dependências em main.
  3. Analise o ciclo de vida de conexões e o efeito sobre testes.
  4. Pratique e verifique a composição.

O fluxo tem um lugar para escolher detalhes

Quando o processo inicia, main cria configurações e adaptadores. Ele conhece PostgreSQL porque está na borda: seu papel é montar a aplicação, não decidir regras de pedido.

Main compõe o repositório e o serviço
main → postgres.New → checkout.NewService — borda → detalhe → regra

Se checkout criar o próprio repositório, cada caso de uso passa a conhecer a infraestrutura. A troca de banco, de configuração ou de ambiente então se espalha pelo código de negócio.

Quando o código executa

  1. Inicialização: main abre a conexão e constrói PostgresOrderRepository.
  2. Composição: ele passa esse valor como OrderSaver para checkout.NewService.
  3. Operação: o serviço recebe pedidos e chama o contrato.
  4. Encerramento: a borda fecha recursos que ela própria abriu.

Essa separação permite montar a mesma regra com PostgreSQL em produção e com um fake em testes.

Uma composição explícita

O construtor continua simples; a escolha concreta fica em main.

// package main
func main() {
    db := openPostgres()
    orders := postgres.NewOrderRepository(db)
    service := checkout.NewService(orders)
    runHTTPServer(service)
}
Use os controles do diagrama para comparar as composições e seguir as relações.

No diagrama, compare "Composição em produção" e "Composição no teste". A seta relevante parte da regra para o contrato, não para PostgreSQL.

Recupere da memória

Em qual pacote a implementação concreta deve ser escolhida?

Fonte primária recomendada

Martin Fowler — Inversion of Control Containers and the Dependency Injection pattern. Leia a distinção entre configuração e uso de dependências.

Próxima: testar a regra com um fake.

A inicialização é diferente da operação

Problema em trabalho real: Quando checkout cria a conexão ou o repositório, a política escolhe infraestrutura, configuração e ciclo de vida. A mesma regra então não consegue ser montada com um fake sem caminhos especiais.

Fluxo e momento de execução: Na partida, main lê configuração, abre o banco, constrói o adaptador e injeta-o em checkout. Depois que o servidor começa, o adaptador de entrada chama Service; main não participa da requisição. No desligamento, main fecha os recursos que abriu.

Abordagem insuficiente: Não esconda postgres.New dentro de checkout.NewService. A dependência deixa de ser explícita, a regra ganha import de infraestrutura e o teste passa a depender de um modo alternativo de construção.

Decisão recomendada: Receba OrderSaver no construtor e conecte o valor concreto uma vez em main. A composição explícita torna configuração, ownership e desligamento auditáveis. Um contêiner de DI só é justificável quando a montagem real se torna mais complexa que sua indireção.

Como verificar na prática: Crie uma função de composição que recebe uma configuração de teste e confira que o serviço pode ser construído com um fake. Em produção, verifique falhas ao abrir o banco antes de iniciar o servidor e use defer/encerramento coordenado para liberar recursos.

Limite de segurança: Credenciais e DSNs pertencem à configuração protegida da borda, nunca ao pacote checkout nem ao teste unitário. Não registre segredos; valide a configuração e aplique o menor privilégio à conta do banco.

Fonte primária: Martin Fowler — Separating Configuration from Use.

Guia de solução do exercício

Fluxo de execução: Na inicialização, main cria o detalhe, injeta-o no serviço e inicia os adaptadores de entrada. Durante a operação, main não participa.

Abordagem inadequada: Construir PostgreSQL dentro de checkout mistura montagem de infraestrutura e política de negócio.

// package checkout
func NewService() *Service { return &Service{orders: postgres.New()} }

Por que falha e como corrigir: Configuração, ciclo de vida e substituição para teste ficam presos ao caso de uso. O teste não consegue escolher um fake sem caminhos especiais.

Abordagem correta: Use main como ponto de composição explícito.

// package main
orders := postgres.NewOrderRepository(db)
service := checkout.NewService(orders)
runHTTPServer(service)

Raciocínio, trade-offs e limites: main conhece detalhes porque é a borda da aplicação. Ele abre e fecha recursos que criou. Essa abordagem é ideal para aplicações pequenas e médias: é explícita, auditável e evita um contêiner de DI desnecessário.

Abra o diagrama relacionado e compare o fluxo.

Pergunta de mercado e entrevista

1. O que é composition root?

Como responder: É o lugar de inicialização, geralmente main, que cria detalhes e os conecta à regra. Assim configuração, ciclo de vida e infraestrutura ficam fora do negócio.

2. Por que não construir o banco dentro do serviço?

Como responder: Porque a regra perde controle sobre a colaboração, e o teste não consegue injetar um fake. main conhece o detalhe; checkout não precisa conhecê-lo.

3. Você usaria contêiner de DI em Go?

Como responder: Começaria com composição explícita em main. Um contêiner pode esconder dependências e só vale a pena quando a complexidade real de montagem justificar seu custo.

Pratique mais 9 questões de recuperação

Responda sem consultar a aula. O feedback é imediato; volte ao trecho relevante antes de tentar de novo.

DI entrega uma dependência; DIP define:

Em Go, uma interface é satisfeita quando:

Quem deve possuir uma interface pequena?

Onde a implementação concreta é escolhida?

Para testar uma regra sem banco, injete:

Uma interface deve conter:

O detalhe PostgreSQL deve depender de:

Qual sinal pede uma interface?

Após a refatoração, a regra não deve importar: