hexagonal symfony

53
Hexagonal Symfony Marcello Duarte @_md Sensio Labs

Upload: marcello-duarte

Post on 08-Sep-2014

4.764 views

Category:

Technology


3 download

DESCRIPTION

What would your application look like if it were written by the people who write the testing frameworks? If unit tests make classes more modular, by forcing you to test it in isolation, then what is the effect of expanding this to a less granular level, the acceptance and functional test. The more modern application architecture evolves, the more we hear the very old patterns being rediscovered and re-adopted. 1979 Trygve's MVC is a classic example, so are the SOLID principles. In this talk we will look on how Symfony allows for a really decoupled, easy to test application, by following on the footsteps of Alistair Cockburn's hexagonal architecture.

TRANSCRIPT

Page 1: Hexagonal symfony

Hexagonal SymfonyMarcello Duarte

@_md SensioLabs

Page 2: Hexagonal symfony

@_md SensioLabs

About me

Marcello DuarteSensioLabs

UK

@_md

work

contribute

tweet

Page 3: Hexagonal symfony

@_md SensioLabs

Why do we care about maintainability?

Page 4: Hexagonal symfony

@_md SensioLabs

Convenience vs Maintainability

Page 5: Hexagonal symfony

@_md SensioLabs

Maintainability is not a thing for the future

Page 6: Hexagonal symfony

@_md SensioLabs

“We cannot manage what we cannot measure”

Page 7: Hexagonal symfony

@_md SensioLabs

“We cannot manage what we cannot measure”

Page 8: Hexagonal symfony

@_md SensioLabs

We cannot manage what we cannot

change

Page 9: Hexagonal symfony

@_md SensioLabs

“A framework is ‘just’ one of the tools to help you develop better and faster” – Symfony documentation

Page 10: Hexagonal symfony

@_md SensioLabs

Faster is not enough

Page 11: Hexagonal symfony

@_md SensioLabs

Growable http

://up

load

.wik

imed

ia.o

rg/w

ikip

edia

/com

mon

s/3/

39/D

omes

tic_G

oose

.jpg

Page 12: Hexagonal symfony

@_md SensioLabs

“Key in making great and growable systems is to

design how its modules communicate

[and not] what their properties and behaviours should be.”

View

poin

ts R

esea

rch

Inst

itute

Sou

rce

- Bon

nie

Mac

bird

UR

L -h

ttp://

ww

w.vp

ri.or

g

Page 13: Hexagonal symfony

@_md SensioLabs

real procedure Sum (k, l, u, ak) value l, u; integer k, l, u; real ak; begin real S; S := 0; for k := l step 1 until u do S := S + ak; Sum := S; end; x := Sum( i, 1, n, V[i] );

Page 14: Hexagonal symfony

@_md SensioLabs

From Block Structure to OO

Page 15: Hexagonal symfony

@_md SensioLabs

Glyph Class Char (c); Character c; Begin Procedure print; OutChar(c); End;

Page 16: Hexagonal symfony

@_md SensioLabs

Module A Module B

flow of control? source code dependency?

Page 17: Hexagonal symfony

@_md SensioLabs

controller

use case

utility

uses

uses

Naïve Implementation

Page 18: Hexagonal symfony

@_md SensioLabs

!public function updateAction($id) { $em = $this->getDoctrine()->getManager(); $product = $em->getRepository('AcmeStoreBundle:Product')->find($id); ! if (!$product) { throw $this->createNotFoundException( 'No product found for id '.$id ); } ! $product->setName('New product name!'); $em->flush(); ! return $this->redirect($this->generateUrl('homepage')); }

Page 19: Hexagonal symfony

@_md SensioLabs

!public{ $em $product ! ! $product $em! }

Doctrine

RedirectModel

Router

Page 20: Hexagonal symfony

@_md SensioLabs

Module A Polymorphic

Dependency Inversion Principle

Module B

[Martin 02]

Page 21: Hexagonal symfony

@_md SensioLabs

use case

service adapter

utility adapter

service

utility

Page 22: Hexagonal symfony

@_md SensioLabs

Layered Architecture

User Interface

Application

Domain

Infrastructure

[Evans 04]

Page 23: Hexagonal symfony

@_md SensioLabs

Page 24: Hexagonal symfony

@_md SensioLabs

“The hexagon is not a hexagon because the number six is important, but rather to allow the people doing the drawing

to have room to insert ports and adapters as they need”

:)

[Cockburn 08]

Page 25: Hexagonal symfony

@_md SensioLabshttp://www.flickr.com/photos/10849858@N00/2696404813

Ports are the interfaces that describe the relationship of your application with the outside world

Page 26: Hexagonal symfony

@_md SensioLabshttp://www.flickr.com/photos/mac_users_guide/3680586328/

Page 27: Hexagonal symfony

@_md SensioLabs

Hexagonal Architecture

your application

Page 28: Hexagonal symfony

@_md SensioLabs

web gui app

your application

user side ports

test adapter

Page 29: Hexagonal symfony

@_md SensioLabs

web gui app

your application

user side ports

test adapter

data side ports

db access

in memory

db

Page 30: Hexagonal symfony

@_md SensioLabs

Dbrest

Logs

Page 31: Hexagonal symfony

@_md SensioLabs

“Architecture is about intent”

[Martin 02]

Page 32: Hexagonal symfony
Page 33: Hexagonal symfony

START WRITING A UNIT TEST FOR THE ENTRY POINT,

BUT INSTEAD OF IMMEDIATELY TRYING TO SOLVE THE PROBLEM, INTENTIONALLY

DEFER WRITING ANY IMPLEMENTATION LOGIC! INSTEAD, BREAK DOWN THE

PROBLEM BY DREAMING UP ALL OF THE OBJECTS YOU WISH YOU HAD

Just

in S

earl

s

Page 34: Hexagonal symfony

KNOW IN ADVANCE WHERE YOU ARE GOING TO PUT CERTAIN

BEHAVIOURS. […] IN SHORT, BEFORE YOU WRITE YOUR FIRST TEST,

YOU HAVE TO "DREAM UP THE [BOUNDARIES] THAT YOU WISH YOU

HAD".U

ncle

Beb

Page 35: Hexagonal symfony

@_md SensioLabs

Dream the feature

Page 36: Hexagonal symfony

@_md SensioLabs

Page 37: Hexagonal symfony

@_md SensioLabs

Dream the boundaries

Page 38: Hexagonal symfony

@_md SensioLabs

L&D app !

!

Learning Domain

Google Auth Employees

API

Twitter bootstrap

Page 39: Hexagonal symfony

@_md SensioLabs

Express intent

Page 40: Hexagonal symfony

@_md SensioLabs

<—— Domain<—— Application<—— Delivery Port<—— Persistency Port

Page 41: Hexagonal symfony

@_md SensioLabs

Context also have boundaries

Page 42: Hexagonal symfony

@_md SensioLabs

Page 43: Hexagonal symfony

@_md SensioLabs

Controllers depend on use cases

Page 44: Hexagonal symfony

@_md SensioLabs

/** * @Route(service="controllers.skills") */ class SkillsController { private $competencyFinder; private $raterFinder; ! public function __construct(CompetencyFinder $competencyFinder) { $this->competencyFinder = $competencyFinder; } ! /** * @Route("/skills", name="skills") * @Template() * Show the skill for the learners role */ public function indexAction() { return ['competencies' => $this->competencyFinder->findDictionary()]; } }

Page 45: Hexagonal symfony

@_md SensioLabs

Use cases depend on the domain services and ports

Page 46: Hexagonal symfony

@_md SensioLabs

<?php !namespace Inviqa\TeamUp\Skills; !use Inviqa\Learning\Learner; use Inviqa\Learning\Skills\CompetencyDictionaryFinder; !class CompetencyFinder { private $learner; ! private $finder; ! public function __construct(Learner $learner, CompetencyDictionaryFinder $finder) { $this->learner = $learner; $this->finder = $finder; } ! public function findDictionary() { return $this->finder->findByLearnerRole($this->learner->getLearnerRole()); } }

Page 47: Hexagonal symfony

@_md SensioLabs

<?php !namespace Inviqa\Learning\Skills; !use Inviqa\Learning\LearnerRole; !interface CompetencyDictionaryFinder { /** * Gets the dictionary for a particular role * * @param LearnerRole $role * @return \Inviqa\Learning\CompetencyDictionary */ public function findByLearnerRole(LearnerRole $role); }

Page 48: Hexagonal symfony

@_md SensioLabs

Leverage Symfony Event Dispatcher Keep Controller stuff in the controller

Page 49: Hexagonal symfony

@_md SensioLabs

public function onSuccess(Event $event) { $this->flashBag->add('success', 'Project has been created.'); } ! public function onFailure(Event $event) { $this->flashBag->add( 'failure', 'Failed to create project.' . PHP_EOL . $event->get(‘reason') ); }

Page 50: Hexagonal symfony

@_md SensioLabs

And make you controller a listener of your use case

Page 51: Hexagonal symfony

@_md SensioLabs

github.com/MarcelloDuarte/hexagonal-symfony

Page 52: Hexagonal symfony

@_md SensioLabs

Thanks!

Page 53: Hexagonal symfony

@_md SensioLabs

Questions?

joind.in/10781 github.com/MarcelloDuarte/hexagonal-symfony