0Pricing
PHP Academy · Aula

Inversão de dependências na prática

Conecte adaptadores ao núcleo usando inversão de dependências.

Inversão de dependências na prática é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.

Inversão versus injeção

As pessoas confundem duas ideias distintas. Injeção de dependência é uma técnica: passe os colaboradores em vez de construí-los. Inversão de dependência (o D de SOLID) é um princípio: tanto a política de alto nível quanto o detalhe de baixo nível dependem de uma abstração, e a abstração pertence ao módulo de alto nível. Esta lição trata de tornar a segunda ideia real — conectando adaptadores concretos a um núcleo que define as interfaces.

O princípio, com precisão

O DIP afirma:

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

A parte sutil é que a interface pertence ao consumidor, não ao implementador. Seu domínio declara PaymentGateway; o adaptador do Stripe se conforma a ela — e não o contrário.

Defina a abstração no núcleo

Coloque a interface próxima ao código que precisa dela, expressa nos termos do domínio. Nenhum tipo do Stripe deve vazar para dentro.

<?php
// src/Billing/Application/Port/PaymentGateway.php
interface PaymentGateway
{
    public function charge(Money $amount, CardToken $token): ChargeId;
}

final class Money {
    public function __construct(public readonly int $cents, public readonly string $currency) {}
}
final class CardToken { public function __construct(public readonly string $value) {} }
final class ChargeId { public function __construct(public readonly string $value) {} }

Faça um adaptador se conformar a ela

O adaptador de infraestrutura implementa a interface do núcleo e faz a tradução para o SDK do fornecedor. A seta de dependência aponta do adaptador (detalhe) para a abstração (política) — inversão alcançada.

<?php
final class StripePaymentGateway implements PaymentGateway
{
    public function __construct(private \Stripe\StripeClient $stripe) {}

    public function charge(Money $amount, CardToken $token): ChargeId {
        $intent = $this->stripe->paymentIntents->create([
            'amount'   => $amount->cents,
            'currency' => strtolower($amount->currency),
            'payment_method' => $token->value,
            'confirm'  => true,
        ]);
        return new ChargeId($intent->id);
    }
}

Injeção pelo construtor é o padrão

Injete pelo construtor e declare o tipo da abstração. As dependências tornam-se explícitas, imutáveis e impossíveis de esquecer. Evite a injeção por métodos de configuração e por propriedades para colaboradores obrigatórios — elas permitem objetos parcialmente construídos.

<?php
final class CheckoutService
{
    public function __construct(
        private PaymentGateway $payments,   // abstraction, not StripeClient
        private OrderRepository $orders,
    ) {}

    public function pay(OrderId $id, CardToken $token): ChargeId {
        $order  = $this->orders->get($id);
        $charge = $this->payments->charge($order->total(), $token);
        $order->markPaid($charge);
        $this->orders->save($order);
        return $charge;
    }
}

A raiz de composição

Toda a configuração concreta acontece em um único lugar — a raiz de composição — o mais próximo possível de main()/do ponto de entrada. Nenhum outro lugar instancia a infraestrutura. Este é o único ponto que sabe que o Stripe existe.

<?php
// public/index.php — composition root
$stripe   = new \Stripe\StripeClient(getenv('STRIPE_SECRET'));
$gateway  = new StripePaymentGateway($stripe);
$orders   = new PdoOrderRepository(new PDO(getenv('DB_DSN')));
$checkout = new CheckoutService($gateway, $orders);

// Everything below depends only on abstractions
$controller = new CheckoutController($checkout);

Configuração com um contêiner de DI

Para aplicações não triviais, um contêiner (PHP-DI, Symfony) automatiza a configuração. O passo fundamental é vincular interfaces a implementações. A ligação automática resolve os construtores pelo tipo; basta declarar o mapa de interface→classe.

<?php
use function DI\autowire;
use function DI\get;

return [
    PaymentGateway::class => autowire(StripePaymentGateway::class),
    OrderRepository::class => autowire(PdoOrderRepository::class),
    \Stripe\StripeClient::class => fn() => new \Stripe\StripeClient(getenv('STRIPE_SECRET')),
    PDO::class => fn() => new PDO(getenv('DB_DSN')),
];

Trocar adaptadores comprova o ponto

Como o núcleo depende apenas de PaymentGateway, trocar de fornecedor ou testar sem conexão é uma alteração de uma única linha. Aqui está uma implementação falsa usada em um teste unitário — o CheckoutService não muda e nunca importa o Stripe.

<?php
final class FakeGateway implements PaymentGateway {
    public array $charges = [];
    public function charge(Money $a, CardToken $t): ChargeId {
        $this->charges[] = $a;
        return new ChargeId('ch_test_' . count($this->charges));
    }
}

$fake = new FakeGateway();
$id = $fake->charge(new Money(2500, 'EUR'), new CardToken('tok_visa'));
echo $id->value, ' charges=', count($fake->charges), PHP_EOL; // ch_test_1 charges=1

Evite a armadilha do localizador de serviços

Injetar o próprio contêiner e buscar dependências dentro dos métodos é o padrão antiprojeto do localizador de serviços. Isso oculta as dependências, impede a verificação de tipos e volta a acoplar o código ao contêiner. Injete explicitamente aquilo de que precisa.

<?php
// ANTI-PATTERN: hidden dependencies, container leaks everywhere
final class BadCheckout {
    public function __construct(private ContainerInterface $c) {}
    public function pay($id, $token) {
        $gateway = $this->c->get(PaymentGateway::class); // hidden!
        // ...
    }
}

Onde o contêiner pode aparecer

O contêiner é legítimo em exatamente uma camada: a raiz de composição e o código de integração do framework ao redor dela (por exemplo, uma fábrica de controladores). Suas classes de domínio e de aplicação devem permanecer independentes do contêiner — recebem objetos simples pelos construtores e poderiam ser instanciadas manualmente. Um bom teste é: seria possível configurar a aplicação inteira em um único arquivo PHP sem contêiner? Se sim, suas dependências são honestas.

Configuração tardia sem localizar serviços

Às vezes, uma dependência é cara de construir ou só é necessária condicionalmente. Resista à injeção do contêiner — injete uma closure de fábrica. A dependência continua explícita e tipada, enquanto sua construção é adiada até que seja realmente usada.

<?php
final class ReportService
{
    /** @param Closure():PaymentGateway $gatewayFactory */
    public function __construct(private Closure $gatewayFactory) {}

    public function refundIfNeeded(bool $needed): void {
        if (!$needed) return;
        $gateway = ($this->gatewayFactory)(); // built only when required
        // $gateway->charge(...) etc.
    }
}

// Composition root supplies the factory, not the container
$svc = new ReportService(fn() => new StripePaymentGateway($stripe ?? null));
echo 'lazy dependency wired', PHP_EOL;

Verificação rápida

Quem deve ser responsável pela interface PaymentGateway?

Recapitulação

Você conectou os adaptadores ao núcleo da maneira correta:

  • Inversão ≠ injeção: injeção é o mecanismo; inversão é a propriedade da abstração pelo consumidor.
  • O núcleo define a interface; os adaptadores de infraestrutura se conformam a ela.
  • Use injeção pelo construtor de abstrações; faça toda a configuração concreta em uma única raiz de composição (ou configuração de contêiner que vincule interface→classe).
  • Trocar adaptadores e usar implementações falsas nos testes passa a ser uma alteração de uma única linha.
  • Evite o padrão antiprojeto do localizador de serviços — mantenha o contêiner apenas na borda.

Perguntas Frequentes

A aula “Inversão de dependências na prática” é grátis?

Sim — o texto completo de “Inversão de dependências na prática” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.

O que vou aprender em “Inversão de dependências na prática”?

Conecte adaptadores ao núcleo usando inversão de dependências. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar PHP Academy?

Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Inversão de dependências na prática”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de PHP Academy?

Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Da arquitetura em camadas à arquitetura limpa
  2. Portas e adaptadores explicados
  3. Casos de uso e serviços de aplicação
  4. Inversão de dependências na prática
← Voltar para PHP Academy