The Pipeline Pattern — for fun and profit

  1. User places the order
  2. Payment processor takes the payment
  3. Invoice is generated and sent to the user
  4. The order is sent to your ERP system
  5. The order is packed and shipped
  6. Customer receives a thank-you email.
if ($order->getStatus() === 'success') {
$this->getErpAdapter()->sendOrder($order);
}

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

$pipeline = (new Pipeline)
->pipe(new createOrder)
->pipe(new processPayment)
->pipe(new sendInvoice)
->pipe(new exportOrder);
$pipeline->process($order);

Definition of the Pipeline Pattern

Implementation in PHP

Composer install

Assemble your class

<?php
namespace Example\Pipeline;
/**
* Class RunAllTheThings
* @package Example\Pipeline
*/
class RunAllTheThings
{
/**
* @return Payload
*/
public function doIt()
{
// Define the pipeline stages
$pipeline = (new Pipeline)
->pipe(new Segment\DoStage1))
->pipe(new Segment\DoStage2))
->pipe(new Segment\DoStage3));

// The payload is an object that's passed between stages
$payload = new Payload();
// Run the pipeline
$pipeline->process($payload);
return $payload;
}
}

Payload

<?php
namespace Example\Pipeline;
/**
* Class Payload
* @package Example\Pipeline
*/
class Payload
{
/**
* @var null|string
*/
protected $result = null;
/**
* @return null
*/
public function getResult()
{
return $this->result;
}
/**
* @param string $result
* @return static
*/
public function setResult($result)
{
$this->result = $result;
return $this;
}
/**
* @param $result
* @return $this
*/
public function addResult($result)
{
$this->result .= $result;
return $this;
}
}

Stage 1

<?php
namespace Example\Pipeline;
/**
* Class Stage1
* @package Example\Pipeline
*/
class Stage1
{
public function __invoke(Payload $payload)
{
$payload->addResult('all');
return $payload;
}
}

Stage 2

<?php
namespace Example\Pipeline;
/**
* Class Stage2
* @package Example\Pipeline
*/
class Stage2
{
public function __invoke(Payload $payload)
{
$payload->addResult('the');
return $payload;
}
}

Stage 3

<?php
namespace Example\Pipeline;
/**
* Class Stage3
* @package Example\Pipeline
*/
class Stage3
{
public function __invoke(Payload $payload)
{
$payload->addResult('things');
return $payload;
}
}

The Result

<?php
$allThethings = new \Example\Pipeline\RunAllTheThings();
$result = $allThethings->doIt();
var_dump($result->getResult());
string "allthethings"

Short-circuiting

try {
$pipeline->process($payload);
catch (LogicException $e) {
// Do something else!
}

Dynamic Pipelines

use League\Pipeline\PipelineBuilder;// Instantiate the PipelineBuilder
$builder = (new PipelineBuilder)
->add(new CreateOrder);
// Conditional stage
if ($order->getOrigin() === 'New Zealand') {
$builder->add(new PreBookCarrier);
}
// Continue adding more stages
$builder->add(new processPayment)
->add(new sendInvoice)
->add(new exportOrder);
// Assemble to pipeline
$pipeline = $builder->build();
// Process
$pipeline->process($order);

Reusing Pipes

$createOrder = (new Pipeline)
->pipe(new CreateOrder)
->pipe(new GenerateInvoice);
$pipeline = (new Pipeline)
->pipe($createOrder)
->pipe(new ProcessPayment)
->pipe(new SendInvoice)
->pipe(new ExportOrder);
$pipeline->process($order);

Conclusion

--

--

Passionate Product Owner, security fanatic, Senior Software Engineer, boat restorer, blogger, Melbournite and keen motorcyclist!

Love podcasts or audiobooks? Learn on the go with our new app.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Aaron Weatherall

Aaron Weatherall

Passionate Product Owner, security fanatic, Senior Software Engineer, boat restorer, blogger, Melbournite and keen motorcyclist!