Hexagonal Architecture
Hexagonal Architecture
Boundary/Layers
WHY
Architecture
Maintainability
Technical
Debt
Time
What is it?
So…What
is it?
The Hexagon
Core
Domain
Domain
Application
Framework
(Core) Domain
Core
Domain
Domain
Application
Framework
Behavior
Constraints
Application
Core
Domain
Domain
Application
Framework
Framework
Core
Domain
Domain
Application
Framework
Outside
Core
Domain
Domain
Application
Framework
Ports /Adapters
Ports & Adapters
Core
Domain
Domain
Application
Framework
Inside/Outside
HTTP
CommandBus
Use Case
Core
Domain
ts
er
Re
l
en
tch
p
DB
Im
Da
Ev
o
pa
AL
tab
Domain
ce
Dis
rvi
as
Application
Se
e
Framework
Dependencies
Core
Domain
Domain
Application
Framework
interface Notifier {
!
public function send(Message $message);
}
•API
•CLI
•Queue
•Event Handler
Use Cases:
CommandBus
// Class SimpleCommandBus
!
public function execute( $command )
{
return $this->getHandler( $command )
->handle( $command );
}
HTTP
CommandBus
Use Case
Core
Domain
ts
r
Re
he
pl
en
DB
atc
p
Im
Da
Ev
o
AL
tab
Domain
p
ce
Dis
rvi
as
Application
Se
e
Framework
Boundaries
Domain/Application
Boundary
Use Case
Core
Domain
nts
Re
e
po
Ev
Domain
Application
Framework
interface CommandBusInterface {
!
public function execute( $command );
}
interface HandlerInterface {
!
public function handle( $command );
}
Use Case
Core
Domain
ts
Re
en
p
Ev
o
Domain
Application
Framework
interface TicketRepositoryInterface {
!
public function getStaffOpenTickets(
Staffer $staffer, $limit=10);
!
!
public function save(Ticket $model);
}
The Application/External
Boundary
CommandBus
er
tch
DB
pa
AL
Domain Dis
Application
Framework
interface Notifier {
!
public function send(Message $message);
}
interface Validator {
!
public function passes(Array $data);
!
public function getErrors();
}
interface Dispatcher {
!
public function dispatch(Array $events);
}
Framework
HTTP
Core
Domain
l p
Im
Da
tab
Domain
ce
rvi
as
Application Se
e
Framework
Identify the aspects
that vary and
separate them from
what stays the
same
Layers
The Domain
Use Case
Core
Domain
ts
Re
en
po
Ev
Domain
Application
Framework
<?php namespace Hex\Tickets;
!
class Ticket extends Model {
!
public function assignStaffer(Staffer $staffer)
{
if( ! $staffer->categories->contains( $this->category ) )
{
throw new DomainException("Staffer can't be assigned to ".$this->category);
}
!
$this->staffer()->associate($staffer); // Set Relationship
!
return $this;
}
!
public function setCategory(Category $category)
{
if( $this->staffer instanceof Staffer &&
! $this->staffer->categories->contains( $category ) )
{
// Unset staffer if can't be assigned to set category
$this->staffer = null;
}
!
$this->category()->associate($category); // Set Relationship
!
return $this;
}
}
class Ticket extends Model {
!
/* ... Other logic ... */
!
public function save(array $options = array())
{
/* Integrity Checks, and then: */
!
if( ! $this->exists )
{
$this->raise( new TicketCreatedEvent($this) );
}
!
return parent::save($options);
}
}
class CreateTicketCommand {
!
protected $data;
!
public function __construct($data)
{
$this->data = $data;
}
!
public function __get($property)
{
// Simplified example
return $this->data[$property];
}
}
The Application
CommandBus
er
tch
DB
pa
AL
Domain
Application Dis
Framework
// Class SimpleCommandBus
!
Core
Domain
l p
Im
Da
tab
Domain
ce
rvi
as
Application
Se
e
Framework
class TicketController extends BaseController {
!
public function createTicket()
{
$command = new CreateTicketCommand( Input::all() );
!
try {
$this->bus->execute($command);
}
catch(ValidationException $e)
{
return Redirect::to('/tickets/new')
->withErrors( $e->getErrors() );
} catch(DomainException $e)
{
return Redirect::to('/tickets/new')
->withErrors( $e->getErrors() );
}
return Redirect::to(‘/tickets');
}
}
class SesEmailNotifier implements NotifierInterface {
!
public function __construct(SesClient $client)
{
$this->client = $client;
}
!
public function send(Message $message)
{
$to = [$message->to()];
$message = ['Data' => $message->message()];
!
$this->client->sendEmail([
'Destination' => ['ToAddresses' => $to],
'Message' => ['Body' => ['Html' => $message]]
]);
}
}
use Illuminate\Events\Dispatcher as EventDispatcher;
!
class LaravelDispatcher implements Dispatcher {
!
public function __construct(EventDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
!
public function dispatch(Array $events)
{
foreach( $events as $event )
{
$this->dispatcher->fire( $event->name(), $event );
}
}
!
}
TDD is DEAD
(and other
myths)
Identify the aspects
that vary and
separate them from
what stays the
same
Thanks