• Pick the Message up later and process it $queue->enqueue('[email protected]'); // pretend there’s a ton of stuff here while (!$queue->isEmpty()) { $email = $queue->dequeue(); $mailer->send(new Email( $email, 'Hello', 'World' )); }
all storage abstractions, avoid indicating the underlying storage backend interface Producer { public function send($message); } class InMemoryProducer implements Producer { private $queue; public function __construct(SplQueue $queue) { $this->queue = $queue; } public function send($message) { $this->queue->enqueue($message); } }
that... ◦ Run continuously ◦ Consume a single message • Maybe a method to stop as well • Again, avoid queue terminology • Clicky for Implementation interface Consumer { /** * Consume a single message */ public function once(); /** * Run continuously */ public function run(); /** * Tell the consumer to stop itself */ public function stop(); }
• Command Query Responsibility Segregation (CQRS) • Commands change something about the state of the system (or world) • Queries return information about the state of the system (or world) • Any command is a good candidate to queue
it behind an interface interface MessageHandler { public function handle($message); } final class SwitchMessageHandler implements MessageHandler { public function handle($msg) { switch (get_class($msg)) { case ResetPassword::class: $email = new Email( $msg->getEmailAddy(), 'Password'); break; case SayHello::class: $email = new Email( $msg->getEmailAddy(), 'Hello'); break; default: throw new InvalidArgumentException(); } $this->mailer->send($email); } }
$this->mailer->send(new Email( $message->getEmailAddy(), 'Hello')); } } final class ResetPasswordHandler implements MessageHandler { public function handle($message) { $this->mailer->send(new Email( $message->getEmailAddy(), 'Password')); } } Delegate to Child Message Handlers final class DelegatingHandler implements MessageHandler { private $handlers; public function __construct(array $handlers) { $this->handlers = $handlers; } public function handle($message) { $cls = get_class($message); if (!isset($this->handlers[$cls])) { throw new InvalidArgumentException(); } $this->handlers[$cls]->handle($message); } }
Email( $message->getEmailAddy(), 'Hello')); } } final class ResetPasswordHandler { public function __invoke(ResetPassword $message) { $this->mailer->send(new Email( $message->getEmailAddy(), 'Password')); } } Delegate to Callables final class DelegatingHandler implements MessageHandler { private $handlers; public function __construct(array $handlers) { $this->handlers = $handlers; } public function handle($message) { $cls = get_class($message); if (!isset($this->handlers[$cls])) { throw new InvalidArgumentException(); } call_user_func($this->handlers[$cls], $message); } }
the details behind a Driver interface • Could also call this interface Queue interface Driver { public function enqueue($message); public function dequeue($message); } final class InMemoryDriver implements Driver { private $queue; public function __construct(SplQueue $queue=null) { $this->queue = $queue ?: new SplQueue(); } public function enqueue($message) { $this->queue->enqueue($message); } public function dequeue() { return $this->queue->dequeue(); } }
the consumer • Logging • Safe serialization/deserialization of messages • Acknowledgement & failure of message in drivers and consumers • How to set up and run consumers on servers