SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
-
Upload
alessandro-nadalin -
Category
Technology
-
view
6.154 -
download
2
description
Transcript of SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
A software design based on discrete software components, "services", that collectively provide the functionalities of the larger
software application
You typically start with the infamous PHPapp, hopefully built with Symfony2
which does everything on its own
And soon you also decide,to improve performances,
that your frontend should have its ownin-memory persistence, to be faster
and you put it into another service
And eventually, your lead architectwill come up and tell youthat your Java-based chat
sucks and should bereplaced with...
In human-understandable words, SOA is a software design which embraces splitting a monolithic, totalitarian software
architecture into smaller pieces, thus making them independent, loosely coupled and more maintainable
https://github.com/fabpot/Goutte
https://github.com/guzzle/guzzle
https://github.com/kriswallsmith/buzz
class UsersController{ public function getUsersAction() { return $this->get(‘storage’)->getUsers(); }}
class UsersController{ public function getUsersAction() { return $this->get(‘storage’)->getUsers(); }}
class UsersController{ public function getUsersAction() { return $this->get(‘storage’)->getUsers(); }}
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
/** * @ExclusionPolicy("all") */class Customer implements UserInterface, EquatableInterface{ /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses;
/** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
old_sound_rabbit_mq: connections: default: host: 'localhost' port: 5672 user: 'guest' password: 'guest' vhost: '/' lazy: false producers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} consumers: user_registration: connection: default exchange_options: {name: 'userreg', type: direct} queue_options: {name: 'userreg'} callback: mailer
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
class RedisStorage{ public function getRepository($name) { $this->hash = $name;
return $this; }
public function findActiveOnes() { $results = $this->redis->hget($this->hash); return array_filter($results, function($r){ return $r[‘active’] == true; }); }}
But you choosewhat perfectly fits
each service:your transactionsover a RDBMS andyour communityover a graph DB
JOSEhttp://www.thread-safe.com/2012/03/json-object-signing-and-encryption-jose.html
1. The user enters the credentials once in your frontend
JS APP
AUTHSERVICE
2. The JS app will forward themto your Auth webservice
3. The Auth webservice will then generate the encryptedJWS and set a cookie withits value
JS APP
4. The JS app can now just execute calls usingthat cookie
AUTHSERVICE
3. The Auth webservice will then generate the encrypted JWS and set a cookie with its value
1. The user enters the credentials once in your frontend
JS APP
AUTHSERVICE
2. The JS app will forward themto your Auth webservice
3. The Auth webservice will then generate the encryptedJWS and set a cookie withits value
JS APP
4. The JS app can now just execute calls usingthat cookie
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString());
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString());
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString());
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString(), ...);
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString());
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Namshi\JOSE\JWS;
$jws = new JWS('RS256');$jws->setPayload(array( 'uid' => $user->getid(),));
$privateKey = openssl_get_privatekey("file://path/to/private.key");$jws->sign($privateKey);setcookie('identity', $jws->getTokenString());
use Namshi\JOSE\JWS;
$jws = JWS::load($_COOKIE['identity']);$public_key = openssl_pkey_get_public("/path/to/public.key");
if ($jws->verify($public_key)) { echo "EUREKA!;}
use Symfony\Component\Security\...\AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterface{ ...
public function authenticate(TokenInterface $token) { $key = openssl_pkey_get_public($this->publicKeyPath); $jws = $token->getJws(); if ($key && $jws->isValid($key)) { $token->setUser(User::fromArray($jws->getPayload()));
return $token; }
throw new AuthenticationException('authentication failed.'); }
...}
use Symfony\Component\Security\...\AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterface{ ...
public function authenticate(TokenInterface $token) { $key = openssl_pkey_get_public($this->publicKeyPath); $jws = $token->getJws(); if ($key && $jws->isValid($key)) { $token->setUser(User::fromArray($jws->getPayload()));
return $token; }
throw new AuthenticationException('authentication failed.'); }
...}
use Symfony\Component\Security\...\AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterface{ ...
public function authenticate(TokenInterface $token) { $key = openssl_pkey_get_public($this->publicKeyPath); $jws = $token->getJws(); if ($key && $jws->isValid($key)) { $token->setUser(User::fromArray($jws->getPayload()));
return $token; }
throw new AuthenticationException('authentication failed.'); }
...}
use Symfony\Component\Security\...\AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterface{ ...
public function authenticate(TokenInterface $token) { $key = openssl_pkey_get_public($this->publicKeyPath); $jws = $token->getJws(); if ($key && $jws->isValid($key)) { $token->setUser(User::fromArray($jws->getPayload()));
return $token; }
throw new AuthenticationException('authentication failed.'); }
...}
use Symfony\Component\Security\...\AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterface{ ...
public function authenticate(TokenInterface $token) { $key = openssl_pkey_get_public($this->publicKeyPath); $jws = $token->getJws(); if ($key && $jws->isValid($key)) { $token->setUser(User::fromArray($jws->getPayload()));
return $token; }
throw new AuthenticationException('authentication failed.'); }
...}
SUPERVISEhttp://cr.yp.to/daemontools/supervise.html
http://odino.org/why-we-choose-symfony2-over-any-other-php-framework/
By being decoupled and HTTP-centricSymfony2 has turned into anideal application framework
that can take (part of) the stage in a SOA
you have the freedom of changing or replacing serviceswithout the hassle of touching an entire system
SoC happens at architectural, not application, level and you can perform large-scale refactorings without the fear of destroying the entire system
Image credits
http://www.flickr.com/photos/randystiefer/6998037429/sizes/h/in/photostream/http://www.flickr.com/photos/55432818@N02/5500963965/
http://www.flickr.com/photos/pamhule/4503305775/http://www.flickr.com/photos/wili/1427890704/
http://www.flickr.com/photos/nickpiggott/5212959770/sizes/l/in/photostream/http://www.flickr.com/photos/nomad9491/2549965427/sizes/l/in/photostream/
http://www.flickr.com/photos/amyvdh/95764607/sizes/l/in/photostream/http://www.flickr.com/photos/matthoult/4524176654/
http://www.flickr.com/photos/kittyeden/2416355396/sizes/l/in/photostream/http://www.flickr.com/photos/jpverkamp/3078094381/
http://www.flickr.com/photos/madpoet_one/5554416836/http://www.flickr.com/photos/87792096@N00/2732978107/
http://www.flickr.com/photos/petriv/4787037035/http://www.flickr.com/photos/51035796522@N01/111091247/sizes/l/in/photostream/
http://www.flickr.com/photos/m-i-k-e/6366787693/sizes/l/in/photostream/http://www.flickr.com/photos/39065466@N04/9111005211/
http://www.flickr.com/photos/marchorowitz/5449945176/sizes/l/in/photolist-9iAoQ1-8s4ueH-bCWef9-bCWdPh-e48XUm-bu67nh-a7xaEr-8wLiNh-9aYU1k-9F4VUN-dYqzr1-9vosHb-8BtFuw-8P3h2e-9tqc6M-82qpt4-7UgkBJ-dgSnfS-aJiubZ-9Xji2U-9UVpkC-
7BSh7Y-8GE54k-91GHtB-8VMHJ2-8wiwvo-aCmPCg-925Tg8-bcBv9T-dGUseY/http://www.flickr.com/photos/blegg/745322703/sizes/l/in/photostream/
http://www.flickr.com/photos/centralasian/4649550142/sizes/l/in/photostream/http://www.flickr.com/photos/pennstatelive/4947279459/sizes/l/in/photostream/
http://www.flickr.com/photos/tjblackwell/7819341478/http://www.flickr.com/photos/brainbitch/6066375386/
http://www.flickr.com/photos/nnova/4215594009/http://www.flickr.com/photos/publicenergy/2246574379/
http://www.flickr.com/photos/andrewteman/4592833017/sizes/o/in/photostream/http://www.flickr.com/photos/beautifulrevelry/8548004964/sizes/o/in/photostream/
http://www.flickr.com/photos/denaldo/5066810104/sizes/l/in/photostream/http://www.flickr.com/photos/picturewendy/8365723674/sizes/l/in/photostream/
http://www.flickr.com/photos/danielygo/6644679037/sizes/l/in/photostream/http://www.flickr.com/photos/ross/7614352/sizes/l/in/photostream/
http://www.flickr.com/photos/75932013@N02/6874087329/sizes/l/in/photostream/http://crucifixjel.deviantart.com/art/300-Wallpaper-03-66516887
https://www.flickr.com/photos/acidsaturation/6635987033/sizes/l/