Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hexagonal Architecture

Hexagonal Architecture

An explanation on what Hexagonal Architecture is - the decoupling of layers in your code.

64b0ae04826716bcc1ca5dfd003145fe?s=128

Chris Fidao

May 16, 2014
Tweet

Transcript

  1. Hexagonal Architecture Chris Fidao (hek-sag-uh-nl)

  2. @fideloper

  3. None
  4. Implementing Laravel Real-world implementation of testable and maintainable code. (hopefully)

  5. Vaprobash Vagrant Provisioning Bash Scripts

  6. Servers for Hackers.com

  7. Why / What Ports / Adapters Boundary Layers /

  8. WHY Architecture

  9. Maintainability Technical Debt Time

  10. What is it?

  11. So…What is it?

  12. The Hexagon Core Domain Application Domain Framework

  13. (Core) Domain Core Domain Application Domain Framework

  14. Behavior

  15. Constraints

  16. Application Core Domain Application Domain Framework

  17. Framework Core Domain Application Domain Framework

  18. Outside Core Domain Application Domain Framework

  19. Ports Adapters /

  20. Ports & Adapters Core Domain Application Domain Framework

  21. Inside/Outside Core Domain Application Domain CommandBus Framework HTTP Use Case

    Repo DBAL Database Events Dispatcher Service Impl
  22. Core Domain Application Domain Framework Dependencies

  23. interface Notifier { ! public function send(Message $message); } class

    SesNotifier implements Notifier { ! public function send(Message $message) { // Details } }
  24. Use-Case Driven Development

  25. All the Contexts •Web •API •CLI •Queue •Event Handler

  26. Use Cases: CommandBus CommandBus executes( ) Command Handler handles( )

    Command
  27. // Class SimpleCommandBus ! public function execute( $command ) {

    return $this->getHandler( $command ) ->handle( $command ); } Simple CommandBus
  28. Core Domain Application Domain CommandBus Framework HTTP Use Case Repo

    DBAL Database Events Dispatcher Service Impl
  29. None
  30. Boundaries

  31. Domain/Application Boundary Core Domain Application Domain Framework Use Case Repo

    Events
  32. interface CommandBusInterface { ! public function execute( $command ); }

    interface HandlerInterface { ! public function handle( $command ); }
  33. Core Domain Application Domain Framework Use Case Repo Events

  34. interface TicketRepositoryInterface { ! public function getStaffOpenTickets( Staffer $staffer, $limit=10);

    ! ! public function save(Ticket $model); }
  35. Application Domain CommandBus Framework DBAL Dispatcher The Application/External Boundary

  36. interface Notifier { ! public function send(Message $message); } interface

    Validator { ! public function passes(Array $data); ! public function getErrors(); } interface Dispatcher { ! public function dispatch(Array $events); }
  37. Framework Core Domain Application Domain Framework HTTP Database Service Impl

  38. Identify the aspects that vary and separate them from what

    stays the same
  39. Layers

  40. The Domain Core Domain Application Domain Framework Use Case Repo

    Events
  41. <?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; } }
  42. 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); } }
  43. class CreateTicketCommand { ! protected $data; ! public function __construct($data)

    { $this->data = $data; } ! public function __get($property) { // Simplified example return $this->data[$property]; } }
  44. The Application Application Domain CommandBus Framework DBAL Dispatcher

  45. // Class SimpleCommandBus ! public function execute( $command ) {

    return $this->getHandler( $command ) ->handle( $command ); }
  46. ! 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 } }
  47. 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(); } }
  48. Framework Core Domain Application Domain Framework HTTP Database Service Impl

  49. 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'); } }
  50. 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]] ]); } }
  51. 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 ); } } ! }
  52. TDD is DEAD (and other myths)

  53. Identify the aspects that vary and separate them from what

    stays the same
  54. Thanks