Puertos y adaptadores explicados
Aísle el núcleo mediante puertos y adaptadores intercambiables.
Puertos y adaptadores explicados es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 2 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de PHP Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de PHP Academy incluye 4 lecciones en total.
La idea del hexágono
Puertos y adaptadores —la Arquitectura hexagonal de Alistair Cockburn— representa su aplicación como un hexágono. Dentro se encuentra la lógica de negocio pura. Toda interacción con el mundo exterior (HTTP, DB, cola, reloj o correo electrónico) atraviesa un puerto, y cada puerto es satisfecho por uno o más adaptadores. La forma no tiene una parte superior ni inferior privilegiada: la interfaz de usuario y la base de datos son simétricas, ambas son simplemente adaptadores.
Los puertos son interfaces
Un puerto es una interfaz propiedad del núcleo de la aplicación que expresa una necesidad o capacidad en términos del dominio. No debe filtrar vocabulario de infraestructura: ni PDOStatement, ni GuzzleResponse, ni 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;
}Puertos de entrada y de salida
Hay dos variantes:
- Puertos conductores (primarios, de entrada): la API que el exterior llama para poner en marcha la aplicación. Normalmente son las interfaces de sus casos de uso.
- Puertos conducidos (secundarios, de salida): interfaces que la aplicación llama para acceder al mundo exterior: repositorios, servicios de correo y relojes.
Los adaptadores de entrada llaman al núcleo; el núcleo llama al exterior mediante adaptadores de salida.
<?php
// Driving (inbound) port — the public capability of the core
interface RegisterUser
{
public function handle(string $email, string $plainPassword): UserId;
}El núcleo implementa los puertos de entrada
El caso de uso implementa un puerto de entrada y depende de puertos de salida. Observe que acepta PasswordHasher y Clock como puertos inyectados: incluso el tiempo y el hashing se abstraen para que el núcleo siga siendo determinista y fácil de probar.
<?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();
}
}Un adaptador de salida
Un adaptador de salida implementa un puerto de salida mediante una tecnología concreta. Aquí, un adaptador PDO satisface UserRepository. Puede sustituirlo por Doctrine, Redis o un cliente de API HTTP sin tocar el 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
}
}Un adaptador de entrada
Un adaptador de entrada traduce un activador externo en una llamada a un puerto de entrada. Un controlador HTTP, un comando CLI o un consumidor de mensajes: todos son adaptadores de entrada intercambiables para el mismo 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 en memoria para pruebas
La mayor ventaja: cada puerto de salida obtiene un doble de prueba rápido. Las pruebas ejercitan el caso de uso real con adaptadores en memoria, relojes deterministas y un hasher no-op.
<?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;Los adaptadores traducen, nunca deciden
Un error común es dejar que las reglas de negocio se filtren en los adaptadores. Regla práctica: un adaptador solo traduce formatos de datos y protocolos. Si encuentra un if relacionado con precios, elegibilidad o estado dentro de un controlador o repositorio, esa lógica pertenece al núcleo.
- Mapeo de JSON ↔ DTO: adaptador
- Hidratación de SQL ↔ entidad: adaptador
- «Los clientes VIP obtienen un 10 % de descuento»: núcleo
Un puerto, muchos adaptadores
Los puertos permiten sustituir adaptadores e incluso utilizar varios en paralelo. Un NotificationPort puede tener adaptadores de correo electrónico, SMS y Slack combinados. El núcleo invoca un método; la configuración decide cuántos canales responden.
<?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;Cómo se traduce el hexágono en carpetas
Una estructura pragmática de PHP para un contexto delimitado:
Domain/— entidades, objetos de valor y servicios de dominioApplication/Port/In/— interfaces de puertos de entrada (casos de uso)Application/Port/Out/— interfaces de puertos de salida (repositorios, reloj)Application/— implementaciones de casos de usoInfrastructure/Adapter/In/— controladores, CLI y consumidoresInfrastructure/Adapter/Out/— adaptadores de PDO/Doctrine/HTTP
La raíz de composición (la configuración del contenedor DI) conecta los adaptadores In y Out con los puertos.
Probar todo el hexágono
Además de las pruebas unitarias, los puertos permiten realizar rápidamente pruebas de aceptación que ejercitan la aplicación a través de su puerto primario y hacen aserciones mediante adaptadores secundarios en memoria, cubriendo un caso de uso completo sin HTTP ni base de datos. Más adelante, el mismo conjunto de pruebas se ejecuta con adaptadores reales como pruebas de integración, lo que proporciona una estrategia de pruebas por capas sin tener que reescribirlas.
<?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;Comprobación rápida
¿Qué afirmación sobre los puertos y adaptadores es correcta?
Resumen
Ports & Adapters aísla el núcleo tras interfaces:
- Los puertos son interfaces expresadas en el lenguaje del dominio y pertenecen al núcleo.
- Los puertos de entrada son invocados por los adaptadores de entrada; los puertos de salida son invocados por el núcleo y satisfechos por los adaptadores de salida.
- Los adaptadores solo traducen protocolos y formatos; nunca toman decisiones de negocio.
- El mismo puerto admite muchos adaptadores (fakes para pruebas y composiciones para distribuir una operación a varios destinos).
- La raíz de composición conecta todo; el hexágono permanece libre de frameworks.
Preguntas frecuentes
¿La lección «Puertos y adaptadores explicados» es gratis?
Sí — el texto completo de «Puertos y adaptadores explicados» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de PHP Academy, actualiza a CoddyKit PRO. El curso de PHP Academy incluye 4 lecciones en total.
¿Qué aprenderé en «Puertos y adaptadores explicados»?
Aísle el núcleo mediante puertos y adaptadores intercambiables. Practicas PHP Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar PHP Academy?
No se requiere experiencia previa. PHP Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 2 de 4.
¿Cuánto tiempo toma la lección «Puertos y adaptadores explicados»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de PHP Academy?
Sí. Cada lección de PHP Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- De la arquitectura por capas a la arquitectura limpia
- Puertos y adaptadores explicados
- Casos de uso y servicios de aplicación
- Inversión de dependencias en la práctica