SlideShare a Scribd company logo
1 of 37
Hexagonal
Architecture
Paulo Victor
Systems Analyst, Open
Source Developer, Zend
Certified Engineer PHP
5.3.
@pv_fusion
VOUCHER: php_conf2015
Presentation licensed by
This presentation is free to use under Creative Commons Attribution license. If you use the content
and graphic assets (photos, icons, typographies) provided with this presentation you must keep the
Credits slide.
Beginnings...
How?
◇ How can I separate Domain from Framework?
◇ How can I decouple the libraries?
◇ How can I make communication between layers?
◇ How can I make a semantic structure?
You need to think about
Software Architecture
Software Architecture
Why do we even talk about Architecture?
◇ High Maintainability
◇ Low Technical Debt
◇ Semantic structure (Layers are
responsible for doing what they
should do)
Software Architecture
The architecture of a software system is a
metaphor, analogous to the architecture of a
building.
Perry, D. E.; Wolf, A. L. (1992). "Foundations for the study of software
architecture".
1
#weareallhexagons
WhatWhy How
Architectural Styles
In order to be able to build complex applications,
one of the key aspects is having an architecture
design that fits the application needs.
Buenosvinos, C., Soronellas C. Akbary K. (2015) "Domain-Driven
Design in PHP"
2
#weareallhexagons
WhatWhy How
Hexagonal Architecture3
#weareallhexagons
WhatWhy How
Allow an application to equally be driven by users,
programs, automated test or batch scripts, and to
be developed and tested in isolation from its
eventual run-time devices and databases.
Cockburn, A. (2008). "Hexagonal architecture".
Domain
#weareallhexagons
Ports and Adapters
<?php namespace DomainCore;
interface MarketRepository extends RepositoryInterface
{
/**
* Find Market By KeyName
* @param $keyName
* @return Market
*/
public function byKeyName($keyName);
/**
* Find Market By KeyName and token
* @param $keyName
* @param $token
* @return Market
*/
public function byKeyNameAndToken($keyName, $token);
/**
* Create an Market
* @param Market $market
* @return Market
*/
public function add(Market $market);
}
<?php namespace AppBundleInfrastructureCore;
class MarketRepository extends PDORepository implements
DomainCoreMarketRepository
{
public function byKeyName($keyName)
{
$sql = "select * from market where key_name = $keyName";
return $this->map($this->getRepository()
->query($sql));
}
public function byKeyNameAndToken($keyName, $token)
{
$sql = <<<SQL
select * from market
where key_name = $keyName
and access_token = $token
SQL;
return $this->map($this->getRepository()
->query($sql));
}
...
}
PDO Adapter Port
<?php namespace DomainCore;
interface MarketRepository extends RepositoryInterface
{
/**
* Find Market By KeyName
* @param $keyName
* @return Market
*/
public function byKeyName($keyName);
/**
* Find Market By KeyName and token
* @param $keyName
* @param $token
* @return Market
*/
public function byKeyNameAndToken($keyName, $token);
/**
* Create an Market
* @param Market $market
* @return Market
*/
public function add(Market $market);
}
<?php namespace AppBundleInfrastructureCore;
class MarketRepository extends EntityRepository implements
DomainCoreMarketRepository
{
/**
* {@inheritdoc}
*/
public function byKeyName($keyName)
{
return $this->getRepository()
->findOneByKeyName($keyName);
}
public function byKeyNameAndToken($keyName, $token)
{
return $this->getRepository()
->findOneBy(['keyName' => $keyName, 'accessToken' => $token]);
}
public function add(DomainCoreMarket $market)
{
$this->getEntityManager()->persist($market);
$this->getEntityManager()->flush();
}
}
Doctrine Adapter Port
<?php namespace DomainCore;
interface MarketRepository extends RepositoryInterface
{
/**
* Find Market By KeyName
* @param $keyName
* @return Market
*/
public function byKeyName($keyName);
/**
* Find Market By KeyName and token
* @param $keyName
* @param $token
* @return Market
*/
public function byKeyNameAndToken($keyName,
$token);
/**
* Create an Market
* @param Market $market
* @return Market
*/
public function add(Market $market);
}
<?php namespace AppBundleInfrastructureCore;
class MarketRepository extends SolrRepository implements
DomainCoreMarketRepository
{
public function byKeyName($keyName)
{
$marketId = $this->elasticSearchRepository()->search('mkt', [$keyName]);
$market = new Market();
$market->setId($marketId);
$market->setName($this->redisRepository()->get('mkt':'.$keyName.':name'));
$market->setKeyName($this->redisRepository()->get('mkt':'.$keyName.':keyname'));
$market->setAccessToken($this->redisRepository()->get('mkt':'.$keyName.':token'));
return $market;
}
public function byKeyNameAndToken($keyName, $token)
{
$marketId = $this->elasticSearchRepository()->search('mkt', [$keyName, $token]);
$market = new Market();
$market->setId($marketId);
$market->setName($this->redisRepository()->get('mkt':'.$keyName.':name'));
$market->setKeyName($this->redisRepository()->get('mkt':'.$keyName.':keyname'));
$market->setAccessToken($this->redisRepository()->get('mkt':'.$keyName.':token'));
return $market;
}
}
Redis and Solr Adapter Port
“
Domain Driven Design (DDD)
Do you know DDD ?
DDD is about placing our attention at the
heart of the application, focusing on the
complexity that is intrinsic to the business
domain itself.
The reason to an
Application
Troubleshoot space and time
problems
What is the best
way to do this?
Encapsulating the business
rules with layers
Work with layers
“Hexagonal Architecture defines
conceptual layers of code
responsibility, and then points
out ways to decouple code
between those layers. It's helped
clarify when, how and why we
use interfaces (among other
ideas)”
Fideloper
Coupling between layers
User Interface
Application
Domain
Infrastructure
Coupling between layers
User Interface
Application
Domain
Infrastructure
Dependency Inversion Principle
Decoupling between layers
Core
Domain
Domain
Application
Infrastructure
S
Q
L
Dependencies
Core
Domain
Domain
Application
Infrastructure
S
Q
L
Boundaries
Core
Domain
Domain
Application
Infrastructure
S
Q
L
Communication Between
Layers: Boundaries
Use Case Bus Handle
CommandBus
Command
Handler
Command
executes( )
handles( )
Fideloper. Hexagonal Architecture
CommandBus
Use Case
Use Case
<?php namespace HexCommandBus;
interface CommandInterface {}
<?php namespace HexTicketsCommands;
use HexCommandBusCommandInterface;
class CreateTicketCommand implements CommandInterface {
/**
* @var array
*/
public $data;
public function __construct(Array $data)
{
$this->data = $data;
}
public function __get($property)
{
if( isset($this->data[$property]) )
{
return $this->data[$property];
}
return null;
}
}
https://github.com/fideloper/hexagonal-php
https://github.com/fideloper/hexagonal-php
interface CommandBusInterface {
public function execute(CommandInterface $command);
}
class CommandBus implements CommandBusInterface {
private $container;
private $inflector;
public function __construct(Container $container, CommandNameInflector
$inflector)
{
$this->container = $container;
$this->inflector = $inflector;
}
public function execute(CommandInterface $command)
{
return $this->getHandler($command)->handle($command);
}
private function getHandler($command)
{
return $this->container->make( $this->inflector->getHandler($command) );
}
}
<?php namespace HexTicketsHandlers;
class CreateTicketHandler implements HandlerInterface {
private $validator;
private $repository;
private $dispatcher;
public function __construct(CreateTicketValidator $validator, TicketRepositoryInterface $repository, Dispatcher $dispatcher)
{
// set attributes
}
public function handle(CommandInterface $command)
{
$this->validator->validate($command);
$this->save($command);
}
protected function save($command)
{
$message = new Message;
$message->message = $command->message;
$ticket = new Ticket;
$ticket->subject = $command->subject;
$ticket->name = $command->name;
$ticket->email = $command->email;
$ticket->setCategory( Category::find($command->category_id) ); // Need repo
$ticket->setStaffer( Staffer::find($command->staffer_id) ); // Need repo
$ticket->addMessage( $message );
$this->repository->save($ticket);
$this->dispatcher->dispatch( $ticket->flushEvents() );
}
}
https://github.com/fideloper/hexagonal-php
Structure
Core
Domain
Domain
Application
Infrastructure
S
Q
L
Biso
Symfony 2
FriendsOfSym
fony
{
"name": "Symfony2Biso",
"type": "project",
"require": {
"php": ">=5.3.9",
"friendsofsymfony/rest-bundle": "1.7.*",
"predis/predis": "1.0.*",
"biso": "dev-master"
},
"repositories": [
{
"type": "package",
"package": {
"name": "biso",
"version": "dev-master",
"source": {
"url": "https://github.com/pvgomes/biso",
"type": "git",
"reference": "origin/master"
},
"autoload": {
"psr-0": { "Domain": "src" }
}
}
}
],
}
<?php
namespace Domain;
interface Command {
public function repositories();
public function eventName();
public function eventNameError();
}
<?php namespace AppBundleApplicationCore;
use AppBundleInfrastructureCoreConfiguration;
use Domain;
class CreateConfigurationCommand implements DomainCommand
{
public $data;
private $configuration;
private $eventName;
public function __construct($marketKey, $key, $value)
{
$this->eventName = DomainCoreEvents::MARKET_CREATE_CONFIGURATION;
$this->configuration = new Configuration();
$this->data = ['marketKey' => $marketKey, 'key' => $key, 'value' => $value];
}
public function __get($property)
{
$value = null;
if( isset($this->data[$property]) ) {
$value = $this->data[$property];
}
return $value;
}
...
Create Configuration Command
<?php
namespace Domain;
interface CommandBus {
public function execute(Command $command);
}
<?php namespace AppBundleApplicationCommandBus;
class CommandBus implements DomainCommandBus
{
private $container;
private $eventDispatcher;
private $inflector;
private $applicationEvent;
public function __construct(ContainerInterface $container, CommandNameInflector $inflector)
{
$this->container = $container;
$this->inflector = $inflector;
$this->eventDispatcher = $container->get('event_dispatcher');
$this->applicationEvent = new ApplicationEvent();
}
public function execute(DomainCommand $command)
{
try {
$this->applicationEvent->setCommand($command);
$response = $this->getHandler($command)->handle($command);
$this->eventDispatcher->dispatch($command->eventName(), $this->applicationEvent);
} catch (Exception $exception) {
$this->applicationEvent->setException($exception);
$this->eventDispatcher->dispatch($command->eventNameError(), $this-
>applicationEvent);
throw $exception;
}
return $response;
Command Bus
<?php
namespace Domain;
interface Handler {
public function handle(Command $command);
}
<?php namespace DomainCore;
class CreateConfigurationHandler implements Handler {
private $configurationRepository;
private $market;
public function __construct(Market $market)
{
$this->market = $market;
}
public function handle(Command $command)
{
return $this->save($command);
}
protected function save(Command $command)
{
$configuration = $command->configurationEntity();
if (!$configuration instanceof Configuration) {
throw new DomainException("Invalid configuration");
}
$configuration->setMarket($this->market);
$configuration->setKey($command->key);
$configuration->setValue($command->value);
$this->configurationRepository->add($configuration);
return $configuration->getId();
}
...
Command Handler
<?php namespace AppBundleApplicationControllerWeb;
class SystemController extends Controller {
/**
* @Route("/system/configuration", name="configuration_list")
* @param Request $request
* @return SymfonyComponentHttpFoundationResponse
*/
public function configurationAction(Request $request)
{
$form = $this->createFormBuilder([])->add('key', 'text')->add('value', 'textarea')->getForm();
if ($request->isMethod('POST')) {
$form->handleRequest($request);
$data = $form->getData();
try {
$createConfigurationCommand = new CreateConfigurationCommand($this->getUser()->getMarket()->getKeyName(), $data['key'], $data['value']);
$this->get("command_bus")->execute($createConfigurationCommand);
$flashMsg = "Chave gravada.";
$flashMsgType = "success";
} catch (DomainException $e) {
$flashMsg = $e->getMessage();
$flashMsgType = "warning";
} catch (Exception $e) {
$flashMsg = "Erro ao inserir a chave de configuração.";
$flashMsgType = "warning";
}
$this->addFlash($flashMsgType , $flashMsg);
}
$viewVars['form'] = $form->createView();
return $this->render('web/system/configuration.html.twig', $viewVars);
}
Create Config Usage | Browser
<?php namespace AppBundleApplicationApiv1Controller;
class SystemController extends ApiController implements TokenAuthentication {
use JsonValidator;
public function configurationCreate()
{
$request = $this->get('request');
$marketKey = $request->headers->get('key');
$requestContent = json_decode($$request->getContent());
$jsonResponse = new JsonResponse();
try {
if (!$this->isValidJson($this->loadConfigurationCreateSchema(), $requestContent)) {
throw new HttpException(400, $this->getJsonErrors());
}
$createConfigurationCommand = new CreateConfigurationCommand($marketKey, $requestContent->key, $requestContent->value);
$this->get("command_bus")>execute($createConfigurationCommand);
$jsonResponse->setStatusCode(204);
} catch (DomainException $exception) {
$contentError['description'] = $exception->getMessage();
$jsonResponse->setStatusCode(400);
$jsonResponse->setData($contentError);
} catch (Exception $exception) {
$contentError['description'] = $exception->getMessage();
$jsonResponse->setStatusCode(500);
$jsonResponse->setData($contentError);
}
return $jsonResponse;
}
...
Create Config Usage | API
/pvgomes/symfony2biso
Thanks!
Any questions?
You can find me at:
◇ @pv_fusion
◇ pv.gomes89@gmail.com
Credits
Special thanks to all the people who made and released
these awesome resources for free:
◇ Contents of this presentation Paulo Victor Gomes
◇ Presentation template by SlidesCarnival
◇ Photographs by Unsplash
References
❖ BROOKS, FREDERICK. The Design of Design: Essays from a Computer Scientist.
❖ BUENOSVINOS, CARLOS. SORONELLA, CHRISTIAN. AKBARY, KEYVAN. Domain Driven Design in PHP.
❖ COCKBURN, ALISTAIR. Hexagonal Architecture.
❖ VERNON, VAUGHN - Implementing Domain-Driven Design.
❖ EVANS, ERICK. Domain Driven Design: Tackling Complexity in the Heart of Software.

More Related Content

What's hot

The Secrets of Hexagonal Architecture
The Secrets of Hexagonal ArchitectureThe Secrets of Hexagonal Architecture
The Secrets of Hexagonal ArchitectureNicolas Carlo
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in phpLeonardo Proietti
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with SpringJoshua Long
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean ArchitectureMattia Battiston
 
Kata: Hexagonal Architecture / Ports and Adapters
Kata: Hexagonal Architecture / Ports and AdaptersKata: Hexagonal Architecture / Ports and Adapters
Kata: Hexagonal Architecture / Ports and Adaptersholsky
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass SlidesNir Kaufman
 
Spring Framework - Core
Spring Framework - CoreSpring Framework - Core
Spring Framework - CoreDzmitry Naskou
 
Spring Framework
Spring Framework  Spring Framework
Spring Framework tola99
 
Dependency injection - the right way
Dependency injection - the right wayDependency injection - the right way
Dependency injection - the right wayThibaud Desodt
 
jQuery Fundamentals
jQuery FundamentalsjQuery Fundamentals
jQuery FundamentalsGil Fink
 
MVC ppt presentation
MVC ppt presentationMVC ppt presentation
MVC ppt presentationBhavin Shah
 
이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정Arawn Park
 

What's hot (20)

The Secrets of Hexagonal Architecture
The Secrets of Hexagonal ArchitectureThe Secrets of Hexagonal Architecture
The Secrets of Hexagonal Architecture
 
Clean architecture with ddd layering in php
Clean architecture with ddd layering in phpClean architecture with ddd layering in php
Clean architecture with ddd layering in php
 
Solid principles
Solid principlesSolid principles
Solid principles
 
Domain Driven Design 101
Domain Driven Design 101Domain Driven Design 101
Domain Driven Design 101
 
Spring Core
Spring CoreSpring Core
Spring Core
 
REST APIs with Spring
REST APIs with SpringREST APIs with Spring
REST APIs with Spring
 
React JS
React JSReact JS
React JS
 
Real Life Clean Architecture
Real Life Clean ArchitectureReal Life Clean Architecture
Real Life Clean Architecture
 
Kata: Hexagonal Architecture / Ports and Adapters
Kata: Hexagonal Architecture / Ports and AdaptersKata: Hexagonal Architecture / Ports and Adapters
Kata: Hexagonal Architecture / Ports and Adapters
 
Nestjs MasterClass Slides
Nestjs MasterClass SlidesNestjs MasterClass Slides
Nestjs MasterClass Slides
 
Spring Boot
Spring BootSpring Boot
Spring Boot
 
Spring Framework - Core
Spring Framework - CoreSpring Framework - Core
Spring Framework - Core
 
Spring Framework
Spring Framework  Spring Framework
Spring Framework
 
Rest API
Rest APIRest API
Rest API
 
Dependency injection - the right way
Dependency injection - the right wayDependency injection - the right way
Dependency injection - the right way
 
jQuery Fundamentals
jQuery FundamentalsjQuery Fundamentals
jQuery Fundamentals
 
MVC ppt presentation
MVC ppt presentationMVC ppt presentation
MVC ppt presentation
 
Spring boot
Spring bootSpring boot
Spring boot
 
이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정이벤트 기반 분산 시스템을 향한 여정
이벤트 기반 분산 시스템을 향한 여정
 
Introduction to Spring Boot
Introduction to Spring BootIntroduction to Spring Boot
Introduction to Spring Boot
 

Similar to Hexagonal architecture in PHP

Exploring Symfony's Code
Exploring Symfony's CodeExploring Symfony's Code
Exploring Symfony's CodeWildan Maulana
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2Hugo Hamon
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Hugo Hamon
 
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)James Titcumb
 
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition!
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition! Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition!
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition! Michel Schudel
 
The Best Way to Become an Android Developer Expert with Android Jetpack
The Best Way to Become an Android Developer Expert  with Android JetpackThe Best Way to Become an Android Developer Expert  with Android Jetpack
The Best Way to Become an Android Developer Expert with Android JetpackAhmad Arif Faizin
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmersAlexander Varwijk
 
Laravel for Web Artisans
Laravel for Web ArtisansLaravel for Web Artisans
Laravel for Web ArtisansRaf Kewl
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 3camp
 
PHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationPHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationAlexander Obukhov
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5Darren Craig
 
Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Darwin Biler
 
Visual Studio .NET2010
Visual Studio .NET2010Visual Studio .NET2010
Visual Studio .NET2010Satish Verma
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to DomainJeremy Cook
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)James Titcumb
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)Jose Manuel Pereira Garcia
 

Similar to Hexagonal architecture in PHP (20)

Exploring Symfony's Code
Exploring Symfony's CodeExploring Symfony's Code
Exploring Symfony's Code
 
Best practices tekx
Best practices tekxBest practices tekx
Best practices tekx
 
Speed up your developments with Symfony2
Speed up your developments with Symfony2Speed up your developments with Symfony2
Speed up your developments with Symfony2
 
Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2Build powerfull and smart web applications with Symfony2
Build powerfull and smart web applications with Symfony2
 
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP Srbija 2017)
 
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition!
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition! Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition!
Battle Of The Microservice Frameworks: Micronaut versus Quarkus edition!
 
The Best Way to Become an Android Developer Expert with Android Jetpack
The Best Way to Become an Android Developer Expert  with Android JetpackThe Best Way to Become an Android Developer Expert  with Android Jetpack
The Best Way to Become an Android Developer Expert with Android Jetpack
 
Gwt.create
Gwt.createGwt.create
Gwt.create
 
Multilingualism makes better programmers
Multilingualism makes better programmersMultilingualism makes better programmers
Multilingualism makes better programmers
 
Laravel for Web Artisans
Laravel for Web ArtisansLaravel for Web Artisans
Laravel for Web Artisans
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2
 
PHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generationPHP: GraphQL consistency through code generation
PHP: GraphQL consistency through code generation
 
What's New In Laravel 5
What's New In Laravel 5What's New In Laravel 5
What's New In Laravel 5
 
Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4Building Large Scale PHP Web Applications with Laravel 4
Building Large Scale PHP Web Applications with Laravel 4
 
Vertx daitan
Vertx daitanVertx daitan
Vertx daitan
 
Vertx SouJava
Vertx SouJavaVertx SouJava
Vertx SouJava
 
Visual Studio .NET2010
Visual Studio .NET2010Visual Studio .NET2010
Visual Studio .NET2010
 
Beyond MVC: from Model to Domain
Beyond MVC: from Model to DomainBeyond MVC: from Model to Domain
Beyond MVC: from Model to Domain
 
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
Kicking off with Zend Expressive and Doctrine ORM (PHP UK 2017)
 
From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)From Legacy to Hexagonal (An Unexpected Android Journey)
From Legacy to Hexagonal (An Unexpected Android Journey)
 

More from Paulo Victor Gomes

More from Paulo Victor Gomes (9)

Functional as a service TDC 2020
Functional as a service TDC 2020Functional as a service TDC 2020
Functional as a service TDC 2020
 
PHP as a Service TDC2019
PHP as a Service TDC2019PHP as a Service TDC2019
PHP as a Service TDC2019
 
PHP as a Service
PHP as a ServicePHP as a Service
PHP as a Service
 
Stacks Cloud - Digital Ocean
Stacks Cloud - Digital OceanStacks Cloud - Digital Ocean
Stacks Cloud - Digital Ocean
 
O mundo do e commerce visto pela ótica do PHP
O mundo do e commerce visto pela ótica do PHPO mundo do e commerce visto pela ótica do PHP
O mundo do e commerce visto pela ótica do PHP
 
Essay about event driven architecture
Essay about event driven architectureEssay about event driven architecture
Essay about event driven architecture
 
DDD in PHP
DDD in PHPDDD in PHP
DDD in PHP
 
PHP e Redis
PHP e RedisPHP e Redis
PHP e Redis
 
Domain Driven Design PHP TDC2014
Domain Driven Design PHP TDC2014Domain Driven Design PHP TDC2014
Domain Driven Design PHP TDC2014
 

Recently uploaded

How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brandgvaughan
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024Lonnie McRorey
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 

Recently uploaded (20)

How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
WordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your BrandWordPress Websites for Engineers: Elevate Your Brand
WordPress Websites for Engineers: Elevate Your Brand
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024TeamStation AI System Report LATAM IT Salaries 2024
TeamStation AI System Report LATAM IT Salaries 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 

Hexagonal architecture in PHP

  • 1. Hexagonal Architecture Paulo Victor Systems Analyst, Open Source Developer, Zend Certified Engineer PHP 5.3. @pv_fusion
  • 3. Presentation licensed by This presentation is free to use under Creative Commons Attribution license. If you use the content and graphic assets (photos, icons, typographies) provided with this presentation you must keep the Credits slide.
  • 5. How? ◇ How can I separate Domain from Framework? ◇ How can I decouple the libraries? ◇ How can I make communication between layers? ◇ How can I make a semantic structure? You need to think about Software Architecture
  • 6. Software Architecture Why do we even talk about Architecture? ◇ High Maintainability ◇ Low Technical Debt ◇ Semantic structure (Layers are responsible for doing what they should do)
  • 7. Software Architecture The architecture of a software system is a metaphor, analogous to the architecture of a building. Perry, D. E.; Wolf, A. L. (1992). "Foundations for the study of software architecture". 1 #weareallhexagons WhatWhy How
  • 8. Architectural Styles In order to be able to build complex applications, one of the key aspects is having an architecture design that fits the application needs. Buenosvinos, C., Soronellas C. Akbary K. (2015) "Domain-Driven Design in PHP" 2 #weareallhexagons WhatWhy How
  • 9. Hexagonal Architecture3 #weareallhexagons WhatWhy How Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. Cockburn, A. (2008). "Hexagonal architecture".
  • 12. <?php namespace DomainCore; interface MarketRepository extends RepositoryInterface { /** * Find Market By KeyName * @param $keyName * @return Market */ public function byKeyName($keyName); /** * Find Market By KeyName and token * @param $keyName * @param $token * @return Market */ public function byKeyNameAndToken($keyName, $token); /** * Create an Market * @param Market $market * @return Market */ public function add(Market $market); } <?php namespace AppBundleInfrastructureCore; class MarketRepository extends PDORepository implements DomainCoreMarketRepository { public function byKeyName($keyName) { $sql = "select * from market where key_name = $keyName"; return $this->map($this->getRepository() ->query($sql)); } public function byKeyNameAndToken($keyName, $token) { $sql = <<<SQL select * from market where key_name = $keyName and access_token = $token SQL; return $this->map($this->getRepository() ->query($sql)); } ... } PDO Adapter Port
  • 13. <?php namespace DomainCore; interface MarketRepository extends RepositoryInterface { /** * Find Market By KeyName * @param $keyName * @return Market */ public function byKeyName($keyName); /** * Find Market By KeyName and token * @param $keyName * @param $token * @return Market */ public function byKeyNameAndToken($keyName, $token); /** * Create an Market * @param Market $market * @return Market */ public function add(Market $market); } <?php namespace AppBundleInfrastructureCore; class MarketRepository extends EntityRepository implements DomainCoreMarketRepository { /** * {@inheritdoc} */ public function byKeyName($keyName) { return $this->getRepository() ->findOneByKeyName($keyName); } public function byKeyNameAndToken($keyName, $token) { return $this->getRepository() ->findOneBy(['keyName' => $keyName, 'accessToken' => $token]); } public function add(DomainCoreMarket $market) { $this->getEntityManager()->persist($market); $this->getEntityManager()->flush(); } } Doctrine Adapter Port
  • 14. <?php namespace DomainCore; interface MarketRepository extends RepositoryInterface { /** * Find Market By KeyName * @param $keyName * @return Market */ public function byKeyName($keyName); /** * Find Market By KeyName and token * @param $keyName * @param $token * @return Market */ public function byKeyNameAndToken($keyName, $token); /** * Create an Market * @param Market $market * @return Market */ public function add(Market $market); } <?php namespace AppBundleInfrastructureCore; class MarketRepository extends SolrRepository implements DomainCoreMarketRepository { public function byKeyName($keyName) { $marketId = $this->elasticSearchRepository()->search('mkt', [$keyName]); $market = new Market(); $market->setId($marketId); $market->setName($this->redisRepository()->get('mkt':'.$keyName.':name')); $market->setKeyName($this->redisRepository()->get('mkt':'.$keyName.':keyname')); $market->setAccessToken($this->redisRepository()->get('mkt':'.$keyName.':token')); return $market; } public function byKeyNameAndToken($keyName, $token) { $marketId = $this->elasticSearchRepository()->search('mkt', [$keyName, $token]); $market = new Market(); $market->setId($marketId); $market->setName($this->redisRepository()->get('mkt':'.$keyName.':name')); $market->setKeyName($this->redisRepository()->get('mkt':'.$keyName.':keyname')); $market->setAccessToken($this->redisRepository()->get('mkt':'.$keyName.':token')); return $market; } } Redis and Solr Adapter Port
  • 15. “ Domain Driven Design (DDD) Do you know DDD ? DDD is about placing our attention at the heart of the application, focusing on the complexity that is intrinsic to the business domain itself.
  • 16. The reason to an Application Troubleshoot space and time problems What is the best way to do this? Encapsulating the business rules with layers
  • 17. Work with layers “Hexagonal Architecture defines conceptual layers of code responsibility, and then points out ways to decouple code between those layers. It's helped clarify when, how and why we use interfaces (among other ideas)” Fideloper
  • 18. Coupling between layers User Interface Application Domain Infrastructure
  • 19. Coupling between layers User Interface Application Domain Infrastructure Dependency Inversion Principle
  • 24. CommandBus Command Handler Command executes( ) handles( ) Fideloper. Hexagonal Architecture CommandBus Use Case Use Case
  • 25. <?php namespace HexCommandBus; interface CommandInterface {} <?php namespace HexTicketsCommands; use HexCommandBusCommandInterface; class CreateTicketCommand implements CommandInterface { /** * @var array */ public $data; public function __construct(Array $data) { $this->data = $data; } public function __get($property) { if( isset($this->data[$property]) ) { return $this->data[$property]; } return null; } } https://github.com/fideloper/hexagonal-php
  • 26. https://github.com/fideloper/hexagonal-php interface CommandBusInterface { public function execute(CommandInterface $command); } class CommandBus implements CommandBusInterface { private $container; private $inflector; public function __construct(Container $container, CommandNameInflector $inflector) { $this->container = $container; $this->inflector = $inflector; } public function execute(CommandInterface $command) { return $this->getHandler($command)->handle($command); } private function getHandler($command) { return $this->container->make( $this->inflector->getHandler($command) ); } }
  • 27. <?php namespace HexTicketsHandlers; class CreateTicketHandler implements HandlerInterface { private $validator; private $repository; private $dispatcher; public function __construct(CreateTicketValidator $validator, TicketRepositoryInterface $repository, Dispatcher $dispatcher) { // set attributes } public function handle(CommandInterface $command) { $this->validator->validate($command); $this->save($command); } protected function save($command) { $message = new Message; $message->message = $command->message; $ticket = new Ticket; $ticket->subject = $command->subject; $ticket->name = $command->name; $ticket->email = $command->email; $ticket->setCategory( Category::find($command->category_id) ); // Need repo $ticket->setStaffer( Staffer::find($command->staffer_id) ); // Need repo $ticket->addMessage( $message ); $this->repository->save($ticket); $this->dispatcher->dispatch( $ticket->flushEvents() ); } } https://github.com/fideloper/hexagonal-php
  • 29. { "name": "Symfony2Biso", "type": "project", "require": { "php": ">=5.3.9", "friendsofsymfony/rest-bundle": "1.7.*", "predis/predis": "1.0.*", "biso": "dev-master" }, "repositories": [ { "type": "package", "package": { "name": "biso", "version": "dev-master", "source": { "url": "https://github.com/pvgomes/biso", "type": "git", "reference": "origin/master" }, "autoload": { "psr-0": { "Domain": "src" } } } } ], }
  • 30. <?php namespace Domain; interface Command { public function repositories(); public function eventName(); public function eventNameError(); } <?php namespace AppBundleApplicationCore; use AppBundleInfrastructureCoreConfiguration; use Domain; class CreateConfigurationCommand implements DomainCommand { public $data; private $configuration; private $eventName; public function __construct($marketKey, $key, $value) { $this->eventName = DomainCoreEvents::MARKET_CREATE_CONFIGURATION; $this->configuration = new Configuration(); $this->data = ['marketKey' => $marketKey, 'key' => $key, 'value' => $value]; } public function __get($property) { $value = null; if( isset($this->data[$property]) ) { $value = $this->data[$property]; } return $value; } ... Create Configuration Command
  • 31. <?php namespace Domain; interface CommandBus { public function execute(Command $command); } <?php namespace AppBundleApplicationCommandBus; class CommandBus implements DomainCommandBus { private $container; private $eventDispatcher; private $inflector; private $applicationEvent; public function __construct(ContainerInterface $container, CommandNameInflector $inflector) { $this->container = $container; $this->inflector = $inflector; $this->eventDispatcher = $container->get('event_dispatcher'); $this->applicationEvent = new ApplicationEvent(); } public function execute(DomainCommand $command) { try { $this->applicationEvent->setCommand($command); $response = $this->getHandler($command)->handle($command); $this->eventDispatcher->dispatch($command->eventName(), $this->applicationEvent); } catch (Exception $exception) { $this->applicationEvent->setException($exception); $this->eventDispatcher->dispatch($command->eventNameError(), $this- >applicationEvent); throw $exception; } return $response; Command Bus
  • 32. <?php namespace Domain; interface Handler { public function handle(Command $command); } <?php namespace DomainCore; class CreateConfigurationHandler implements Handler { private $configurationRepository; private $market; public function __construct(Market $market) { $this->market = $market; } public function handle(Command $command) { return $this->save($command); } protected function save(Command $command) { $configuration = $command->configurationEntity(); if (!$configuration instanceof Configuration) { throw new DomainException("Invalid configuration"); } $configuration->setMarket($this->market); $configuration->setKey($command->key); $configuration->setValue($command->value); $this->configurationRepository->add($configuration); return $configuration->getId(); } ... Command Handler
  • 33. <?php namespace AppBundleApplicationControllerWeb; class SystemController extends Controller { /** * @Route("/system/configuration", name="configuration_list") * @param Request $request * @return SymfonyComponentHttpFoundationResponse */ public function configurationAction(Request $request) { $form = $this->createFormBuilder([])->add('key', 'text')->add('value', 'textarea')->getForm(); if ($request->isMethod('POST')) { $form->handleRequest($request); $data = $form->getData(); try { $createConfigurationCommand = new CreateConfigurationCommand($this->getUser()->getMarket()->getKeyName(), $data['key'], $data['value']); $this->get("command_bus")->execute($createConfigurationCommand); $flashMsg = "Chave gravada."; $flashMsgType = "success"; } catch (DomainException $e) { $flashMsg = $e->getMessage(); $flashMsgType = "warning"; } catch (Exception $e) { $flashMsg = "Erro ao inserir a chave de configuração."; $flashMsgType = "warning"; } $this->addFlash($flashMsgType , $flashMsg); } $viewVars['form'] = $form->createView(); return $this->render('web/system/configuration.html.twig', $viewVars); } Create Config Usage | Browser
  • 34. <?php namespace AppBundleApplicationApiv1Controller; class SystemController extends ApiController implements TokenAuthentication { use JsonValidator; public function configurationCreate() { $request = $this->get('request'); $marketKey = $request->headers->get('key'); $requestContent = json_decode($$request->getContent()); $jsonResponse = new JsonResponse(); try { if (!$this->isValidJson($this->loadConfigurationCreateSchema(), $requestContent)) { throw new HttpException(400, $this->getJsonErrors()); } $createConfigurationCommand = new CreateConfigurationCommand($marketKey, $requestContent->key, $requestContent->value); $this->get("command_bus")>execute($createConfigurationCommand); $jsonResponse->setStatusCode(204); } catch (DomainException $exception) { $contentError['description'] = $exception->getMessage(); $jsonResponse->setStatusCode(400); $jsonResponse->setData($contentError); } catch (Exception $exception) { $contentError['description'] = $exception->getMessage(); $jsonResponse->setStatusCode(500); $jsonResponse->setData($contentError); } return $jsonResponse; } ... Create Config Usage | API
  • 36. Thanks! Any questions? You can find me at: ◇ @pv_fusion ◇ pv.gomes89@gmail.com
  • 37. Credits Special thanks to all the people who made and released these awesome resources for free: ◇ Contents of this presentation Paulo Victor Gomes ◇ Presentation template by SlidesCarnival ◇ Photographs by Unsplash References ❖ BROOKS, FREDERICK. The Design of Design: Essays from a Computer Scientist. ❖ BUENOSVINOS, CARLOS. SORONELLA, CHRISTIAN. AKBARY, KEYVAN. Domain Driven Design in PHP. ❖ COCKBURN, ALISTAIR. Hexagonal Architecture. ❖ VERNON, VAUGHN - Implementing Domain-Driven Design. ❖ EVANS, ERICK. Domain Driven Design: Tackling Complexity in the Heart of Software.