Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Hexagonal Architecture
Search
Chris Fidao
May 16, 2014
Technology
49
200k
Hexagonal Architecture
An explanation on what Hexagonal Architecture is - the decoupling of layers in your code.
Chris Fidao
May 16, 2014
Tweet
Share
More Decks by Chris Fidao
See All by Chris Fidao
Development Environments that Feel Local
fideloper
0
52
Refactoring Terraform - CloudCasts - Scaling EC2
fideloper
0
78
Scaling Laravel - Laracon.net 2018
fideloper
15
1.9k
Linux Environment
fideloper
1
11k
Server Survival
fideloper
29
23k
FileBeat (Won't save you from the JVM)
fideloper
1
330
Powering Your Applications With Nginx
fideloper
9
7.7k
Intro to etcd
fideloper
3
590
Service Oriented Architecture with a little help from NodeJS
fideloper
4
2.3k
Other Decks in Technology
See All in Technology
【新卒研修資料】数理最適化 / Mathematical Optimization
brainpadpr
25
12k
2時間で300+テーブルをデータ基盤に連携するためのAI活用 / FukuokaDataEngineer
sansan_randd
0
140
AIのグローバルトレンド 2025 / ai global trend 2025
kyonmm
PRO
1
130
Nx × AI によるモノレポ活用 〜コードジェネレーター編〜
puku0x
0
430
金融サービスにおける高速な価値提供とAIの役割 #BetAIDay
layerx
PRO
1
780
VLMサービスを用いた請求書データ化検証 / SaaSxML_Session_1
sansan_randd
0
240
データ基盤の管理者からGoogle Cloud全体の管理者になっていた話
zozotech
PRO
0
410
データモデリング通り #2オンライン勉強会 ~方法論の話をしよう~
datayokocho
0
140
Amazon Q と『音楽』-ゲーム音楽もAmazonQで作成してみた感想-
senseofunity129
0
120
Telemetry APIから学ぶGoogle Cloud ObservabilityとOpenTelemetryの現在 / getting-started-telemetry-api-with-google-cloud
k6s4i53rx
0
130
Bet "Bet AI" - Accelerating Our AI Journey #BetAIDay
layerx
PRO
4
1.6k
いかにして命令の入れ替わりについて心配するのをやめ、メモリモデルを愛するようになったか(改)
nullpo_head
7
2.4k
Featured
See All Featured
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
What's in a price? How to price your products and services
michaelherold
246
12k
A Tale of Four Properties
chriscoyier
160
23k
Scaling GitHub
holman
461
140k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
139
34k
Being A Developer After 40
akosma
90
590k
A designer walks into a library…
pauljervisheath
207
24k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.4k
Bootstrapping a Software Product
garrettdimon
PRO
307
110k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
3.9k
GraphQLの誤解/rethinking-graphql
sonatard
71
11k
Transcript
Hexagonal Architecture Chris Fidao (hek-sag-uh-nl)
@fideloper
None
Implementing Laravel Real-world implementation of testable and maintainable code. (hopefully)
Vaprobash Vagrant Provisioning Bash Scripts
Servers for Hackers.com
Why / What Ports / Adapters Boundary Layers /
WHY Architecture
Maintainability Technical Debt Time
What is it?
So…What is it?
The Hexagon Core Domain Application Domain Framework
(Core) Domain Core Domain Application Domain Framework
Behavior
Constraints
Application Core Domain Application Domain Framework
Framework Core Domain Application Domain Framework
Outside Core Domain Application Domain Framework
Ports Adapters /
Ports & Adapters Core Domain Application Domain Framework
Inside/Outside Core Domain Application Domain CommandBus Framework HTTP Use Case
Repo DBAL Database Events Dispatcher Service Impl
Core Domain Application Domain Framework Dependencies
interface Notifier { ! public function send(Message $message); } class
SesNotifier implements Notifier { ! public function send(Message $message) { // Details } }
Use-Case Driven Development
All the Contexts •Web •API •CLI •Queue •Event Handler
Use Cases: CommandBus CommandBus executes( ) Command Handler handles( )
Command
// Class SimpleCommandBus ! public function execute( $command ) {
return $this->getHandler( $command ) ->handle( $command ); } Simple CommandBus
Core Domain Application Domain CommandBus Framework HTTP Use Case Repo
DBAL Database Events Dispatcher Service Impl
None
Boundaries
Domain/Application Boundary Core Domain Application Domain Framework Use Case Repo
Events
interface CommandBusInterface { ! public function execute( $command ); }
interface HandlerInterface { ! public function handle( $command ); }
Core Domain Application Domain Framework Use Case Repo Events
interface TicketRepositoryInterface { ! public function getStaffOpenTickets( Staffer $staffer, $limit=10);
! ! public function save(Ticket $model); }
Application Domain CommandBus Framework DBAL Dispatcher The Application/External Boundary
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 Core Domain Application Domain Framework HTTP Database Service Impl
Identify the aspects that vary and separate them from what
stays the same
Layers
The Domain Core Domain Application Domain Framework Use Case Repo
Events
<?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 Application Domain CommandBus Framework DBAL Dispatcher
// Class SimpleCommandBus ! public function execute( $command ) {
return $this->getHandler( $command ) ->handle( $command ); }
! class CreateTicketHandler implements HandlerInterface { ! ! public function
handle($command) { $this->validate($command); // Throw ValidationException $this->save($command); } ! protected function save($command) { $ticket = new Ticket; /* Some other setters... */ $ticket->setCategory( $this->catRepo->find($command->category_id) ); $ticket->setStaffer( $this->staffRepo->find($command->staffer_id) ); $ticket->addMessage( $ticket->addMessage($command->message); ); ! $this->ticketRepo->save($ticket); // Use Repositories ! $this->dispatcher->dispatch( $ticket->flushEvents() ); // Fire Events } }
class DbTicketRepository implements RepositoryInterface { ! public function getStaffOpenTickets(Staffer $staffer,
$limit=10) { return $this->ticket->where('staff_id', $staffer->id) ->take($limit)->get(); } ! public function save(Ticket $ticket) { $ticket->save(); } }
Framework Core Domain Application Domain Framework HTTP Database Service Impl
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