0Pricing
PHP Academy · Aula

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ínio
  • Application/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 uso
  • Infrastructure/Adapter/In/ — controladores, CLI, consumidores
  • Infrastructure/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

  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