Command Bus To Awesome Town

183
Command Bus To DPC 2015 Awesome Town Ross Tuck

Transcript of Command Bus To Awesome Town

Page 1: Command Bus To Awesome Town

Command Bus To

DPC 2015

Awesome Town

Ross Tuck

Page 2: Command Bus To Awesome Town
Page 3: Command Bus To Awesome Town

Command Bus To Awesome Town

Page 4: Command Bus To Awesome Town

This is a story...

Page 5: Command Bus To Awesome Town

...about a refactoring.

Page 6: Command Bus To Awesome Town

$store->purchaseGame($gameId, $customerId);

Page 7: Command Bus To Awesome Town

Domain Model

Service

Controller

View

Page 8: Command Bus To Awesome Town

$store->purchaseGame($gameId, $customerId);

Page 9: Command Bus To Awesome Town

class BUYBUYBUYController{ public function buyAction(Request $request) { $form = $this->getForm(); $form->bindTo($request); if ($form->isTotallyValid()) { $store->purchaseGame($gameId, $customerId); return $this->redirect('thanks_for_money'); } return ['so_many_errors' => $form->getAllTheErrors()]; }}

Page 10: Command Bus To Awesome Town

class Store{ public function purchaseGame($gameId, $customerId) { Assert::notNull($gameId); Assert::notNull($customerId);

$this->security->allowsPurchase($customerId); $this->logging->debug('purchasing game'); try { $this->db->beginTransaction(); $purchase = new Purchase($gameId, $customerId); $this->repository->add($purchase); $this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 11: Command Bus To Awesome Town

class Store{ public function purchaseGame($gameId, $customerId) {

$purchase = new Purchase($gameId, $customerId); $this->repository->add($purchase);

}}

Page 12: Command Bus To Awesome Town

$store->purchaseGame($gameId, $customerId);

Page 13: Command Bus To Awesome Town

->purchaseGame($gameId, $customerId);$store

Page 14: Command Bus To Awesome Town

purchaseGame($gameId, $customerId);$store->

Page 15: Command Bus To Awesome Town

purchaseGame $gameId, $customerId ;$store-> ( )

Page 16: Command Bus To Awesome Town

purchaseGame $gameId $customerId $store-> ( , );

Page 17: Command Bus To Awesome Town

purchaseGame $gameId $customerId

Data

Page 18: Command Bus To Awesome Town

purchaseGame $gameId $customerId

Intent

Information

Page 19: Command Bus To Awesome Town

purchaseGame $gameId $customerId

Page 20: Command Bus To Awesome Town

PurchaseGame $gameId $customerId

Page 21: Command Bus To Awesome Town

PurchaseGame($gameId, $customerId);

Page 22: Command Bus To Awesome Town

new PurchaseGame($gameId, $customerId);

Page 23: Command Bus To Awesome Town

new PurchaseGame($gameId, $customerId);->getGameId();->getCustomerId();

Page 24: Command Bus To Awesome Town

new PurchaseGame($gameId, $customerId);

Page 25: Command Bus To Awesome Town

Object

Page 26: Command Bus To Awesome Town

Message

Page 27: Command Bus To Awesome Town
Page 28: Command Bus To Awesome Town

People Paperwork →Machines Messages→

Page 29: Command Bus To Awesome Town

Different types of messages

Page 30: Command Bus To Awesome Town

Command

Page 31: Command Bus To Awesome Town

Reserve Seat

Page 32: Command Bus To Awesome Town

Reserve SeatSchedule Appointment

Page 33: Command Bus To Awesome Town

new PurchaseGame($gameId, $customerId);

Reserve SeatSchedule Appointment

Page 34: Command Bus To Awesome Town

Data StructureWhy object?

Page 35: Command Bus To Awesome Town

+------------+--------------+| Field | Type |+------------+--------------+| id | varchar(60) || indication | varchar(255) || arrived | tinyint(1) || firstName | varchar(255) || lastName | varchar(255) || birthDate | datetime |+------------+--------------+

Page 36: Command Bus To Awesome Town
Page 37: Command Bus To Awesome Town

struct purchase_game { int game_id; int customer_id;}

Page 38: Command Bus To Awesome Town

Map[Int, Int]Tuple[Int, Int]

Page 39: Command Bus To Awesome Town

[ 'game_id' => 42, 'customer_id' => 11]

Page 40: Command Bus To Awesome Town

class PurchaseGame{ public $gameId; public $customerId;}

Page 41: Command Bus To Awesome Town

class PurchaseGame{ private $gameId; private $customerId;

public function __construct($gameId, $customerId) { $this->gameId = $gameId; $this->customerId = $customerId; }

public function getGameId() { return $this->gameId; }

public function getCustomerId() { return $this->customerId; }}

Page 42: Command Bus To Awesome Town

class PurchaseController{ public function purchaseGameAction(Request $request) { $form = $this->createForm('purchase_game'); $form->bind($request);

if ($form->isValid()) { $command = $form->getData(); } }}

Page 43: Command Bus To Awesome Town

We have intent...

Page 44: Command Bus To Awesome Town

...but no ability to carry out.

Page 45: Command Bus To Awesome Town

Command Handlers

Page 46: Command Bus To Awesome Town

Handlers handle commands

Page 47: Command Bus To Awesome Town

1:1 Command to Handler

Page 48: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) { Assert::notNull($command->getGameId()); Assert::notNull($command->getCustomerId());

$this->security->allowsPurchase($command->getCustomerId()); $this->logging->debug('purchasing game');

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 49: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId()); $this->logging->debug('purchasing game');

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 50: Command Bus To Awesome Town

Command + Handler

Page 51: Command Bus To Awesome Town

$handler->handle(new PurchaseGame($gameId, $customerId));

$store->purchaseGame($gameId, $customerId);

“Extract To Message”

Page 52: Command Bus To Awesome Town
Page 53: Command Bus To Awesome Town

Command Message vs Command Pattern

Page 54: Command Bus To Awesome Town

$command = new PurchaseGame($gameId, $customerId);

$handler->handle($command);

Handler version

Page 55: Command Bus To Awesome Town

$command = new PurchaseGame($gameId, $customerId);

Page 56: Command Bus To Awesome Town

$command = new PurchaseGame($gameId, $customerId);

$command->execute();

Classical version

Page 57: Command Bus To Awesome Town

$command = new PurchaseGame($gameId, $customerId, $repository, $db, $logger, $security);$command->execute();

Classical version

Page 58: Command Bus To Awesome Town

● Game ID● Customer ID

Purchase THIS game Purchase ANY game● Repository● Security● Logger● DB

Page 59: Command Bus To Awesome Town

I generally advocate Handlers

Page 60: Command Bus To Awesome Town

Connecting the Dots

Page 61: Command Bus To Awesome Town

Command Handler

Page 62: Command Bus To Awesome Town

class PurchaseController{ public function purchaseGameAction() { // form stuff lol

$command = $form->getData(); $this->handler->handle($command); }}

Controller

Must be the RIGHT handler

Page 63: Command Bus To Awesome Town

Mailman

Page 64: Command Bus To Awesome Town

Enter the Bus

Page 65: Command Bus To Awesome Town

Command Bus

Page 66: Command Bus To Awesome Town

Handler

Command Bus

Command

Page 67: Command Bus To Awesome Town

Handler

Command Bus

Command

Page 68: Command Bus To Awesome Town

Handler

Command Bus

Command

♥♥♥

♥♥

Page 69: Command Bus To Awesome Town

So, what is this command bus?

Page 70: Command Bus To Awesome Town

new CommandBus( [ PurchaseGame::class => $purchaseGameHandler, RegisterUser::class => $registerUserHandler ]);

Page 71: Command Bus To Awesome Town

$command = new PurchaseGame($gameId, $customerId);$commandBus->handle($command);

Page 72: Command Bus To Awesome Town

● Over the Network● In a queue● Execute in process

Page 73: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 74: Command Bus To Awesome Town
Page 75: Command Bus To Awesome Town

Easier to Wire

Page 76: Command Bus To Awesome Town

Handler Freedom

Page 77: Command Bus To Awesome Town

It's just conventions

Page 78: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 79: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->execute($command); }}

Page 80: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->__invoke($command); }}

Page 81: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); } $methodName = 'handle'.$name; $this->handlers[$name]->{$methodName}($command); }}

Page 82: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 83: Command Bus To Awesome Town

class CommandBus{ private $container = []; public function __construct($container) { $this->container = $container; }

public function handle($command) { $name = get_class($command)."Handler"; if (!$this->container->has($name)) { throw new Exception("No handler for $name"); }

$this->container->get($name)->handle($command); }}

Page 84: Command Bus To Awesome Town

It's just conventions

Page 85: Command Bus To Awesome Town

Plugins

Page 86: Command Bus To Awesome Town

Command Bus Uniform Interface→

Page 87: Command Bus To Awesome Town

->handle($command);

Page 88: Command Bus To Awesome Town
Page 89: Command Bus To Awesome Town

Decorator Pattern

Page 90: Command Bus To Awesome Town

Command Bus 3

Command Bus 2

Command Bus

Page 91: Command Bus To Awesome Town

Command Bus 1

Command B

us 3

Command B

us 2

Page 92: Command Bus To Awesome Town

Command Bus 3

Command Bus 2

Command Bus

Page 93: Command Bus To Awesome Town

Command Bus 3

Logging Command Bus

Command Bus

Page 94: Command Bus To Awesome Town

Transaction Command Bus

Logging Command Bus

Command Bus

Page 95: Command Bus To Awesome Town

interface CommandBus{ public function handle($command);}

Page 96: Command Bus To Awesome Town

class CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 97: Command Bus To Awesome Town

class MyCommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 98: Command Bus To Awesome Town

class MyCommandBus implements CommandBus{ private $handlers = []; public function __construct($handlers) { $this->handlers = $handlers; }

public function handle($command) { $name = get_class($command); if (!isset($this->handlers[$name])) { throw new Exception("No handler for $name"); }

$this->handlers[$name]->handle($command); }}

Page 99: Command Bus To Awesome Town

What to refactor out?

Page 100: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId()); $this->logging->debug('purchasing game');

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 101: Command Bus To Awesome Town

class LoggingDecorator implements CommandBus{ private $logger; private $innerBus;

public function __construct(Logger $logger, CommandBus $innerBus) { $this->logger = $logger; $this->innerBus = $innerBus; }

public function handle($command) { $this->logger->debug('Handling command '.get_class($command)); $this->innerBus->handle($command);

}

}

Page 102: Command Bus To Awesome Town

class LoggingDecorator implements CommandBus{ private $logger; private $innerBus;

public function __construct(Logger $logger, CommandBus $innerBus) { $this->logger = $logger; $this->innerBus = $innerBus; }

public function handle($command) {

$this->innerBus->handle($command); $this->logger->debug('Executed command '.get_class($command)); }

}

Page 103: Command Bus To Awesome Town

class LoggingDecorator implements CommandBus{ private $logger; private $innerBus;

public function __construct(Logger $logger, CommandBus $innerBus) { $this->logger = $logger; $this->innerBus = $innerBus; }

public function handle($command) { $this->logger->debug('Handling command '.get_class($command)); $this->innerBus->handle($command); $this->logger->debug('Executed command '.get_class($command)); }

}

Page 104: Command Bus To Awesome Town

$commandBus = new LoggingDecorator( $logger, new MyCommandBus(...));

$commandBus->handle($command);

Page 105: Command Bus To Awesome Town
Page 106: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId()); $this->logging->debug('purchasing game');

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 107: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId());

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 108: Command Bus To Awesome Town

Ridiculously Powerful

Page 109: Command Bus To Awesome Town

Redonkulously Powerful

Page 110: Command Bus To Awesome Town

Why not Events?

Page 111: Command Bus To Awesome Town

Cohesion

Page 112: Command Bus To Awesome Town

Execution

Page 113: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId());

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 114: Command Bus To Awesome Town

Let's DecoratorizeTM

Page 115: Command Bus To Awesome Town

class TransactionDecorator implements CommandBus{ private $db; private $innerBus;

public function __construct($db, $innerBus) { $this->db = $db; $this->innerBus = $innerBus; }

public function handle($command) { try { $this->db->beginTransaction();

$this->innerBus->handle($command);

$this->db->commitTransaction(); } catch (Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 116: Command Bus To Awesome Town

The Handler?

Page 117: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId());

try { $this->db->beginTransaction();

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

$this->db->commitTransaction(); } catch(Exception $e) { $this->db->rollbackTransaction(); throw $e; } }}

Page 118: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId());

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

}}

Page 119: Command Bus To Awesome Town
Page 120: Command Bus To Awesome Town

...Wait, all the Commands?

Page 121: Command Bus To Awesome Town

Limiting which decorators run

Page 122: Command Bus To Awesome Town

Marker Interface

Page 123: Command Bus To Awesome Town

$this->security->allowsPurchase($command->getCustomerId());

Page 124: Command Bus To Awesome Town

interface PurchaseCommand{ public function getCustomerId();}

Page 125: Command Bus To Awesome Town

class PurchaseGame{ private $gameId; private $customerId;

// constructors, getters, etc}

Page 126: Command Bus To Awesome Town

class PurchaseGame implements PurchaseCommand{ private $gameId; private $customerId;

// constructors, getters, etc}

Page 127: Command Bus To Awesome Town

class AllowedPurchaseDecorator implements CommandBus{ private $security; private $innerBus;

public function __construct($security, $innerBus) { $this->security = $security; $this->innerBus = $innerBus; }

public function handle($command) { if ($command instanceof PurchaseCommand) { $this->security->allowPurchase($command->getCustomerId()); }

$this->innerBus->handle($command); }}

Page 128: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$this->security->allowsPurchase($command->getCustomerId());

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

}}

Page 129: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

}}

Page 130: Command Bus To Awesome Town

class PurchaseGameHandler{ public function handle(PurchaseGame $command) {

$purchase = new Purchase( $command->getGameId(), $command->getCustomerId() ); $this->repository->add($purchase);

}}

Stop cutting!

Page 131: Command Bus To Awesome Town

Beyond Decorators

Page 132: Command Bus To Awesome Town

new AllowedPurchaseDecorator( $security, new TransactionDecorator( $db, new LoggingDecorator( $logger, new MyCommandBus( [ PurchaseGame::class => $purchaseGameHandler ] ) ) ))

Page 133: Command Bus To Awesome Town

Constructor Parameter

Method

Page 134: Command Bus To Awesome Town

interface Middleware { public function handle($command, callable $next);}

Page 135: Command Bus To Awesome Town

class LoggingMiddleware implements Middleware{ private $logger;

public function __construct($logger) { $this->logger = $logger; }

public function handle($command, callable $next) { $this->logger->debug('handling '.get_class($command)); next($command); }}

Page 136: Command Bus To Awesome Town

new CommandBus( [ new AllowedPurchaseMiddleware($security), new TransactionMiddleware($db), new LoggingMiddleware($logger), new CommandHandlerMiddleware($handlerMapping), ] );

Page 137: Command Bus To Awesome Town

5 Things That I Have Seen

Page 138: Command Bus To Awesome Town

Thing #1Handling Commands in Commands

Page 139: Command Bus To Awesome Town

Domain Model

Service

Controller

View

PurchaseGameSendConfirmation

Page 140: Command Bus To Awesome Town

If it needs to happen with the command…JUST CALL IT

Page 141: Command Bus To Awesome Town

If it needs to happen after the command…Wait for a domain event

Page 142: Command Bus To Awesome Town

Thing #2Reading From Commands

Or the lack thereof

Page 143: Command Bus To Awesome Town

“Command Bus should never return anything.”

Page 144: Command Bus To Awesome Town

Command Bus != CQRS

Page 145: Command Bus To Awesome Town

$commandBus->handle(new PurchaseGame($gameId, $customerId));

$store->purchaseGame($gameId, $customerId);

Page 146: Command Bus To Awesome Town

$commandBus->handle(new PurchaseGame($gameId, $customerId));

$id = $this ->domainEvents ->waitFor(GamePurchased::class) ->getId();

Page 147: Command Bus To Awesome Town

Thing #3Reading with a Command Bus

Page 148: Command Bus To Awesome Town

$handler->handle(new PurchaseGame($gameId, $customerId));

$this->repository->findPurchaseById($id);$this->readService->findPurchaseVerification($id);$this->queryBus->find(new PurchaseById($id));

Page 149: Command Bus To Awesome Town

Thing #4Command Buses Work Great in JS

Page 150: Command Bus To Awesome Town

commandBus.handle({ "command": "purchase_game", "game_id": gameId, "customer_id": customerId});

Page 151: Command Bus To Awesome Town

Thing #5Single Endpoint “REST”

Page 152: Command Bus To Awesome Town

/commands

Page 153: Command Bus To Awesome Town

/purchase-game

Page 154: Command Bus To Awesome Town

The Meta of Command Buses

Page 155: Command Bus To Awesome Town

Why are there 50 of them?

Page 156: Command Bus To Awesome Town
Page 157: Command Bus To Awesome Town
Page 158: Command Bus To Awesome Town
Page 159: Command Bus To Awesome Town

μ

Page 160: Command Bus To Awesome Town

function createBus($handlers) { return function ($command) use ($handlers) { $handlers[get_class($command)]($command); };}

Page 161: Command Bus To Awesome Town

$bus = createBus( [ FooCommand::class => $someClosure, BarCommand::class => $someOtherClosure ]);

$cmd = new FooCommand();$bus($cmd);

Page 162: Command Bus To Awesome Town

But you should use my library.

Page 163: Command Bus To Awesome Town
Page 164: Command Bus To Awesome Town
Page 165: Command Bus To Awesome Town

Command Bus Libraries are useless

Page 166: Command Bus To Awesome Town

...but the plugins aren't.

Page 167: Command Bus To Awesome Town
Page 168: Command Bus To Awesome Town

Me

tactician-doctrinetactician-loggernamed commandslocking

Page 169: Command Bus To Awesome Town

@sagikazarmarktactician-bernardtactician-command-events

Page 170: Command Bus To Awesome Town

@boekkooi tactician-amqp

Page 171: Command Bus To Awesome Town

@rdohms@xtrasmal@Richard_Tuin

tactician-bundle

Page 172: Command Bus To Awesome Town

@GeeH@mike_kowalsky

tactician-module

Page 173: Command Bus To Awesome Town

Prasetyo WicaksonoEugene TerentevJildert MiedemaNigel GreenwayFrank de JongeIvan Habunek

tactician-service-provideryii2-tacticianlaravel-tacticiantactician-containertactician-awesome-advicetactician-pimple

Page 174: Command Bus To Awesome Town

Shared Interfaces

Page 175: Command Bus To Awesome Town

The Future of Tactician

Page 176: Command Bus To Awesome Town

● Simplify Interfaces● Metadata● Tracking● RAD

Page 177: Command Bus To Awesome Town

Domain Model

Service

Controller

View

Page 178: Command Bus To Awesome Town

SimpleBusBroadway

Page 179: Command Bus To Awesome Town

Command

Handler

Bus

Decorators

executes

connects extends

Page 180: Command Bus To Awesome Town

Command Handler

Page 181: Command Bus To Awesome Town

The End

Page 182: Command Bus To Awesome Town

Images

● http://bj-o23.deviantart.com/art/NOT-Rocket-Science-324795055● sambabulli.blogsport.de● https://www.youtube.com/watch?v=XHa6LIUJTPw● http://khongthe.com/wallpapers/animals/puppy-bow-227081.jpg● https://www.flickr.com/photos/emptyhighway/7173454/sizes/l● https://www.flickr.com/photos/jronaldlee/5566380424

Page 183: Command Bus To Awesome Town

domcode.org

Ross Tuck@rosstuck

joind.in/14219

PHPArchitectureTour.com