$30 off During Our Annual Pro Sale. View Details »

Hexagonal Architecture

Hexagonal Architecture

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

Chris Fidao

May 16, 2014
Tweet

More Decks by Chris Fidao

Other Decks in Technology

Transcript

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

    View Slide

  2. @fideloper

    View Slide

  3. View Slide

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

    View Slide

  5. Vaprobash
    Vagrant Provisioning Bash Scripts

    View Slide

  6. Servers for Hackers.com

    View Slide

  7. Why / What
    Ports / Adapters
    Boundary Layers
    /

    View Slide

  8. WHY
    Architecture

    View Slide

  9. Maintainability
    Technical
    Debt
    Time

    View Slide

  10. What is it?

    View Slide

  11. So…What
    is it?

    View Slide

  12. The Hexagon
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  13. (Core) Domain
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  14. Behavior

    View Slide

  15. Constraints

    View Slide

  16. Application
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  17. Framework
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  18. Outside
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  19. Ports Adapters
    /

    View Slide

  20. Ports & Adapters
    Core
    Domain
    Application
    Domain
    Framework

    View Slide

  21. Inside/Outside
    Core
    Domain
    Application
    Domain
    CommandBus
    Framework
    HTTP
    Use Case
    Repo
    DBAL
    Database
    Events
    Dispatcher
    Service Impl

    View Slide

  22. Core
    Domain
    Application
    Domain
    Framework
    Dependencies

    View Slide

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

    View Slide

  24. Use-Case Driven
    Development

    View Slide

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

    View Slide

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

    View Slide

  27. // Class SimpleCommandBus
    !
    public function execute( $command )
    {
    return $this->getHandler( $command )
    ->handle( $command );
    }
    Simple CommandBus

    View Slide

  28. Core
    Domain
    Application
    Domain
    CommandBus
    Framework
    HTTP
    Use Case
    Repo
    DBAL
    Database
    Events
    Dispatcher
    Service Impl

    View Slide

  29. View Slide

  30. Boundaries

    View Slide

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

    View Slide

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

    View Slide

  33. Core
    Domain
    Application
    Domain
    Framework
    Use Case
    Repo
    Events

    View Slide

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

    View Slide

  35. Application
    Domain
    CommandBus
    Framework
    DBAL
    Dispatcher
    The Application/External
    Boundary

    View Slide

  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);
    }

    View Slide

  37. Framework
    Core
    Domain
    Application
    Domain
    Framework
    HTTP
    Database
    Service Impl

    View Slide

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

    View Slide

  39. Layers

    View Slide

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

    View Slide

  41. !
    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;
    }
    }

    View Slide

  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);
    }
    }

    View Slide

  43. class CreateTicketCommand {
    !
    protected $data;
    !
    public function __construct($data)
    {
    $this->data = $data;
    }
    !
    public function __get($property)
    {
    // Simplified example
    return $this->data[$property];
    }
    }

    View Slide

  44. The Application
    Application
    Domain
    CommandBus
    Framework
    DBAL
    Dispatcher

    View Slide

  45. // Class SimpleCommandBus
    !
    public function execute( $command )
    {
    return $this->getHandler( $command )
    ->handle( $command );
    }

    View Slide

  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
    }
    }

    View Slide

  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();
    }
    }

    View Slide

  48. Framework
    Core
    Domain
    Application
    Domain
    Framework
    HTTP
    Database
    Service Impl

    View Slide

  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');
    }
    }

    View Slide

  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]]
    ]);
    }
    }

    View Slide

  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 );
    }
    }
    !
    }

    View Slide

  52. TDD is DEAD
    (and other
    myths)

    View Slide

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

    View Slide

  54. Thanks

    View Slide