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
92
Refactoring Terraform - CloudCasts - Scaling EC2
fideloper
0
100
Scaling Laravel - Laracon.net 2018
fideloper
15
2k
Linux Environment
fideloper
1
11k
Server Survival
fideloper
29
24k
FileBeat (Won't save you from the JVM)
fideloper
1
360
Powering Your Applications With Nginx
fideloper
9
7.7k
Intro to etcd
fideloper
3
640
Service Oriented Architecture with a little help from NodeJS
fideloper
4
2.3k
Other Decks in Technology
See All in Technology
詳解 強化学習 / In-depth Guide to Reinforcement Learning
prinlab
0
370
生成AIで速度と品質を両立する、QAエンジニア・開発者連携のAI協調型テストプロセス
shota_kusaba
0
490
「お金で解決」が全てではない!大規模WebアプリのCI高速化 #phperkaigi
stefafafan
5
2.2k
Blue/Green Deployment を用いた PostgreSQL のメジャーバージョンアップ
kkato1
0
110
コンテキスト・ハーネスエンジニアリングの現在
hirosatogamo
PRO
6
760
中央集権型を脱却した話 分散型をやめて、連邦型にたどり着くまで
sansantech
PRO
1
320
AIエージェント勉強会第3回 エージェンティックAIの時代がやってきた
ymiya55
0
100
スピンアウト講座05_実践活用事例
overflowinc
0
1.1k
20年以上続く PHP 大規模プロダクトを Kubernetes へ ── クラウド基盤刷新プロジェクトの4年間
oogfranz
PRO
0
170
AIエージェント×GitHubで実現するQAナレッジの資産化と業務活用 / QA Knowledge as Assets with AI Agents & GitHub
tknw_hitsuji
0
190
Escape from Excel方眼紙 ~マークダウンで繋ぐ、人とAIの架け橋~ /nikkei-tech-talk44
nikkei_engineer_recruiting
0
190
形式手法特論:SMT ソルバで解く認可ポリシの静的解析 #kernelvm / Kernel VM Study Tsukuba No3
ytaka23
1
760
Featured
See All Featured
Mobile First: as difficult as doing things right
swwweet
225
10k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
330
エンジニアに許された特別な時間の終わり
watany
106
240k
Ruling the World: When Life Gets Gamed
codingconduct
0
180
Producing Creativity
orderedlist
PRO
348
40k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
110k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
Leo the Paperboy
mayatellez
4
1.5k
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
0
240
The Invisible Side of Design
smashingmag
302
51k
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
1
3.5k
The Spectacular Lies of Maps
axbom
PRO
1
640
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