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=1Evite 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
- Da arquitetura em camadas à arquitetura limpa
- Portas e adaptadores explicados
- Casos de uso e serviços de aplicação
- Inversão de dependências na prática