symfony in microservice architecture

Post on 07-Jan-2017

10.317 Views

Category:

Software

5 Downloads

Preview:

Click to see full reader

TRANSCRIPT

SYMFONYSYMFONYCOMPONENTS INCOMPONENTS INMICROSERVICESMICROSERVICESARCHITECTUREARCHITECTURE

ABOUT MEABOUT ME

Daniele D'Angeli@dangelidanielehttp://danieledangeli.com

http://www.sainsburys.co.uk/London

Born and raised in Rome

WHAT WE'RE DOINGWHAT WE'RE DOING

A "brownfield" project 8 "micro" service so farAPI gateway written in GoLangPython, PHP, Symfony, Django, GoLang

TECHNOLOGIESTECHNOLOGIES

... LET ME START... LET ME STARTFROMFROM

CONCLUSIONSCONCLUSIONS

IS SYMFONY SUITABLE FORA MICROSERVICESARCHITECTURE?

UNDER CERTAINUNDER CERTAIN

CONDITIONSCONDITIONS

YESYES

.What they are

.Drawbacks

.When Symfony can fail?

.When Symfony can success?

MICROSERVICESMICROSERVICES

WHAT THEY AREWHAT THEY ARE

BUZZWORD WINNINGBUZZWORD WINNINGAWARD 2015AWARD 2015

...EMERGED FROM...EMERGED FROM

#CONTINUOS#CONTINUOSDELIVERYDELIVERY

#DOMAIN DRIVEN#DOMAIN DRIVENDESIGNDESIGN

#ON DEMAND#ON DEMANDVIRTUALIZATIONVIRTUALIZATION

#INFRASTRACTURE#INFRASTRACTUREAUTOMATIONAUTOMATION

SMALL, AUTONOMOUSSMALL, AUTONOMOUSSERVICES THAT WORKSERVICES THAT WORK

TOGETHERTOGETHER

small and focused on doing one thing well

"Gather together those things that change for the same reason,

and separate those things that change for different reasons"

Robert C. Martin

HOW SMALL IS SMALL?HOW SMALL IS SMALL?

AUTONOMOUSAUTONOMOUS

Each microservice can be able to change independentlywithout requiring consumer to change

IDEALLY!!!IDEALLY!!!

DRAWBACKSDRAWBACKS

TESTINGTESTING

REPRODUCE THE ENTIREREPRODUCE THE ENTIRESTACK ON A LOCAL ENVSTACK ON A LOCAL ENV

WHEN SYMFONY CANWHEN SYMFONY CANFAILFAIL

SYMFONY IS NOT ASYMFONY IS NOT AMICROMICRO FRAMEWORK FRAMEWORK

IT CAN BEIT CAN BECONSIDERED MORECONSIDERED MORE

A A "FULL-STACK""FULL-STACK"FRAMEWORKFRAMEWORK

...IF THEY ARE SO...IF THEY ARE SOSMALLSMALL

TOO MANY TOOLS INTOO MANY TOOLS INOUR "FRAMEWORK" OUR "FRAMEWORK"

SYMFONY IS HARDSYMFONY IS HARDTO LEARNTO LEARN

MICROSERVICESMICROSERVICES

====

ETEREGENOUS TEAMETEREGENOUS TEAM

WHAT ABOUT IFWHAT ABOUT IFYOU ASK TO AYOU ASK TO APYTHONIST TOPYTHONIST TO

WORK WITHWORK WITHSYMFONY?SYMFONY?

TOO MANY CONFIGURATIONTOO MANY CONFIGURATIONFILESFILES

WHEN SYMFONY CANWHEN SYMFONY CANSUCCESS?SUCCESS?

...THEY ARE SO...THEY ARE SOSMALL?SMALL?

THERE ARE A LOT OFTHERE ARE A LOT OFEXAMPLES WHEREEXAMPLES WHERE

COMPANIES FAILEDCOMPANIES FAILEDWITH MICROSERVICESWITH MICROSERVICES

IT'S NOT ABOUTIT'S NOT ABOUTSYMFONY, OR OTHERSYMFONY, OR OTHERMVC FRAMEWORKS...MVC FRAMEWORKS...

IT'S ABOUTIT'S ABOUTPREMATUREPREMATURE

DECOMPOSITIONDECOMPOSITION

HTTPS://RCLAYTON.SILVRBACK.COM/FAILING-HTTPS://RCLAYTON.SILVRBACK.COM/FAILING-AT-MICROSERVICESAT-MICROSERVICES

"... ANOTHER LESSON I LEARNED WAS"... ANOTHER LESSON I LEARNED WAS

TO NOT GET TOO GRANULAR WITHTO NOT GET TOO GRANULAR WITH

MICROSERVICES AT THE BEGINNINGMICROSERVICES AT THE BEGINNING

OF A PROJECT"OF A PROJECT"

"... INSTEAD OF DOING WHAT WE DID"... INSTEAD OF DOING WHAT WE DID

(STARTING WITH 8 SERVICES), TRY(STARTING WITH 8 SERVICES), TRY

STARTING WITH TWO OR THREESTARTING WITH TWO OR THREE

SERVICES OF LOGICALLY RELATEDSERVICES OF LOGICALLY RELATED

FUNCTIONALITY (THEY WON'T BEFUNCTIONALITY (THEY WON'T BE

MICRO, HOWEVER)"MICRO, HOWEVER)"

HOW FACINGHOW FACINGPREMATUREPREMATURE

DECOMPOSITION?DECOMPOSITION?

FIRST APPROACHFIRST APPROACHMONOLITH FIRSTMONOLITH FIRST

http://martinfowler.com/bliki/MonolithFirst.html

RealityHope vs.

http://martinfowler.com/articles/dont-start-monolith.html

DOMAIN DRIVENDOMAIN DRIVENDESIGN PRINCIPLESDESIGN PRINCIPLES

ARE USEFULARE USEFUL

ERIC EVANS ERIC EVANS

He talked about how microservices boundary enable DDDhttps://skillsmatter.com/skillscasts/6259-ddd-and-microservices-at-last-some-

bounderies

DDD eXchange 2015

THOUGHWORKSTHOUGHWORKS https://www.thoughtworks.com/insights/blog/domain-driven-design-services-

architecture

Domain Driven Design for Services ArchitectureBounded Contexts Designed as Service Applications

ONEONE"MICRO"SERVICE"MICRO"SERVICE

FOR EACH BOUNDEDFOR EACH BOUNDEDCONTEXTCONTEXT

https://flic.kr/p/3p16o1

IN A NUTSHELLIN A NUTSHELL

LESS MICRO, MORELESS MICRO, MORE"MACRO""MACRO"

WE NEED TO DESIGNWE NEED TO DESIGNOUR APPLICATIONS OUR APPLICATIONS

PROPERLYPROPERLY

IN SUCHIN SUCHSCENARIOS SCENARIOS

SYMFONY CAN HELPSYMFONY CAN HELP...BUT PROBABLY WE...BUT PROBABLY WE

NEED SOMENEED SOMEEXPEDIENTSEXPEDIENTS

A PRATICAL EXAMPLEA PRATICAL EXAMPLE

.Symfony scaffolding

&minimal configuration

.Testing

.How develop the integrations

LET'SLET'SCONSIDER ANCONSIDER ANAPPLICATIONAPPLICATIONLIKE SLACKLIKE SLACK

DISCOVERDISCOVERBOUNDED CONTEXT (HARD)BOUNDED CONTEXT (HARD)

ChannelContext

MessageContext

AuthorizationContext

AuthenticationContext

User management

Context

MESSAGE CONTEXTMESSAGE CONTEXT

MessagesContext

ALLOWS AALLOWS A PUBLISHER PUBLISHERTO PUBLISH AND DELETETO PUBLISH AND DELETEMESSAGGES ON MESSAGGES ON OPENOPEN

CHANNELSCHANNELS

A A PUBLISHERPUBLISHER MUST BE MUST BEAUTHORIZEDAUTHORIZED TO TO

PUBLISH A MESSAGE ONPUBLISH A MESSAGE ONA CHANNELA CHANNEL

SYMFONYSYMFONYSCAFFOLDINGSCAFFOLDINGAND MINIMALAND MINIMAL

CONFIGURATIONCONFIGURATION

AVOID A CLASSICAVOID A CLASSICSYMFONYSYMFONY

SCAFFOLDINGSCAFFOLDING

USE A MINIMALUSE A MINIMALSYMFONYSYMFONY

CONFIGURATIONCONFIGURATION PLUSPLUS

DDD DIRECTORYDDD DIRECTORYSTRUCTURE STRUCTURE

https://github.com/danieledangeli/symfony-microservice-bounded-context-example

Github repository

MINIMAL SYMFONY CONFMINIMAL SYMFONY CONF

Inspired by:

http://www.whitewashing.de/2014/10/26/symfony_all_the_things_web.html

environment variables.env files

only one config.ymlonly one entry point (index.php)

DotEnv

https://github.com/vlucas/phpdotenv

require_once __DIR__ . "/../vendor/autoload.php";require_once __DIR__ . "/../app/AppKernel.php";

use Symfony\Component\HttpFoundation\Request;use Dotenv\Dotenv;

//load environment variables. It doesn't overwrite existing ones$dotenv = new Dotenv(__DIR__ . '/../');$dotenv->load();

$request = Request::createFromGlobals();

$kernel = new AppKernel( $_SERVER['SYMFONY_ENV'], (bool)$_SERVER['SYMFONY_DEBUG']);

$response = $kernel->handle($request);

$response->send();$kernel->terminate($request, $response);

Only one index.php

Only 1 config.yml

public function registerBundles(){ ...}

public function registerContainerConfiguration(LoaderInterface $loader){ $loader->load(__DIR__ . '/config/config.yml');

if (in_array($this->getEnvironment(), array('dev', 'test'))) { $loader->load(function ($container) { $container->loadFromExtension('web_profiler', array( 'toolbar' => true, )); $container->loadFromExtension('framework', array( 'test' => true, )); }); }}

The result:

START TOSTART TOCODINGCODING

TESTINGTESTING

TEST BEHAVIOURTEST BEHAVIOURFIRSTFIRST

MAKE THE VALUE EXPLICITMAKE THE VALUE EXPLICIT(It's important also for other developers)

Feature: message publisher As a Publisher I need to be able to publish a message on a channel ... Scenario: A publisher, if not authorized is not able to publish a new message on a channel Given I am publisher And exists an "open" channel And I'm not authorized to publish messages on that channel When I write the message "Hi guys" Then I'm informed that I'm not authorized And no new messages will be published on that channel

FUNCTIONAL TESTSFUNCTIONAL TESTS

Stub dependencies

http://martinfowler.com/articles/microservice-testing/

PHPUNIT/PHPSPECPHPUNIT/PHPSPECwith or without

MOCKERYMOCKERY

UNIT TESTSUNIT TESTS

MAKE CLEAR THE TESTMAKE CLEAR THE TESTTYPE: TYPE: FUNCTIONAL,FUNCTIONAL, UNIT,UNIT, INTEGRATIONINTEGRATION

PHPUNIT @GROUPSPHPUNIT @GROUPS

HOW TO HOW TO

DEVELOP THEDEVELOP THEINTEGRATIONSINTEGRATIONS

final class Publisher { public function publishOnChannel( Channel $channel, ChannelAuthorization $channelAuthorization, BodyMessage $body ) { if($channelAuthorization->canPublisherPublishOnChannel()) { if (!$channel->isClosed()) { //create message }

throw new ChannelClosedException( sprintf("The channel %s is closed", $channel ); }

throw new PublisherNotAuthorizedException; }}

Channel { id ChannelId isOpen boolean}

ChannelAuthorization { publisherId PublisherId channelId ChannelId isAuthorized boolean}

MESSAGE CONTEXT MODELSMESSAGE CONTEXT MODELS

Publisher { id PublisherId}

sent within theauthenticatedrequest

taken from services integrations (Channel Context, Channel Authorization Context)

namespace MessageContext\Domain\Service\Gateway;

interface ChannelGatewayInterface{ /** * @param ChannelId $channelId * * @return Channel */ public function getChannel(ChannelId $channelId);}

<?php

namespace MessageContext\InfrastructureBundle\Service\Channel;

class ChannelGateway implements ChannelGatewayInterface{ private $channelAdapter;

...

/** * @param ChannelId $channelId * * @return Channel */ public function getChannel(ChannelId $channelId) { return $this->channelAdapter ->toChannel($channelId); }}

namespace MessageContext\InfrastructureBundle\Service\ChannelAuthorization

class ChannelAdapter{ ...

public function toChannel(ChannelId $channelId) { $request = new Request("GET", sprintf("%s/api/channels/%s", $this->channelContextUri, $channelId ));

$request->addHeader("Accept", "application/json"); $response = $this->requestHandler->handle($request);

return $this->channelTranslator->toChannelFromResponse( $response ); }}

class ChannelTranslator{ public function toChannelFromResponse(Response $response) { if (200 === $response->getStatusCode()) { $contentArray = $this->validateAndGetResponseBodyArray($response); return new Channel(new ChannelId($contentArray["id"]), $contentArray["isOpen"]); } .... }

private function validateAndGetResponseBodyArray(Response $response) { $contentArray = $response->getBody();

if (isset($contentArray["id"]) && isset($contentArray["isOpen"])) { return $contentArray; }

.... }}

Only what we need

{ "id": "456t-889-4444", "isOpen": false, "createAt": "26/05/2015", "publisherId": "11111-5555-3333-5555", "name": "a channel name", "spot": "a channel spot", "messagges_count": 450

... ...} The original response

ARE WE MISSINGARE WE MISSINGSOMETHINGSOMETHING

??

<?php

interface ServiceIntegrationInterface{ /** * @param $message * * @throws ServiceNotAvailableException */ public function onServiceNotAvailable($message);

/** * @param $message * * @throws ServiceFailureException */ public function onServiceFailure($message);}

namespace MessageContext\Domain\Service\Gateway;

interface ChannelGatewayInterface extends ServiceIntegrationInterface{ /** * @param ChannelId $channelId * * @throws MicroServiceIntegrationException * @return Channel */ public function getChannel(ChannelId $channelId);}

class ChannelGateway implements ChannelGatewayInterface { ... /** * @param $message * @throws ServiceNotAvailableException */ public function onServiceNotAvailable($message) { throw new ServiceNotAvailableException($message); }

/** * @param $message * @throws ServiceFailureException */ public function onServiceFailure($message) { throw new ServiceFailureException($message); }

ARE WE STILLARE WE STILLMISSINGMISSING

SOMETHINGSOMETHING

??

IF AN INTEGRATIONIF AN INTEGRATIONIS NOT AVAILABLE,IS NOT AVAILABLE,WHY CONTINUINGWHY CONTINUING

TO SEND REQUESTSTO SEND REQUESTSTO IT?TO IT?

CIRCUIT BREAKERCIRCUIT BREAKER

"A CIRCUIT BREAKER IS"A CIRCUIT BREAKER ISUSED TO DETECT FAILURESUSED TO DETECT FAILURESAND ENCAPSULATES LOGICAND ENCAPSULATES LOGICOF PREVENTING A FAILUREOF PREVENTING A FAILURE

TO REOCCURTO REOCCURCONSTANTLY"CONSTANTLY"

HTTPS://GITHUB.COM/EJSMONT-ARTUR/PHP-HTTPS://GITHUB.COM/EJSMONT-ARTUR/PHP-CIRCUIT-BREAKERCIRCUIT-BREAKER

namespace MessageContext\InfrastructureBundle\CircuitBreaker;

class CircuitBreaker implements PostContextCircuitBreakerInterface{ private $circuitBreaker; ...

public function isAvailable($serviceName) { return $this->circuitBreaker->isAvailable($serviceName); }

public function reportSuccess($serviceName) { $this->circuitBreaker->reportSuccess($serviceName); }

public function reportFailure($serviceName) { $this->circuitBreaker->reportFailure($serviceName); }}

public function getChannel(ChannelId $channelId){ if ($this->circuitBreaker->isAvailable($this->serviceName)) { try { $channel = $this->channelAdapter->toChannel($channelId); $this->circuitBreaker->reportSuccess($this->serviceName);

return $channel; } catch (UnableToProcessResponseFromService $e) { $this->handleNotExpectedResponse($e->getResponse()); } }

$this->onServiceNotAvailable("Service not available");}

private function handleNotExpectedResponse(Response $response){ $this->circuitBreaker->reportFailure($this->serviceName);

...}

CONCLUSIONCONCLUSION(AGAIN)(AGAIN)

SYMFONY CAN BE USED INSYMFONY CAN BE USED INSOME MICROSERVICESOME MICROSERVICE

ARCHITECTURES,ARCHITECTURES,ESPECIALLY IF WE STARTESPECIALLY IF WE STARTWITH A MONOLITH FIRSTWITH A MONOLITH FIRST

APPROACHAPPROACH

HOWEVER WE MAY NEED TOHOWEVER WE MAY NEED TOCONSIDER SOMECONSIDER SOME

EXPEDIENTS EXPEDIENTS (env variables, avoid complex tools)

(IT DEPENDS ON THE TEAM!!)(IT DEPENDS ON THE TEAM!!)

MINIMAL INTEGRATIONSMINIMAL INTEGRATIONS&

HANDLING FAILURESHANDLING FAILURES

ARE A GOOD STARTING POINTARE A GOOD STARTING POINTTO FACE THE OVERCOMPLEXITYTO FACE THE OVERCOMPLEXITY

INTRODUCED BYINTRODUCED BYTHIS ARCHITECTURETHIS ARCHITECTURE

WHAT WE HAVEWHAT WE HAVEMISSEDMISSED

OTHER INTEGRATION WAYS:OTHER INTEGRATION WAYS:

SERVICE CHOREOGRAPHY SERVICE CHOREOGRAPHY

OTHER TESTING WAYS:OTHER TESTING WAYS:

CONSUMER DRIVEN CONTRACTCONSUMER DRIVEN CONTRACTTESTSTESTS

QUESTIONS?QUESTIONS?https://www.slideshare.net/danieledangeli10/symfony-in-

microservice-architecture

top related