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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Chris Fidao
May 16, 2014
Technology
200k
49
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Hexagonal Architecture
An explanation on what Hexagonal Architecture is - the decoupling of layers in your code.
Chris Fidao
May 16, 2014
More Decks by Chris Fidao
See All by Chris Fidao
Development Environments that Feel Local
fideloper
0
100
Refactoring Terraform - CloudCasts - Scaling EC2
fideloper
0
110
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
380
Powering Your Applications With Nginx
fideloper
9
7.7k
Intro to etcd
fideloper
3
650
Service Oriented Architecture with a little help from NodeJS
fideloper
4
2.3k
Other Decks in Technology
See All in Technology
現場のトークンマネジメント
dak2
1
150
[チョークトーク資料]AWS DevOps Agent を使いこなす / AWS Dev Ops Agent Chalk Talk AWS Summit Japan 2026
kinunori
3
630
AIはどのように 組織のアジリティを変えるのか?
junki
4
1.1k
【NRUG vol.18】KubernetesにおけるNew Relicデータ取得量削減の考え方
nrug_member
0
170
ザ・データベース、MySQL ~ OSC 2026 Sendai ~
sakaik
0
150
SONiCで構築・運用する生成AI向けパブリッククラウドネットワーク ~実装編~
sonic
0
290
Oracle AI Database@Google Cloud:サービス概要のご紹介
oracle4engineer
PRO
6
1.6k
SONiCの統計情報を取得したい
sonic
0
240
マルチアカウント環境での コーディングエージェントを使った障害調査が大変なので AIエージェントにReadOnly権限を付与してみた / ReadOnly AI Agents for Multi-Account AWS Incident Response
yamaguchitk333
2
120
データサイエンスを価値につなげるプロジェクト設計 〜 DS一年目が現場で得た気づき 〜
ysd113
1
290
AI-DLCを “そのまま導入しなかった”話 ~組織に合わせてアジャストした 私たちの実践共有~
hiroramos4
PRO
1
250
Oracle AI Database@AWS:サービス概要のご紹介
oracle4engineer
PRO
4
3k
Featured
See All Featured
Speed Design
sergeychernyshev
33
1.9k
Ecommerce SEO: The Keys for Success Now & Beyond - #SERPConf2024
aleyda
1
2k
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
360
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
The Invisible Side of Design
smashingmag
302
52k
The Art of Programming - Codeland 2020
erikaheidi
57
14k
Building the Perfect Custom Keyboard
takai
2
800
Build your cross-platform service in a week with App Engine
jlugia
234
18k
SERP Conf. Vienna - Web Accessibility: Optimizing for Inclusivity and SEO
sarafernandez
2
1.5k
The SEO Collaboration Effect
kristinabergwall1
1
490
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.4k
Deep Space Network (abreviated)
tonyrice
0
210
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