models and service layers, hemoglobin and hobgoblins

284
This talk is clocked at 1 slide per 12.8 seconds and features unsafe amounts of code. Presenter is a registered Class 3 Fast Talker (equal to 1 Gilmore Girls episode). Viewing is not recommended for those hungover, expected to become hungover or consuming excessive amounts of caffeine. Do not watch and operate motor vehicles. If you accidentally consume this talk, flush brain with kitten pictures and seek emergency help in another talk. No hard feelings, seriously. It's almost the end of the conference, after all. Why are we even here? Surgeon General's Warning Notice in accordance with the PHP Disarmament Compact of 1992. Void where prohibited.

Upload: ross-tuck

Post on 09-May-2015

37.371 views

Category:

Technology


3 download

DESCRIPTION

As presented at ZendCon 2014, AmsterdamPHP, PHPBenelux 2014, Sweetlake PHP and PHP Northwest 2013, an overview of some different patterns for integrating and managing logic throughout your application.

TRANSCRIPT

Page 1: Models and Service Layers, Hemoglobin and Hobgoblins

This talk is clocked at 1 slide per 12.8 seconds and features unsafe amounts of code. Presenter is a registered Class 3 Fast Talker (equal to 1 Gilmore Girls episode).

Viewing is not recommended for those hungover, expected to become hungover or consuming excessive amounts of caffeine. Do not watch and operate motor vehicles.

If you accidentally consume this talk, flush brain with kitten pictures and seek emergency help in another talk. No hard feelings, seriously. It's almost the end of the conference, after all. Why are we even here?

Surgeon General's Warning

Notice in accordance with the PHP Disarmament Compact of 1992. Void where prohibited.

Page 2: Models and Service Layers, Hemoglobin and Hobgoblins

ZendCon 2014

Models & Service Layers

Hemoglobin & Hobgoblins

Ross Tuck

Page 3: Models and Service Layers, Hemoglobin and Hobgoblins

Freerange CodemonkeyKnow-It-All

Hot-Air Balloon

Page 5: Models and Service Layers, Hemoglobin and Hobgoblins

About Today

Page 6: Models and Service Layers, Hemoglobin and Hobgoblins

Hemoglobin

Page 7: Models and Service Layers, Hemoglobin and Hobgoblins

Anemia.

Page 8: Models and Service Layers, Hemoglobin and Hobgoblins
Page 9: Models and Service Layers, Hemoglobin and Hobgoblins

Objects can too.

Page 10: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function setName($name);

function getName();

function setStatus($status);

function getStatus();

function addTask($task);

function setTasks($tasks);

function getTasks();

}

Model

Page 11: Models and Service Layers, Hemoglobin and Hobgoblins

array(

'name' => '',

'status' => '',

'tasks' => ''

);

Model

Page 12: Models and Service Layers, Hemoglobin and Hobgoblins

Bad Thing TM

Page 13: Models and Service Layers, Hemoglobin and Hobgoblins

“In essence the problem with anemic domain models is that they incur all of the costs of a

domain model, without yielding any of the benefits.”

-Martin Fowler

Page 14: Models and Service Layers, Hemoglobin and Hobgoblins

Our industry standard is an antipattern.

Page 15: Models and Service Layers, Hemoglobin and Hobgoblins

Ouch.

Page 16: Models and Service Layers, Hemoglobin and Hobgoblins

Important Note

Page 17: Models and Service Layers, Hemoglobin and Hobgoblins
Page 18: Models and Service Layers, Hemoglobin and Hobgoblins
Page 19: Models and Service Layers, Hemoglobin and Hobgoblins
Page 20: Models and Service Layers, Hemoglobin and Hobgoblins

Models

Stuf

Page 21: Models and Service Layers, Hemoglobin and Hobgoblins

Integration over implementation

Page 22: Models and Service Layers, Hemoglobin and Hobgoblins

Our Setup

Page 23: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function setName($name);

function getName();

function setStatus($status);

function getStatus();

function addTask($task);

function setTasks($tasks);

function getTasks();

}

Model

Page 24: Models and Service Layers, Hemoglobin and Hobgoblins

class Task {

function setDescription($desc);

function getDescription();

function setPriority($priority);

function getPriority();

}

Model

Page 25: Models and Service Layers, Hemoglobin and Hobgoblins

An ORM that's not Doctrine 2.A framework that's not Symfony2.

I promise.

Page 26: Models and Service Layers, Hemoglobin and Hobgoblins

CRUD

Page 27: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$task = new Task();

$task->setDescription($req->get('desc'));

$task->setPriority($req->get('priority'));

$list = $this->todoRepo->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

return $this->redirect('edit_page');

}

Controller

Page 28: Models and Service Layers, Hemoglobin and Hobgoblins
Page 29: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$task = new Task();

$task->setDescription($req->get('desc'));

$task->setPriority($req->get('priority'));

$list = $this->todoRepo->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

return $this->redirect('edit_page');

}

Controller

Page 30: Models and Service Layers, Hemoglobin and Hobgoblins

Anemic ModelHard to Maintain

TestabilitySRP wha?

Page 31: Models and Service Layers, Hemoglobin and Hobgoblins

In Defense Of CRUD.No, seriously.

Page 32: Models and Service Layers, Hemoglobin and Hobgoblins

Low Barrier to Entry.

Page 33: Models and Service Layers, Hemoglobin and Hobgoblins

Easy to follow.If you can keep it in your head.

Page 34: Models and Service Layers, Hemoglobin and Hobgoblins

Sometimes it really is just data entry.(but it usually isn't)(but sometimes it is)

Page 35: Models and Service Layers, Hemoglobin and Hobgoblins

Not entirely a technical issue.

Page 36: Models and Service Layers, Hemoglobin and Hobgoblins

Service Layer

Page 37: Models and Service Layers, Hemoglobin and Hobgoblins

• Service Layer• Service Container• Web Service• Service Oriented Architecture• Domain Service• Stateless Service• Software-as-a-service• Platform-as-a-service• Whatever-as-a-service meme• Delivery Service• Laundry Service

Page 38: Models and Service Layers, Hemoglobin and Hobgoblins

Application Service

Page 39: Models and Service Layers, Hemoglobin and Hobgoblins
Page 40: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Controller

View

Page 41: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 42: Models and Service Layers, Hemoglobin and Hobgoblins

Why?

Page 43: Models and Service Layers, Hemoglobin and Hobgoblins
Page 44: Models and Service Layers, Hemoglobin and Hobgoblins

1) Multiple User InterfacesWeb + REST API

+ CLI+ Workers

Page 45: Models and Service Layers, Hemoglobin and Hobgoblins

2) “In between” Logic

Page 46: Models and Service Layers, Hemoglobin and Hobgoblins

3) Decouple from frameworks

Page 47: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 48: Models and Service Layers, Hemoglobin and Hobgoblins

Just Build The Stupid Thing

Page 49: Models and Service Layers, Hemoglobin and Hobgoblins

ServiceLayer

Page 50: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$task = new Task();

$task->setDescription($req->get('desc'));

$task->setPriority($req->get('priority'));

$list = $this->todoRepo->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

return $this->redirect('edit_page');

}

Controller

Page 51: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function addTask(TodoList $list, $desc, $priority) {

$task = new Task();

$task->setDescription($desc);

$task->setPriority($priority);

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 52: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

return $this->repository->findById($id);

}

}

Service

Page 53: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

}

Controller

Page 54: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($id, $desc, $priority) {

$list = $this->todoService->findById($id);

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask($list, $desc, $priority);

Page 55: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

function addTaskCommand($id, $desc, $priority) {

$list = $this->todoService->findById($id);

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask($list, $desc, $priority);

CLI

Page 56: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($id, $desc, $priority) {

$list = $this->todoService->findById($id);

if (!$list) { throw new NotFoundException(); }

$this->todoService->addTask($list, $desc, $priority);

Page 57: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

return $this->repository->findById($id);

}

}

Service

Page 58: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

$todo = $this->repository->findById($id);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Service

not http exception

Page 59: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($id, $desc, $priority) {

$list = $this->todoService->findById($id);

$this->todoService->addTask($list, $desc, $priority);

Page 60: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($id, $desc, $priority) {

$list = $this->todoService->findById($id);

$this->todoService->addTask($list, $desc, $priority);

Page 61: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($name, $desc, $priority) {

$list = $this->todoService->findByName($name);

$this->todoService->addTask($list, $desc, $priority);

Page 62: Models and Service Layers, Hemoglobin and Hobgoblins
Page 63: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($name, $desc, $priority) {

$list = $this->todoService->findByName($name);

$this->todoService->addTask($list, $desc, $priority);

Page 64: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($name, $desc, $priority) {

$list = $this->todoService->findByName($name);

$this->todoService->addTask($list, $desc, $priority);

Page 65: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

Controller

CLI

function addTaskCommand($name, $desc, $priority) {

$listId = $this->todoService->findIdByName($name);

$this->todoService->addTask($listId, $desc, $priority);

Page 66: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

public function findLatestLists() {

return $this->repository->findLatestLists();

}

}

Service

Page 67: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

public function findLatestLists() {

if ($this->cache->has('latest:lists')) {

return $this->cache->get('latest:lists');

}

$results = $this->repository->findLatestLists();

$this->cache->set('latest:lists', $results);

return $results;

}

}

Service

Page 68: Models and Service Layers, Hemoglobin and Hobgoblins

Indirect Advantages

Page 69: Models and Service Layers, Hemoglobin and Hobgoblins

Readability

Page 70: Models and Service Layers, Hemoglobin and Hobgoblins

Interface Protection

Page 71: Models and Service Layers, Hemoglobin and Hobgoblins

Discoverability

Page 72: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id);

function addTask($todo, $desc, $priority);

function prance();

}

Service

Page 73: Models and Service Layers, Hemoglobin and Hobgoblins

Mission Accomplished

Page 74: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function setName($name);

function getName();

function setStatus($status);

function getStatus();

function setTasks($tasks);

function getTasks();

}

Model

Page 75: Models and Service Layers, Hemoglobin and Hobgoblins

Dumb as a box of rocks.

Page 76: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function setName($name);

function getName();

function setStatus($status);

function getStatus();

function setTasks($tasks);

function getTasks();

}

Model

Where's mah logic?

Page 77: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$task = new Task();

$task->setDescription($desc);

$task->setPriority($priority);

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 78: Models and Service Layers, Hemoglobin and Hobgoblins
Page 79: Models and Service Layers, Hemoglobin and Hobgoblins

“Organizes business logic by procedures where each procedure handles a single

request from the presentation.”-Fowler

Page 80: Models and Service Layers, Hemoglobin and Hobgoblins

Transaction Scripts

Page 81: Models and Service Layers, Hemoglobin and Hobgoblins

Simple

Page 82: Models and Service Layers, Hemoglobin and Hobgoblins

More flexibleThan CRUD, at least

Page 83: Models and Service Layers, Hemoglobin and Hobgoblins

Don't scale quite as well

Page 84: Models and Service Layers, Hemoglobin and Hobgoblins

What does belong in a service layer?

Page 85: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$task = new Task();

$task->setDescription($desc);

$task->setPriority($priority);

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 86: Models and Service Layers, Hemoglobin and Hobgoblins

Orchestration

Page 87: Models and Service Layers, Hemoglobin and Hobgoblins

TransactionsSecurity

NotificationsBulk operations

Page 88: Models and Service Layers, Hemoglobin and Hobgoblins

Facade

Page 89: Models and Service Layers, Hemoglobin and Hobgoblins

Fat Model, Skinny Controller

Page 90: Models and Service Layers, Hemoglobin and Hobgoblins

Fat Model, Skinny Service Layer

Page 91: Models and Service Layers, Hemoglobin and Hobgoblins

(re)Thinking

Page 92: Models and Service Layers, Hemoglobin and Hobgoblins

addTask()

findById()

findLatestLists()

Service

writereadread

Page 93: Models and Service Layers, Hemoglobin and Hobgoblins

Remodeling our Reading

by

Refactoring our Repository

Redux

Page 94: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

$todo = $this->repository->findById($id);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Service

Page 95: Models and Service Layers, Hemoglobin and Hobgoblins

interface TodoRepository {

public function findById($id);

public function findLatestLists();

}

Repository

Page 96: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoDbRepository implements TodoRepository {

public function findById($id) {

$todo = $this->db->select(...);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Repository

Page 97: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoDbRepository implements TodoRepository {

public function findById($id) {

$todo = $this->db->select(...);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Repository

raw db connection

Page 98: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoDbRepository implements TodoRepository {

public function findById($id) {

$todo = $this->repository->find($id);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Repository

FIXED

Page 99: Models and Service Layers, Hemoglobin and Hobgoblins

interface TodoRepository {

public function findById($id);

public function findLatestLists();

}

Repository

Page 100: Models and Service Layers, Hemoglobin and Hobgoblins

interface EntityRepository {

public function createQueryBuilder($alias);

public function createResultSetMappingBuilder($alias);

public function createNamedQuery($queryName);

public function createNativeNamedQuery($queryName);

public function clear();

public function find($id, $lockMode, $lockVersion);

public function findAll();

public function findBy($criteria, $orderBy, $limit, $offset);

public function findOneBy($criteria, $orderBy);

public function __call($method, $arguments);

public function getClassName();

public function matching(Criteria $criteria);

}

Repository

Page 101: Models and Service Layers, Hemoglobin and Hobgoblins

interface TodoRepository {

public function findById($id);

public function findLatestLists();

}

Repository

Page 102: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

$todo = $this->repository->findById($id);

if (!$todo) {

throw new TodoNotFoundException();

}

return $todo;

}

}

Service

Page 103: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

return $this->repository->findById($id);

}

}

Service

Page 104: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

public function findLatestLists() {

if ($this->cache->has('latest:lists')) {

return $this->cache->get('latest:lists');

}

$results = $this->repository->findLatestLists();

$this->cache->set('latest:lists', $results);

return $results;

}

}

Service

Page 105: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoDbRepository implements TodoRepository {

public function findLatestLists() {

if ($this->cache->has('latest:lists')) {

return $this->cache->get('latest:lists');

}

$results = $this->repository->query(...);

$this->cache->set('latest:lists', $results);

return $results;

}

}

Repository

Page 106: Models and Service Layers, Hemoglobin and Hobgoblins

class CachingTodoRepository implements TodoRepository {

public function findLatestLists() {

if ($this->cache->has('latest:lists')) {

return $this->cache->get('latest:lists');

}

$results = $this->innerRepository->findLatestLists();

$this->cache->set('latest:lists', $results);

return $results;

}

}

Repository Decorator Decorator object

TodoDbRepository

Page 107: Models and Service Layers, Hemoglobin and Hobgoblins

new TodoService(

new CachingTodoRepository(

new TodoDbRepository(

$entityManager->getRepository('TodoList')

)

)

)

DI Layer

Page 108: Models and Service Layers, Hemoglobin and Hobgoblins

The Inverse Biggie Law

Page 109: Models and Service Layers, Hemoglobin and Hobgoblins

Mo' classesMo' decoupling and reduced overall design issues

Page 110: Models and Service Layers, Hemoglobin and Hobgoblins

Too many finder methods?

Page 111: Models and Service Layers, Hemoglobin and Hobgoblins

$this->todoService->matching(array(

new ListIsClosedCriteria(),

new HighPriorityCriteria()

));

Controller

Page 112: Models and Service Layers, Hemoglobin and Hobgoblins

Doctrine\Criteria

Page 113: Models and Service Layers, Hemoglobin and Hobgoblins

Interlude: Services here...

...services there...

...services everywhere!

Page 114: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TaskService

TodoList

TodoListService

Tag

TagService

TaskRepository TodoListRepository TagRepository

Page 115: Models and Service Layers, Hemoglobin and Hobgoblins
Page 116: Models and Service Layers, Hemoglobin and Hobgoblins
Page 117: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TaskService

TodoList

TodoListService

Tag

TagService

TaskRepository TodoListRepository TagRepository

Page 118: Models and Service Layers, Hemoglobin and Hobgoblins
Page 119: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TodoList

TodoService

Tag

Page 120: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TodoList

TodoService

Tag

User

UserService

Page 121: Models and Service Layers, Hemoglobin and Hobgoblins
Page 122: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TodoList

TodoService

Tag

User

UserService

Page 123: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

public function findByUser(User $user) {

return $this->repository->findByUser($user);

}

}

Service

Page 124: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

public function findByUser(UserId $userId) {

return $this->repository->findByUser($userId);

}

}

Service

Page 125: Models and Service Layers, Hemoglobin and Hobgoblins

Task

TodoList

TodoService

Tag

User

UserService

Interfaces!

Page 126: Models and Service Layers, Hemoglobin and Hobgoblins

Services aren't only for entities

Page 127: Models and Service Layers, Hemoglobin and Hobgoblins

Scale can differ wildly

Page 128: Models and Service Layers, Hemoglobin and Hobgoblins

PrintingService

Page 129: Models and Service Layers, Hemoglobin and Hobgoblins

Quality of Implementation

Page 130: Models and Service Layers, Hemoglobin and Hobgoblins

(re)Modeling our Writing

Page 131: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$task = new Task();

$task->setDescription($desc);

$task->setPriority($priority);

$task->setTodoList($list);

$this->repository->save($task);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 132: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask(Task $task) {

$this->tasks[] = $task;

}

}

Model

Page 133: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask($desc, $priority) {

$task = new Task();

$task->setDescription($desc);

$task->setPriority($priority);

$this->tasks[] = $task;

}

}

Model

Page 134: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority);

$this->tasks[] = $task;

}

}

Model

Page 135: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

}

}

Model

ORM allowance

Page 136: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority);

$this->tasks[] = $task;

}

}

Model

Page 137: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 138: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority);

$this->tasks[] = $task;

}

}

Model

Page 139: Models and Service Layers, Hemoglobin and Hobgoblins

Model

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority);

$this->tasks[] = $task;

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

}

Page 140: Models and Service Layers, Hemoglobin and Hobgoblins

Meaningful Tests

Page 141: Models and Service Layers, Hemoglobin and Hobgoblins

Working Together

Page 142: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 143: Models and Service Layers, Hemoglobin and Hobgoblins

Model

class TodoList {

function addTask($desc, $priority) {

$task = new Task($desc, $priority);

$this->tasks[] = $task;

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

}

Page 144: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 145: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

if (count($this->tasks) > 10) {

$this->auditLog->logTooAmbitious($task);

$this->mailer->sendMessage('Too unrealistic');

}

}

}

Service

Page 146: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

if (count($this->tasks) > 10) {

$this->auditLog->logTooAmbitious($task);

$this->mailer->sendMessage('Too unrealistic');

}

}

}

Service

Page 147: Models and Service Layers, Hemoglobin and Hobgoblins

PrintingService

TodoService

Page 148: Models and Service Layers, Hemoglobin and Hobgoblins

Something new...

Page 149: Models and Service Layers, Hemoglobin and Hobgoblins

Something better...

Page 150: Models and Service Layers, Hemoglobin and Hobgoblins

Domain Events

Page 151: Models and Service Layers, Hemoglobin and Hobgoblins

Common Pattern

Page 152: Models and Service Layers, Hemoglobin and Hobgoblins

Observer

Page 153: Models and Service Layers, Hemoglobin and Hobgoblins

New usage

Page 154: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

Page 155: Models and Service Layers, Hemoglobin and Hobgoblins

Event

class TaskAddedEvent {

protected $description;

protected $priority;

function __construct($desc, $priority) {

$this->description = $desc;

$this->priority = $priority;

}

}

Page 156: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

Page 157: Models and Service Layers, Hemoglobin and Hobgoblins

Model

class TodoList {

protected $pendingEvents = array();

protected function raise($event) {

$this->pendingEvents[] = $event;

}

public function releaseEvents() {

$events = $this->pendingEvents;

$this->pendingEvents = array();

return $events;

}

}

Excellent Trait

Page 158: Models and Service Layers, Hemoglobin and Hobgoblins

No dispatcher

Page 159: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$this->auditLog->logNewTask($task);

$this->mailer->sendMessage('New thingy!');

}

}

Service

Page 160: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

}

}

Service

Page 161: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function addTask(TodoList $list, $desc, $priority) {

$list->addTask($desc, $priority);

$this->repository->save($list);

$events = $list->releaseEvents();

$this->eventDispatcher->dispatch($events);

}

}

Service

Page 162: Models and Service Layers, Hemoglobin and Hobgoblins

Event Listeners

class EmailListener {

function onTaskAdded($event) {

$taskDesc = $event->getDescription();

$this->mailer->sendMessage('New thingy: '.$taskDesc);

}

function onUserRegistered($event) {

$this->mailer->sendMessage('welcome sucka!');

}

}

Page 163: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

Page 164: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

$this->raise(new WasTooAmbitiousEvent($this->id));

}

}

Page 165: Models and Service Layers, Hemoglobin and Hobgoblins

Nice things:

Page 166: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

$this->raise(new WasTooAmbitiousEvent($this->id));

}

}

Logic is here!

Page 167: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

protected $dependency1;

protected $dependency2;

protected $dependency3;

protected $dependency4;

protected $dependency5;

protected $dependency6;

}

Service

Big ball of mud

in the making

Page 168: Models and Service Layers, Hemoglobin and Hobgoblins

Event Listeners

class EmailListener {

function onTaskAdded($event) {

$taskName = $event->task->getName();

$this->mailer->sendMessage('New thingy: '.$taskName);

}

function onUserRegistered($event) {

$this->mailer->sendMessage('welcome sucka!');

}

} Thin. Easy to test

Page 169: Models and Service Layers, Hemoglobin and Hobgoblins

PrintingService

TodoService

Serialize & Send,

Sucka!

Page 170: Models and Service Layers, Hemoglobin and Hobgoblins

Model

function addTask($desc, $priority) {

$task = new Task($desc, $priority, $this);

$this->tasks[] = $task;

$this->raise(

new TaskAddedEvent($this->id, $desc, $priority)

);

if (count($this->tasks) > 10) {

$this->status = static::UNREALISTIC;

}

}

Page 171: Models and Service Layers, Hemoglobin and Hobgoblins

Less nice things.

Page 172: Models and Service Layers, Hemoglobin and Hobgoblins

Humans hate debugging events.

Dev Logging.Debug comma

nds.

Page 173: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 174: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 175: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 176: Models and Service Layers, Hemoglobin and Hobgoblins

Consuming Application Services

Page 177: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

}

Controller

Page 178: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$this->todoService->addTask(

$list, $req->get('desc'), $req->get('priority')

);

return $this->redirect('edit_page');

}

Controller

Page 179: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$list->addTask($req->get('desc'), $req->get('priority'));

return $this->redirect('edit_page');

}

Controller

Page 180: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$list->addTask($req->get('desc'), $req->get('priority'));

$list->rename('blah');

return $this->redirect('edit_page');

}

Controller

Page 181: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$list = $this->todoService->findById($req->get('id'));

$list->addTask($req->get('desc'), $req->get('priority'));

$list->rename('blah');

$this->todoService->addTask(...);

return $this->redirect('edit_page');

}

Controller

Page 182: Models and Service Layers, Hemoglobin and Hobgoblins
Page 183: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 184: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 185: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

Page 186: Models and Service Layers, Hemoglobin and Hobgoblins
Page 187: Models and Service Layers, Hemoglobin and Hobgoblins

View Models

Page 188: Models and Service Layers, Hemoglobin and Hobgoblins

PHP version, not MVVM.

Page 189: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

$todoList = $this->repository->findById($id);

return $todoList;

}

}

Service

Page 190: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function findById($id) {

$todoList = $this->repository->findById($id);

return new TodoDTO($todoList);

}

}

Service

Page 191: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoDTO {

public function getName();

public function getStatus();

public function getMostRecentTask();

}

TodoDTO

Page 192: Models and Service Layers, Hemoglobin and Hobgoblins
Page 193: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoService {

function generateReport() {

$data = $this->repository->performSomeCrazyQuery();

return new AnnualGoalReport($data);

}

}

Service

Page 194: Models and Service Layers, Hemoglobin and Hobgoblins

Ain't rocket science.

Page 195: Models and Service Layers, Hemoglobin and Hobgoblins

Reverse it: DTOs not for output...

Page 196: Models and Service Layers, Hemoglobin and Hobgoblins

...but for input.

Page 197: Models and Service Layers, Hemoglobin and Hobgoblins

Going Commando

Page 198: Models and Service Layers, Hemoglobin and Hobgoblins
Page 199: Models and Service Layers, Hemoglobin and Hobgoblins
Page 200: Models and Service Layers, Hemoglobin and Hobgoblins

Command

class AddTaskCommand {

public $description;

public $priority;

public $todoListId;

}

Page 201: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$command = new AddTaskCommand();

$command->description = $req->get('description');

$command->priority = $req->get('priority');

$command->todoListId = $req->get('todo_id');

$this->todoService->execute($command);

return $this->redirect('edit_page');

}

}

Controller

Page 202: Models and Service Layers, Hemoglobin and Hobgoblins

ServiceController Handler Bar

Handler Baz

Handler Foo

Page 203: Models and Service Layers, Hemoglobin and Hobgoblins

ServiceController

Handler Baz

Page 204: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

}

Service

Page 205: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

}

}

Service

Page 206: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

get_class($command);

}

}

Service

Page 207: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

$command->getName();

}

}

Service

Page 208: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

$command->execute();

}

}

Service

Page 209: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

}

}

Service

Page 210: Models and Service Layers, Hemoglobin and Hobgoblins

What goes in a handler?

Page 211: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListHandler {

function handleAddTask($cmd) {

$list = $this->repository->findById($cmd->todoListId);

$list->addTask($cmd->description, $cmd->priority);

}

function handleCompleteTask($command)

function handleRemoveTask($command)

}

Handler

Page 212: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

function execute($command) {

}

}

Service

Page 213: Models and Service Layers, Hemoglobin and Hobgoblins

class CommandBus {

function execute($command) {

}

}

Service

Page 214: Models and Service Layers, Hemoglobin and Hobgoblins

class MyCommandBus implements CommandBus {

function execute($command) {

}

}

Service

Page 215: Models and Service Layers, Hemoglobin and Hobgoblins

class ValidatingCommandBus implements CommandBus {

function execute($command) {

if (!$this->validator->isValid($command)) {

throw new InvalidCommandException();

}

$this->innerCommandBus->execute($command);

}

}

Service

Page 216: Models and Service Layers, Hemoglobin and Hobgoblins

Command

class AddTaskCommand {

public $description;

public $priority;

public $todoListId;

}

Page 217: Models and Service Layers, Hemoglobin and Hobgoblins

Command

use Symfony\Component\Validator\Constraints as Assert;

class AddTaskCommand {

/** @Assert\Length(max="50") */

public $description;

public $priority;

public $todoListId;

}

Page 218: Models and Service Layers, Hemoglobin and Hobgoblins

LoggingTransactions

Event Dispatching

Page 219: Models and Service Layers, Hemoglobin and Hobgoblins

Fewer Dependencies per class.Simple layers.Easy to test.

Page 220: Models and Service Layers, Hemoglobin and Hobgoblins

View Models + Commands

Page 221: Models and Service Layers, Hemoglobin and Hobgoblins

Model

Service Layer

Controller

View

ViewModelsCommands

Page 222: Models and Service Layers, Hemoglobin and Hobgoblins

CRUD for the framework.Domain Model for the chewy center.

formstemplatesvalidators

tough logicsemanticstesting

Page 223: Models and Service Layers, Hemoglobin and Hobgoblins

Diverge Further

Page 224: Models and Service Layers, Hemoglobin and Hobgoblins

CQRS

Page 225: Models and Service Layers, Hemoglobin and Hobgoblins

On the surface, it looks the same.

Page 226: Models and Service Layers, Hemoglobin and Hobgoblins

function addTaskAction($req) {

$command = new AddTaskCommand();

$command->description = $req->get('description');

$command->priority = $req->get('priority');

$command->todoListId = $req->get('todo_id');

$this->commandBus->execute($command);

return $this->redirect('edit_page');

}

}

Controller

Page 227: Models and Service Layers, Hemoglobin and Hobgoblins

CQS

Page 228: Models and Service Layers, Hemoglobin and Hobgoblins

Commands = Change DataQueries = Read Data

Page 229: Models and Service Layers, Hemoglobin and Hobgoblins

CQRS

Page 230: Models and Service Layers, Hemoglobin and Hobgoblins

Model

class TodoList {

function rename($name);

function addTask($desc, $priority);

function getName();

function getTasks();

}

Page 231: Models and Service Layers, Hemoglobin and Hobgoblins

Two Models

Page 232: Models and Service Layers, Hemoglobin and Hobgoblins

Write Model

class TodoListModel {

function rename($name);

function addTask($desc, $priority);

}

class TodoListView {

function getName();

function getTasks();

}

Read Model

Page 233: Models and Service Layers, Hemoglobin and Hobgoblins
Page 234: Models and Service Layers, Hemoglobin and Hobgoblins

Model

class TodoList {

function rename($name);

function addTask($desc, $priority);

function getName();

function getTasks();

function getParticipatingUsers();

}

Page 235: Models and Service Layers, Hemoglobin and Hobgoblins

Write Model

class TodoListModel {

function rename($name);

function addTask($desc, $priority);

}

class TodoListView {

function getName();

function getTasks();

function getParticipatingUsers();

}

Read Model

Page 236: Models and Service Layers, Hemoglobin and Hobgoblins

Write Model

class TodoListModel {

function rename($name);

function addTask($desc, $priority);

}

class TodoListView {

function getName();

function getTasks();

function getParticipatingUsers();

}

Read Model

ORM entity1 Model

SQL queryN Models

Page 237: Models and Service Layers, Hemoglobin and Hobgoblins

Read and Write are two different systems.

Page 238: Models and Service Layers, Hemoglobin and Hobgoblins

User and Shopping Cart?

Page 239: Models and Service Layers, Hemoglobin and Hobgoblins

Same kind of split.

Page 240: Models and Service Layers, Hemoglobin and Hobgoblins

Surrounding classes?

Page 241: Models and Service Layers, Hemoglobin and Hobgoblins

A lot of it looks the same.

Page 242: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListHandler {

function handleAddTask($cmd) {

$list = $this->repository->findById($cmd->todoListId);

$list->addTask($cmd->description, $cmd->priority);

}

}

Handler

Page 243: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

public function findByUser(User $user) {

return $this->repository->findByUser($user);

}

}

Service

Page 244: Models and Service Layers, Hemoglobin and Hobgoblins

class TodoListService {

public function findByUser(User $user) {

return $this->repository->findByUser($user);

}

}

Service

class TodoListHandler {

function handleAddTask($cmd) {

$list = $this->repository->findById($cmd->todoListId);

$list->addTask($cmd->description, $cmd->priority);

}

}

Handler

Page 245: Models and Service Layers, Hemoglobin and Hobgoblins
Page 246: Models and Service Layers, Hemoglobin and Hobgoblins

$todoList = new TodoList();

$this->repository->save($todoList);

$todoList->getId();

Controller

Page 247: Models and Service Layers, Hemoglobin and Hobgoblins

$command = new CreateTodoCommand(UUID::create());

$commandBus->execute($command);

$command->uuid;

Controller

Page 248: Models and Service Layers, Hemoglobin and Hobgoblins

Zoom Out

Page 249: Models and Service Layers, Hemoglobin and Hobgoblins

Martin Fowler waz here

Page 250: Models and Service Layers, Hemoglobin and Hobgoblins
Page 251: Models and Service Layers, Hemoglobin and Hobgoblins
Page 252: Models and Service Layers, Hemoglobin and Hobgoblins
Page 253: Models and Service Layers, Hemoglobin and Hobgoblins
Page 254: Models and Service Layers, Hemoglobin and Hobgoblins

Domain events

Page 255: Models and Service Layers, Hemoglobin and Hobgoblins

DB Views

Page 256: Models and Service Layers, Hemoglobin and Hobgoblins

BigHonkingQueue

Page 257: Models and Service Layers, Hemoglobin and Hobgoblins

github.com/beberlei/litecqrs-php/github.com/qandidate-labs/broadway

github.com/gregoryyoung/m-r

Page 258: Models and Service Layers, Hemoglobin and Hobgoblins

Pros & Cons

Page 259: Models and Service Layers, Hemoglobin and Hobgoblins

Big mental leap.Usually more LOC.

Not for every domain.Can be mixed.

Page 260: Models and Service Layers, Hemoglobin and Hobgoblins

Easy to Scale.Bears Complexity.Async Operations.

Event Sourcing.

Page 261: Models and Service Layers, Hemoglobin and Hobgoblins

Event Sourcing?

Page 262: Models and Service Layers, Hemoglobin and Hobgoblins

CQRS+

Event Sourcing

Page 263: Models and Service Layers, Hemoglobin and Hobgoblins

Instead of storing the current state in the db...

Page 264: Models and Service Layers, Hemoglobin and Hobgoblins

...store the domain events?

Page 265: Models and Service Layers, Hemoglobin and Hobgoblins

SnapshotsDebuggingAudit Log

Business IntelligenceOnline/Offline users

Retroactively Fix Bugs

Page 266: Models and Service Layers, Hemoglobin and Hobgoblins

Google it.Or ask me afterwards.

Page 267: Models and Service Layers, Hemoglobin and Hobgoblins

Epilogue

Page 268: Models and Service Layers, Hemoglobin and Hobgoblins

"A foolish consistency is the hobgoblin of little minds."

- Ralph Waldo Emerson

Page 269: Models and Service Layers, Hemoglobin and Hobgoblins

Strong opinions, weakly held.

Page 270: Models and Service Layers, Hemoglobin and Hobgoblins

Strong techniques, weakly held.

Page 271: Models and Service Layers, Hemoglobin and Hobgoblins

PHP 3

Page 272: Models and Service Layers, Hemoglobin and Hobgoblins

PHP 4 -5

Page 273: Models and Service Layers, Hemoglobin and Hobgoblins

PHP 5.3+

Page 274: Models and Service Layers, Hemoglobin and Hobgoblins

PHP 7

Page 275: Models and Service Layers, Hemoglobin and Hobgoblins

Might seem crazy.

Page 276: Models and Service Layers, Hemoglobin and Hobgoblins

Bang for the buck.

Page 277: Models and Service Layers, Hemoglobin and Hobgoblins

People ARE doing this.

Page 278: Models and Service Layers, Hemoglobin and Hobgoblins

It IS working for them.

Page 279: Models and Service Layers, Hemoglobin and Hobgoblins

You can too.

Page 280: Models and Service Layers, Hemoglobin and Hobgoblins

Questions?

Page 281: Models and Service Layers, Hemoglobin and Hobgoblins

Further Reading• codebetter.com/gregyoung• martinfowler.com/tags/domain driven design.html• shawnmc.cool/domain-driven-design• whitewashing.de• verraes.net

Page 282: Models and Service Layers, Hemoglobin and Hobgoblins

Thanks To:

• Warnar Boekkooi @boekkooi

• Daan van Renterghem @DRvanR• Matthijs van den Bos @matthijsvandenb

Page 283: Models and Service Layers, Hemoglobin and Hobgoblins

Image Credits• http://www.flickr.com/photos/calgaryreviews/6427412605/sizes/l/

• http://msnbcmedia.msn.com/j/MSNBC/Components/Slideshows/_production/twisp_090511_/twisp_090511_02.ss_full.jpg

• http://shotthroughawindow.wordpress.com/2011/07/22/hp7b/

• http://www.sxc.hu/photo/605471

• http://martinfowler.com/bliki/images/cqrs/cqrs.png

• http://www.flickr.com/photos/83346641@N00/5578975430/in/photolist-9uZH2y-8rTZL1-dixjR-ffBPiv-8SbK8K-ffS4md-6UeEGP

• http://www.flickr.com/photos/lac-bac/7195938394/sizes/o/

• http://tdzdaily.org/wp-content/uploads/2013/03/Dumb-and-Dumber.png

• http://upload.wikimedia.org/wikipedia/commons/1/17/Charlton_Heston_in_The_Ten_Commandments_film_trailer.jpg

• http://commons.wikimedia.org/wiki/File:Trench_construction_diagram_1914.png

• http://www.flickr.com/photos/jeffreyww/4747314852/sizes/l/

• http://www.flickr.com/photos/jdhancock/3540861791/sizes/l/

• http://www.flickr.com/photos/superfantastic/50088733/sizes/l

Page 284: Models and Service Layers, Hemoglobin and Hobgoblins

joind.in/12101

@rosstuck

Ross Tuckrosstuck.com