El patrón pipeline nos permite realizar operaciones de manera desacoplada a través de diferentes etapas. Cada etapa realiza recibe un payload, realiza la operación y devuelve una respuesta. Esta respuesta se pasa como parámetro a la siguiente etapa.
Por ejemplo, imagina que se quiere crear un endpoint encargado de registrar un usuario. Al registrar un usuario, debemos enviar un correo de bienvenida y devolver el usuario como respuesta. Lo típico que se suele hacer es lo siguiente:
final class UserService { private UserRepositoryInterface $userRepository; private MailerInterface $mailer; public function __construct(UserRepositoryInterface $userRepository, MailerInterface $mailer) { $this->userRepository = $userRepository; $this->mailer = $mailer; } public function createUser(array $request) { $user = new User($request); $this->userRepository>save($user); $this->mailer->send($user->email(), $user->name()); } } final class UserPutController { private UserService $userService; public function __construct(UserService $userService) { $this->userService = $userService; } public function __invoke(array $request) { return $this->userService->createUser($request); } }
Con este enfoque se está violando el SRP y el OCP de solid. Con lo cual, no solo dificulta la creación y el mantenimiento de test, sino también añadir o quitar operaciones. Una forma de resolver esto es aplicando el patrón pipeline de la siguiente manera.
interface PipelineInterface { public function pipe(callable $stage): PipelineInterface; public function process($payload); public function __invoke($payload); } interface StageInterface { public function __invoke($payload); } final class Pipeline implements PipelineInterface { private $stages = []; public function __construct(callable ...$stages) { $this->stages = $stages; } public function pipe(callable $stage): PipelineInterface { $pipeline = clone $this; $pipeline->stages[] = $stage; return $pipeline; } public function process($payload) { foreach ($this->stages as $stage) { $payload = $stage($payload); } return $payload; } public function __invoke($payload) { return $this->process($payload); } } final class CreateUserStage implements StageInterface { private UserRepositoryInterface $userRepository; public function __construct(UserRepositoryInterface $userRepository) { $this->userRepository = $userRepository; } public function __invoke($request) { $user = new User($request['name'], $request['email']); $this->userRepository->save($user); return $user; } } final class SendWelcomeEmailStage implements StageInterface { private MailerInterface $mailer; public function __construct(MailerInterface $mailer) { $this->mailer = $mailer; } public function __invoke($user) { $this->mailer->send($user->email(), $user->name()); return $user; } } final class UserPutController { public function __invoke(array $request) { $pipeline = (new Pipeline) ->pipe(new CreateUserStage) ->pipe(new SendWelcomeEmailStage); try { return $pipeline->process($request); } catch (\Exception $e) { } } }
Ahora tenemos un código menos acoplado, mas fácil de testear, añadir o quitar operaciones. Es decir, un código que está alineado con los principios solid.
- admin publicó hace 2 años
- Debes iniciar sesión para publicar comentarios
Por favor, primero debes inicia sesión para enviar.