Eventos y servicios de dominio
Capture hechos de negocio mediante eventos y servicios de dominio.
Eventos y servicios de dominio es una lección gratuita de PHP Academy en CoddyKit. Esta es la lección 3 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.
Captura de hechos de negocio
No todo comportamiento encaja dentro de una sola entidad. Los Eventos de dominio capturan hechos de negocio significativos que ya han ocurrido (OrderPlaced), lo que permite que otras partes del sistema reaccionen. Los Servicios de dominio contienen la lógica de dominio que no pertenece de forma natural a una única entidad u objeto de valor. Esta lección muestra cómo ambos mantienen expresivo el modelo sin inflar los agregados.
Qué es un evento de dominio
Un Evento de dominio es un registro inmutable de algo que ocurrió en el dominio, con un nombre en pasado que utiliza el lenguaje ubicuo: OrderPlaced, PaymentReceived, CustomerRelocated. Contiene los datos que describen el hecho, además del momento en que ocurrió. Es un objeto de valor: se crea una sola vez y nunca cambia.
<?php
final class OrderPlaced {
public function __construct(
public readonly string $orderId,
public readonly int $totalCents,
public readonly DateTimeImmutable $occurredOn = new DateTimeImmutable()
) {}
}
$e = new OrderPlaced('o1', 1300);
echo $e->orderId, ' @ ', $e->occurredOn->format('H:i'), PHP_EOL;
Los agregados registran eventos
La raíz del agregado registra los eventos como parte de su comportamiento y después los expone para que la capa de aplicación pueda despacharlos después de confirmar la transacción. El agregado no los despacha; solo los registra. Así, el dominio permanece libre de preocupaciones de infraestructura.
<?php
final class OrderPlaced {
public function __construct(public readonly string $orderId) {}
}
abstract class AggregateRoot {
private array $events = [];
protected function record(object $e): void { $this->events[] = $e; }
public function releaseEvents(): array {
$out = $this->events; $this->events = []; return $out;
}
}
final class Order extends AggregateRoot {
public function __construct(public readonly string $id) {
$this->record(new OrderPlaced($id));
}
}
$o = new Order('o1');
foreach ($o->releaseEvents() as $e) { echo get_class($e), PHP_EOL; }
Despacho después de confirmar
El servicio de aplicación persiste el agregado, confirma la transacción y después despacha los eventos liberados. Despachar después de confirmar garantiza que los manejadores nunca reaccionen ante un hecho que se haya revertido. El orden siguiente es la plantilla segura para efectos secundarios como correos electrónicos o llamadas externas.
<?php
final class PlaceOrderService {
public function __construct(
private object $repo,
private object $dispatcher
) {}
public function handle(object $order): void {
// 1. persist inside a transaction
$this->repo->save($order); // commit happens here
// 2. only AFTER commit, release and dispatch
foreach ($order->releaseEvents() as $event) {
$this->dispatcher->dispatch($event);
}
}
}
echo "order persisted, then events dispatched", PHP_EOL;
Los manejadores reaccionan
Cada evento puede tener muchos manejadores, desacoplados del agregado que lo generó. Un dispatcher PSR-14 enruta el evento a los listeners registrados. Añadir una nueva reacción (analítica o notificación) significa añadir un listener, nunca modificar el agregado.
<?php
final class OrderPlaced { public function __construct(public readonly string $orderId) {} }
final class Dispatcher {
private array $listeners = [];
public function on(string $event, callable $l): void { $this->listeners[$event][] = $l; }
public function dispatch(object $event): void {
foreach ($this->listeners[$event::class] ?? [] as $l) { $l($event); }
}
}
$d = new Dispatcher();
$d->on(OrderPlaced::class, fn($e) => print("email for {$e->orderId}\n"));
$d->on(OrderPlaced::class, fn($e) => print("analytics for {$e->orderId}\n"));
$d->dispatch(new OrderPlaced('o1'));
Eventos entre fronteras
Los eventos de dominio desacoplan los agregados que deben mantenerse consistentes sin compartir una transacción. OrderPlaced en el contexto de ventas puede activar la reserva de inventario en otro contexto mediante un evento. Internamente son objetos; entre servicios suelen convertirse en eventos de integración en un intermediario de mensajes. Mantenga diferenciados los eventos de dominio internos y los eventos de integración externos: el contrato público no debería ser su modelo interno.
Qué es un servicio de dominio
Un Servicio de dominio contiene lógica de dominio que:
- implica varios agregados o entidades, por lo que no encaja en ninguno de ellos, y
- es un concepto genuino del dominio, expresado en el lenguaje ubicuo.
No tiene estado y opera sobre objetos de dominio. Un servicio FundsTransfer que carga una cuenta y abona otra es el ejemplo clásico.
Un servicio de dominio en código
El servicio coordina dos agregados, pero no conserva ningún estado propio. La lógica no pertenece a ninguna de las dos cuentas, por lo que reside en un servicio cuyo nombre describe la operación de dominio.
<?php
final class Account {
public function __construct(public readonly string $id, private int $cents) {}
public function withdraw(int $c): void {
if ($c > $this->cents) { throw new DomainException('insufficient funds'); }
$this->cents -= $c;
}
public function deposit(int $c): void { $this->cents += $c; }
public function balance(): int { return $this->cents; }
}
final class MoneyTransferService {
public function transfer(Account $from, Account $to, int $cents): void {
$from->withdraw($cents);
$to->deposit($cents);
}
}
$a = new Account('a', 1000); $b = new Account('b', 0);
(new MoneyTransferService())->transfer($a, $b, 400);
echo $a->balance(), '/', $b->balance(), PHP_EOL; // 600/400
Servicio de dominio frente a servicio de aplicación
No confunda ambos conceptos:
- Un Servicio de dominio contiene lógica de negocio y reside en la capa de dominio; no sabe nada de transacciones ni de HTTP.
- Un Servicio de aplicación coordina un caso de uso: carga agregados desde los repositorios, llama a la lógica de dominio, confirma la transacción y despacha eventos. Es un coordinador ligero sin reglas de negocio propias.
Evite la trampa de los servicios
Es fácil abusar de los servicios de dominio. Si la lógica puede residir en una entidad o un objeto de valor, debería hacerlo: así se evita un modelo anémico. Recurra a un servicio de dominio solo cuando la operación abarque realmente varios agregados o no tenga un lugar natural. De lo contrario, terminará extrayendo el comportamiento de sus entidades y recreando código procedural.
Coordinación del servicio de aplicación
Para unirlo todo: un servicio de aplicación delgado carga agregados desde repositorios, invoca un servicio de dominio para la lógica entre agregados, persiste el resultado y despacha eventos. Solo contiene orquestación: no tiene reglas de negocio propias.
<?php
final class Account {
public function __construct(public readonly string $id, public int $cents) {}
}
final class MoneyTransferService { // domain service: the rule
public function transfer(Account $from, Account $to, int $c): void {
if ($c > $from->cents) { throw new DomainException('insufficient'); }
$from->cents -= $c; $to->cents += $c;
}
}
final class TransferApp { // application service: orchestration only
public function __construct(private MoneyTransferService $domain) {}
public function run(Account $a, Account $b, int $c): void {
$this->domain->transfer($a, $b, $c); // then repo->save + dispatch
}
}
$a = new Account('a', 1000); $b = new Account('b', 0);
(new TransferApp(new MoneyTransferService()))->run($a, $b, 250);
echo $a->cents, '/', $b->cents, PHP_EOL; // 750/250
Comprobación rápida
Eventos y servicios.
Repaso
Ha capturado hechos de negocio y lógica sin un lugar claro. Los eventos de dominio son registros inmutables en pasado que los agregados registran y la capa de aplicación despacha después del commit, desacoplando las reacciones y atravesando los límites de los agregados. Los servicios de dominio contienen lógica de dominio sin estado que abarca varios agregados, y se distinguen de los servicios de aplicación delgados, que solo orquestan casos de uso. Utilice ambos con moderación para que el comportamiento permanezca en las entidades y los objetos de valor siempre que corresponda.
Preguntas frecuentes
¿La lección «Eventos y servicios de dominio» es gratis?
Sí — el texto completo de «Eventos y servicios de dominio» 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 «Eventos y servicios de dominio»?
Capture hechos de negocio mediante eventos y servicios de dominio. 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 3 de 4.
¿Cuánto tiempo toma la lección «Eventos y servicios de dominio»?
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
- Bloques de construcción de DDD: entidades y objetos de valor
- Agregados, repositorios y fábricas
- Eventos y servicios de dominio
- Contextos delimitados y mapeo de contextos