Inicio » Comunidad » El patrón pipeline

El patrón pipeline

0
0

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.

0 resultados
Tu respuesta

Por favor, primero debes para enviar.

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Más información
Privacidad