Portas e adaptadores explicados
Isole o núcleo com portas e adaptadores intercambiáveis.
Portas e adaptadores explicados é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 2 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.
A ideia do hexágono
Portas e adaptadores — a Arquitetura hexagonal de Alistair Cockburn — representa seu aplicativo como um hexágono. Dentro dele está a lógica de negócio pura. Toda interação com o mundo externo (HTTP, banco de dados, fila, relógio, e-mail) atravessa uma porta, e cada porta é atendida por um ou mais adaptadores. A forma não tem um topo ou uma base privilegiados: a interface de usuário e o banco de dados são simétricos, ambos apenas adaptadores.
Portas são interfaces
Uma porta é uma interface pertencente ao núcleo da aplicação que expressa uma necessidade ou capacidade em termos do domínio. Ela não deve deixar escapar vocabulário da infraestrutura — nenhum PDOStatement, nenhuma GuzzleResponse, nenhum Eloquent.
<?php
// Driven (outbound) port: the core needs to persist users
interface UserRepository
{
public function byId(UserId $id): ?User;
public function save(User $user): void;
}Portas de entrada e de saída
Há duas categorias:
- Portas de entrada (primárias) — a API que o mundo externo chama para conduzir o aplicativo. Geralmente são suas interfaces de casos de uso.
- Portas de saída (secundárias) — interfaces que o aplicativo chama para alcançar o mundo externo: repositórios, componentes de envio de e-mail, relógios.
Adaptadores de entrada chamam o núcleo; o núcleo se comunica com o exterior por meio de adaptadores de saída.
<?php
// Driving (inbound) port — the public capability of the core
interface RegisterUser
{
public function handle(string $email, string $plainPassword): UserId;
}O núcleo implementa as portas de entrada
O caso de uso implementa uma porta de entrada e depende de portas de saída. Observe: ele aceita PasswordHasher e Clock como portas injetadas — até o tempo e as funções de hash são abstraídos, para que o núcleo permaneça determinístico e testável.
<?php
final class RegisterUserService implements RegisterUser
{
public function __construct(
private UserRepository $users,
private PasswordHasher $hasher,
private Clock $clock,
) {}
public function handle(string $email, string $plain): UserId {
if ($this->users->byEmail($email)) {
throw new EmailAlreadyTaken($email);
}
$user = User::register(
$email,
$this->hasher->hash($plain),
$this->clock->now()
);
$this->users->save($user);
return $user->id();
}
}Um adaptador de saída
Um adaptador de saída implementa uma porta de saída usando uma tecnologia concreta. Aqui, um adaptador PDO atende a UserRepository. Troque-o por Doctrine, Redis ou um cliente de API HTTP sem tocar no núcleo.
<?php
final class PdoUserRepository implements UserRepository
{
public function __construct(private PDO $pdo) {}
public function byId(UserId $id): ?User {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = ?');
$stmt->execute([(string) $id]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? User::fromRow($row) : null;
}
public function save(User $user): void {
// INSERT ... ON CONFLICT UPDATE
}
}Um adaptador de entrada
Um adaptador de entrada traduz um gatilho externo em uma chamada a uma porta de entrada. Um controlador HTTP, um comando CLI ou um consumidor de mensagens — todos são adaptadores de entrada intercambiáveis para o mesmo caso de uso.
<?php
// CLI driving adapter
final class RegisterUserCommand
{
public function __construct(private RegisterUser $register) {}
public function run(array $argv): int {
[$email, $password] = array_slice($argv, 1);
$id = $this->register->handle($email, $password);
fwrite(STDOUT, "Created user $id\n");
return 0;
}
}Adaptadores em memória para testes
A maior vantagem: cada porta de saída recebe um dublê rápido. Os testes exercitam o caso de uso real com adaptadores em memória, relógios determinísticos e um gerador de hash sem operação.
<?php
final class FixedClock implements Clock {
public function __construct(private DateTimeImmutable $t) {}
public function now(): DateTimeImmutable { return $this->t; }
}
final class PlainHasher implements PasswordHasher {
public function hash(string $p): string { return 'h:' . $p; }
}
$service = new RegisterUserService(
new InMemoryUsers(),
new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01'))
);
echo 'wired OK', PHP_EOL;Adaptadores traduzem, nunca decidem
Um erro comum é deixar regras de negócio vazarem para os adaptadores. A regra geral é: um adaptador apenas traduz formatos de dados e protocolos. Se encontrar um if sobre preços, elegibilidade ou status dentro de um controlador ou repositório, isso pertence ao núcleo.
- Mapeamento JSON ↔ DTO: adaptador
- SQL ↔ hidratação da entidade: adaptador
- "VIP recebe 10% de desconto": núcleo
Uma porta, muitos adaptadores
As portas permitem substituição e até adaptadores paralelos. Uma NotificationPort pode ter adaptadores de e-mail, SMS e Slack combinados. O núcleo aciona um único método; a configuração decide quantos canais respondem.
<?php
interface Notifier { public function send(string $to, string $msg): void; }
final class CompositeNotifier implements Notifier {
/** @param Notifier[] $channels */
public function __construct(private array $channels) {}
public function send(string $to, string $msg): void {
foreach ($this->channels as $c) $c->send($to, $msg);
}
}
$notifier = new CompositeNotifier([new EmailNotifier(), new SmsNotifier()]);
echo 'composed', PHP_EOL;Como o hexágono se mapeia para pastas
Uma organização pragmática em PHP para um contexto delimitado:
Domain/— entidades, objetos de valor, serviços de domínioApplication/Port/In/— interfaces de portas de entrada (casos de uso)Application/Port/Out/— interfaces de portas de saída (repositórios, relógio)Application/— implementações de casos de usoInfrastructure/Adapter/In/— controladores, CLI, consumidoresInfrastructure/Adapter/Out/— adaptadores PDO/Doctrine/HTTP
A raiz de composição (configuração do contêiner de DI) conecta os adaptadores de In e Out às portas.
Testando o hexágono inteiro
Além dos testes unitários, as portas permitem rápidos testes de aceitação que conduzem a aplicação por sua porta primária e fazem asserções usando adaptadores secundários em memória — cobrindo um caso de uso completo sem HTTP nem banco de dados. Mais tarde, a mesma suíte de testes é executada com adaptadores reais como testes de integração, proporcionando uma estratégia de testes em camadas sem reescrita.
<?php
// Acceptance test: real use case, fake driven adapters, no I/O
$users = new InMemoryUsers();
$service = new RegisterUserService($users, new PlainHasher(),
new FixedClock(new DateTimeImmutable('2026-01-01')));
$id = $service->handle('dev@coddykit.com', 'pw');
assert($users->byId($id) !== null);
echo 'acceptance: user persisted via in-memory adapter', PHP_EOL;Verificação rápida
Qual afirmação sobre portas e adaptadores está correta?
Recapitulação
Portas e Adaptadores isolam o núcleo por trás de interfaces:
- As portas são interfaces na linguagem do domínio, pertencentes ao núcleo.
- As portas de entrada são invocadas por adaptadores de entrada; as portas de saída são invocadas pelo núcleo e atendidas por adaptadores de saída.
- Os adaptadores apenas traduzem protocolos e formatos — nunca tomam decisões de negócio.
- A mesma porta aceita muitos adaptadores (dublês para testes, composições para distribuição).
- A raiz de composição conecta tudo; o hexágono permanece independente de frameworks.
Perguntas Frequentes
A aula “Portas e adaptadores explicados” é grátis?
Sim — o texto completo de “Portas e adaptadores explicados” é 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 “Portas e adaptadores explicados”?
Isole o núcleo com portas e adaptadores intercambiáveis. 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 2 de 4.
Quanto tempo leva a aula “Portas e adaptadores explicados”?
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