0
SOA with Symfony2
Alessandro Nadalin - Montreal, February 2014
This talk is for those...
Stuck with the legacy
dealing with CRONs
in the need of a solid foundation
rely on web services
need a pluggable software architecture
who love @fabpot
SOA
1. SO WHAT?
(A)
A software design based on discrete software
components, "services", that collectively
provide the functionalities of the ...
You typically start with the infamous PHP
app, hopefully built with Symfony2
which does everything on its own
Then you realize that to provide
a chat system to your users
PHP might not be the best...
And soon you also decide,
to improve performances,
that your frontend should have its own
in-memory persistence, to be fas...
Then, as always...
SCALE.
And eventually, your lead architect
will come up and tell you
that your Java-based chat
sucks and should be
replaced with....
NODEJS
In human-understandable words, SOA is a software design which
embraces splitting a monolithic, totalitarian software
archi...
Ok, but in the real world?
2. Your (Symfony2) app
is just a piece of
the puzzle
How does communication (usually) happen?
WEBSERVICES
Services can request data to other services,
usually through WSs
POX
https://github.com/kriswallsmith/buzz
https://github.com/fabpot/Goutte
https://github.com/guzzle/guzzle
SOAP
PHP and SOAP in 2014
http://www.whitewashing.de/2014/01/31/soap_and_php_in_2014.html
HTTP
REST
FosREST
https://github.com/FriendsOfSymfony/FOSRestBundle
# app/config/routing.yml
users:
type:
rest
resource: MyBundleUsersController
GET /users
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();
}
}
View?
SERIALIZE ALL
THE THINGS!
JMSSerializer
Bundle: https://github.com/schmittjoh/JMSSerializerBundle
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
/**
* @ExclusionPolicy("all")
*/
class Customer implements UserInterface, EquatableInterface
{
/**
* @Expose
* @Serialized...
EVENTS
services notify the architecture that an event has happened
asynchronous messaging queues
RabbitMQ
https://github.com/videlalvaro/rabbitmqbundle
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
mailer:
class: "MyMailer"
arguments: [..., ...]
$this->get('old_sound_rabbit_mq.user_registration_producer')
->publish(serialize($msg));
php app/console rabbitmq:consumer user_registration
old_sound_rabbit_mq:
connections:
default:
host:
'localhost'
port:
5672
user:
'guest'
password: 'guest'
vhost:
'/'
lazy:
f...
class Mailer
{
…
public function execute(AMQPMessage $message)
{
// do stuff
}
}
class Mailer
{
…
public function execute(AMQPMessage $message)
{
// do stuff
}
}
2. Free data
CONSIDER ELIMINATING FK CONSTRAINTS
A service might need to handle data with
another DBMS, so FKs are virtually impossible
ABSTRACT THE DATA
You might think in "rows" but the architecture
thinks in "resources"
$this->get(‘doctrine’)
->getRepository(‘My:Entity’)
->findActiveOnes()
$this->get(‘doctrine’)
->getRepository(‘My:Entity’)
->findActiveOnes()
$this->get(‘storage’)
->getRepository(‘My:Entity’)
->findActiveOnes()
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
class RedisStorage
{
public function getRepository($name)
{
$this->hash = $name;
return $this;
}
public function findActiv...
REPOSITORIES NEED
INTERFACES
ENTITIES NEED
INTERFACES
forget managers, you
need collections
implements StockStorageInterface
use StockStorageInterface as Storage;
class RedisStorage implements Storage
{
...
use StockStorageInterface as Storage;
class RedisStorage implements Storage
{
...
use StockStorageInterface as Storage;
class StockStorage implements Storage
{
...
collections as a service
stock:
class: “MyNamespaceStockStorage”
arguments:
…
…

$this->get(‘stock’)->findActiveOnes();
stock:
class: “MyNamespaceStockStorage”
arguments:
…
…

$this->get(‘stock’)->findActiveOnes();
stock:
class: “MyNamespaceStockStorage”
arguments:
…
…

$this->get(‘stock’)->findActiveOnes();
No more FKs and
the ability of
JOINing to retrieve
some related data
But you choose
what perfectly fits
each service:
your transactions
over a RDBMS and
your community
over a graph DB
So complicated!
Have fun returning
serialized collections
over HTTPS in
~50ms with Doctrine!
3. Standardize
EVERY DEVELOPER NEEDS
THE ENTIRE ARCHITECTURE ON HIS MACHINE
The architecture needs
to be installed in
~1 hour
Setting up VMs
is an hassle and
they are so slow!
go #vagrant
But Vagrant is
still suboptimal:
provisioning and
system resources
are still a pain!
4. Identity
Centralized authentication = identity service
OAuth
OpenID
JWS
JSON WEB SIGNATURE
JSON WEB TOKEN
JSON WEB SIGNATURE
JAVASCRIPT OBJECT
SIGNING & ENCRYPTION
JOSE
http://www.thread-safe.com/2012/03/json-object-signing-and-encryption-jose.html
1. The user enters the
credentials once in your
frontend

2. The JS app will forward them
to your Auth webservice
JS APP

...
1. The user enters the credentials
once in your frontend

JS APP
2. The JS app will forward them
to your Auth webservice

JS APP
AUTH
SERVICE
AUTH
SERVICE
3. The Auth webservice will then generate the
encrypted JWS and set a cookie with its value
AUTH
SERVICE

JS APP

4. The JS app can now just execute
calls using that cookie
1. The user enters the
credentials once in your
frontend

2. The JS app will forward them
to your Auth webservice
JS APP

...
setcookie($name, $jws,$ttl, $path, $domain, true);
setcookie($name, $jws,$ttl, $path, $domain, true);

HTTPS
JWS in PHP?
namshi/jose
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
use NamshiJOSEJWS;
$jws = new JWS('RS256');
$jws->setPayload(array(
'uid' => $user->getid(),
));
$privateKey = openssl_get...
...what about Symfony2?
use SymfonyComponentSecurity...AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterfa...
use SymfonyComponentSecurity...AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterfa...
use SymfonyComponentSecurity...AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterfa...
use SymfonyComponentSecurity...AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterfa...
use SymfonyComponentSecurity...AuthenticationProviderInterface;
class JwsProvider implements AuthenticationProviderInterfa...
I can't simply
use the HTTP
basic authentication,
it was so
convenient!
...and flawed.
Modern apps,
modern tech.
4. Embrace
messaging
Don't wait, notify instead
Different services can intercept an even, separately
If one is down, the others keep working
Who cares about milliseconds for notifications?
The human body is the bottleneck
Email?
SMS?
Be reliable
“Daemons are great”
“Daemons are great”
- No PHP developer ever
SUPERVISOR
http://supervisord.org/
SUPERVISE
http://cr.yp.to/daemontools/supervise.html
use python ;-)
It doesn’t matter...
‫اﻟﺤﺮوف اﻟﻌﺮﺑﯿﺔ ‪if you talk‬‬
Rabbit makes everyone talk the same language
chat

sync daemons
Batch processing
frontend

ERP
agony
transcoding

telcom
But I

Symfony2
Tech monogamy
is so ‘90
“given a hammer,
everything
becomes a nail”
One size doesn’t fit all
“But look at Google,
they basically use
python for everything”
“...and C”
“...and C++”
“...and Java”
“...and JavaScript”
“...and Go”
But hey, you say...
they really dislike supporting
multiple platforms
“...and Dart”
But hey, you say...
they really are not into
supporting the secondary platforms
“...and AngularJS”
5. Not always
sunday
Monitor in real time
Native support for Symfony2
Logs are first-class citizens
https://github.com/Seldaek/monolog
Sharp, as much
as possible
I LIED A LOOOOOT
Symfony isn’t even
the main point of this
SOA talk
You can build SOAs
with anything
...or can you?
http://odino.org/why-we-choose-symfony2-over-any-other-php-framework/
By being decoupled and HTTP-centric
Symfony2 has turned into an
ideal application framework
that can take (part of)
the st...
Full-stack is dead
PHP developers are dead
LONG LIVE API ENGINEERS!
All in all...
SOA is complex
like Symfony2
A puzzle with more pieces
like Symfony2
More things to keep in mind
like Symfony2
COMPLEX
IS NOT
COMPLICATED
Loose coupling
every service is independent, not forced to the
constraints of a monolithic block
you have the freedom of changing or replacing services
without the hassle of touching an entire system
State-of-the-art defense against outages
Fault tolerance
if one of the services has an outage, the rest
of the architecture still works
if a service, listening for messages, is down,
the publisher doesn't get stuck
Cleaner architecture
SoC happens at architectural, not application, level and you can perform large-scale
refactorings without the fear of dest...
...yawn...
Alessandro Nadalin
Alessandro Nadalin
@_odino_
Alessandro Nadalin
@_odino_
Namshi | Rocket Internet
Alessandro Nadalin
@_odino_
Namshi | Rocket Internet
VP Technology
Alessandro Nadalin
@_odino_
Namshi | Rocket Internet
VP Technology
odino.org
Thanks!
Alessandro Nadalin
@_odino_
Namshi | Rocket Internet
VP Technology
odino.org
By the way
Wanna join?
We are looking for talented nerds!
We are looking for talented nerds!

frontend engineer
We are looking for talented nerds!

frontend engineer
data engineer
We are looking for talented nerds!

lead frontend engineer
data engineer
Thanks!
Alessandro Nadalin
@_odino_
Namshi | Rocket Internet
VP Technology
odino.org
Image credits
http://www.flickr.com/photos/randystiefer/6998037429/sizes/h/in/photostream/
http://www.flickr.com/photos/55...
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)
Upcoming SlideShare
Loading in...5
×

SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)

10,111

Published on

Symfony2 is one of the de-facto standards for developing enterprise-ready applications in PHP: being a very structured & decoupled framework, it becomes very handy and suitable for building Service Oriented architectures, which require loose coupling and a clean and tested structure: we will see hot to create a Service Oriented Architecture in Symfony2, taking advantage of messaging systems like RabbitMQ, HTTP APIs and Sf2's internals.

Published in: Technology
0 Comments
32 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
10,111
On Slideshare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
111
Comments
0
Likes
32
Embeds 0
No embeds

No notes for slide

Transcript of "SOA with Symfony2 @ ConFoo 2014 in Montreal (CA)"

  1. 1. SOA with Symfony2 Alessandro Nadalin - Montreal, February 2014
  2. 2. This talk is for those...
  3. 3. Stuck with the legacy
  4. 4. dealing with CRONs
  5. 5. in the need of a solid foundation
  6. 6. rely on web services
  7. 7. need a pluggable software architecture
  8. 8. who love @fabpot
  9. 9. SOA
  10. 10. 1. SO WHAT? (A)
  11. 11. A software design based on discrete software components, "services", that collectively provide the functionalities of the larger software application
  12. 12. You typically start with the infamous PHP app, hopefully built with Symfony2 which does everything on its own
  13. 13. Then you realize that to provide a chat system to your users PHP might not be the best...
  14. 14. And soon you also decide, to improve performances, that your frontend should have its own in-memory persistence, to be faster and you put it into another service
  15. 15. Then, as always...
  16. 16. SCALE.
  17. 17. And eventually, your lead architect will come up and tell you that your Java-based chat sucks and should be replaced with...
  18. 18. NODEJS
  19. 19. 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
  20. 20. Ok, but in the real world?
  21. 21. 2. Your (Symfony2) app is just a piece of the puzzle
  22. 22. How does communication (usually) happen?
  23. 23. WEBSERVICES
  24. 24. Services can request data to other services, usually through WSs
  25. 25. POX
  26. 26. https://github.com/kriswallsmith/buzz https://github.com/fabpot/Goutte https://github.com/guzzle/guzzle
  27. 27. SOAP
  28. 28. PHP and SOAP in 2014 http://www.whitewashing.de/2014/01/31/soap_and_php_in_2014.html
  29. 29. HTTP
  30. 30. REST
  31. 31. FosREST https://github.com/FriendsOfSymfony/FOSRestBundle
  32. 32. # app/config/routing.yml users: type: rest resource: MyBundleUsersController
  33. 33. GET /users
  34. 34. class UsersController { public function getUsersAction() { return $this->get(‘storage’)->getUsers(); } }
  35. 35. class UsersController { public function getUsersAction() { return $this->get(‘storage’)->getUsers(); } }
  36. 36. class UsersController { public function getUsersAction() { return $this->get(‘storage’)->getUsers(); } }
  37. 37. View?
  38. 38. SERIALIZE ALL THE THINGS!
  39. 39. JMSSerializer Bundle: https://github.com/schmittjoh/JMSSerializerBundle
  40. 40. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  41. 41. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  42. 42. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  43. 43. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  44. 44. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  45. 45. /** * @ExclusionPolicy("all") */ class Customer implements UserInterface, EquatableInterface { /** * @Expose * @SerializedName("addresses") * @Groups({"Customer:depth:1"}) */ protected $addresses; /** * @Expose * @Groups({ * "Customer:list", * "Customer:detail", * }) */ private $email;
  46. 46. EVENTS
  47. 47. services notify the architecture that an event has happened
  48. 48. asynchronous messaging queues
  49. 49. RabbitMQ https://github.com/videlalvaro/rabbitmqbundle
  50. 50. 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
  51. 51. 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
  52. 52. 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
  53. 53. 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
  54. 54. 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
  55. 55. mailer: class: "MyMailer" arguments: [..., ...]
  56. 56. $this->get('old_sound_rabbit_mq.user_registration_producer') ->publish(serialize($msg));
  57. 57. php app/console rabbitmq:consumer user_registration
  58. 58. 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
  59. 59. class Mailer { … public function execute(AMQPMessage $message) { // do stuff } }
  60. 60. class Mailer { … public function execute(AMQPMessage $message) { // do stuff } }
  61. 61. 2. Free data
  62. 62. CONSIDER ELIMINATING FK CONSTRAINTS
  63. 63. A service might need to handle data with another DBMS, so FKs are virtually impossible
  64. 64. ABSTRACT THE DATA
  65. 65. You might think in "rows" but the architecture thinks in "resources"
  66. 66. $this->get(‘doctrine’) ->getRepository(‘My:Entity’) ->findActiveOnes()
  67. 67. $this->get(‘doctrine’) ->getRepository(‘My:Entity’) ->findActiveOnes()
  68. 68. $this->get(‘storage’) ->getRepository(‘My:Entity’) ->findActiveOnes()
  69. 69. 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; }); } }
  70. 70. 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; }); } }
  71. 71. 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; }); } }
  72. 72. 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; }); } }
  73. 73. 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; }); } }
  74. 74. 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; }); } }
  75. 75. REPOSITORIES NEED INTERFACES
  76. 76. ENTITIES NEED INTERFACES
  77. 77. forget managers, you need collections
  78. 78. implements StockStorageInterface
  79. 79. use StockStorageInterface as Storage; class RedisStorage implements Storage { ...
  80. 80. use StockStorageInterface as Storage; class RedisStorage implements Storage { ...
  81. 81. use StockStorageInterface as Storage; class StockStorage implements Storage { ...
  82. 82. collections as a service
  83. 83. stock: class: “MyNamespaceStockStorage” arguments: … … $this->get(‘stock’)->findActiveOnes();
  84. 84. stock: class: “MyNamespaceStockStorage” arguments: … … $this->get(‘stock’)->findActiveOnes();
  85. 85. stock: class: “MyNamespaceStockStorage” arguments: … … $this->get(‘stock’)->findActiveOnes();
  86. 86. No more FKs and the ability of JOINing to retrieve some related data
  87. 87. But you choose what perfectly fits each service: your transactions over a RDBMS and your community over a graph DB
  88. 88. So complicated!
  89. 89. Have fun returning serialized collections over HTTPS in ~50ms with Doctrine!
  90. 90. 3. Standardize
  91. 91. EVERY DEVELOPER NEEDS THE ENTIRE ARCHITECTURE ON HIS MACHINE
  92. 92. The architecture needs to be installed in ~1 hour
  93. 93. Setting up VMs is an hassle and they are so slow!
  94. 94. go #vagrant
  95. 95. But Vagrant is still suboptimal: provisioning and system resources are still a pain!
  96. 96. 4. Identity
  97. 97. Centralized authentication = identity service
  98. 98. OAuth
  99. 99. OpenID
  100. 100. JWS
  101. 101. JSON WEB SIGNATURE
  102. 102. JSON WEB TOKEN
  103. 103. JSON WEB SIGNATURE
  104. 104. JAVASCRIPT OBJECT SIGNING & ENCRYPTION
  105. 105. JOSE http://www.thread-safe.com/2012/03/json-object-signing-and-encryption-jose.html
  106. 106. 1. The user enters the credentials once in your frontend 2. The JS app will forward them to your Auth webservice JS APP AUTH SERVICE 3. The Auth webservice will then generate the encrypted JWS and set a cookie with its value JS APP 4. The JS app can now just execute calls using that cookie
  107. 107. 1. The user enters the credentials once in your frontend JS APP
  108. 108. 2. The JS app will forward them to your Auth webservice JS APP AUTH SERVICE
  109. 109. AUTH SERVICE 3. The Auth webservice will then generate the encrypted JWS and set a cookie with its value
  110. 110. AUTH SERVICE JS APP 4. The JS app can now just execute calls using that cookie
  111. 111. 1. The user enters the credentials once in your frontend 2. The JS app will forward them to your Auth webservice JS APP AUTH SERVICE 3. The Auth webservice will then generate the encrypted JWS and set a cookie with its value JS APP 4. The JS app can now just execute calls using that cookie
  112. 112. setcookie($name, $jws,$ttl, $path, $domain, true);
  113. 113. setcookie($name, $jws,$ttl, $path, $domain, true); HTTPS
  114. 114. JWS in PHP?
  115. 115. namshi/jose
  116. 116. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  117. 117. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  118. 118. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  119. 119. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  120. 120. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  121. 121. use NamshiJOSEJWS; $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 NamshiJOSEJWS; $jws = JWS::load($_COOKIE['identity']); $public_key = openssl_pkey_get_public("/path/to/public.key"); if ($jws->verify($public_key)) { echo "EUREKA!; }
  122. 122. ...what about Symfony2?
  123. 123. use SymfonyComponentSecurity...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.'); } ... }
  124. 124. use SymfonyComponentSecurity...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.'); } ... }
  125. 125. use SymfonyComponentSecurity...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.'); } ... }
  126. 126. use SymfonyComponentSecurity...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.'); } ... }
  127. 127. use SymfonyComponentSecurity...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.'); } ... }
  128. 128. I can't simply use the HTTP basic authentication, it was so convenient!
  129. 129. ...and flawed. Modern apps, modern tech.
  130. 130. 4. Embrace messaging
  131. 131. Don't wait, notify instead
  132. 132. Different services can intercept an even, separately
  133. 133. If one is down, the others keep working
  134. 134. Who cares about milliseconds for notifications?
  135. 135. The human body is the bottleneck
  136. 136. Email?
  137. 137. SMS?
  138. 138. Be reliable
  139. 139. “Daemons are great”
  140. 140. “Daemons are great” - No PHP developer ever
  141. 141. SUPERVISOR http://supervisord.org/
  142. 142. SUPERVISE http://cr.yp.to/daemontools/supervise.html
  143. 143. use python ;-)
  144. 144. It doesn’t matter...
  145. 145. ‫اﻟﺤﺮوف اﻟﻌﺮﺑﯿﺔ ‪if you talk‬‬
  146. 146. Rabbit makes everyone talk the same language
  147. 147. chat sync daemons Batch processing frontend ERP agony transcoding telcom
  148. 148. But I Symfony2
  149. 149. Tech monogamy is so ‘90 “given a hammer, everything becomes a nail”
  150. 150. One size doesn’t fit all
  151. 151. “But look at Google, they basically use python for everything”
  152. 152. “...and C”
  153. 153. “...and C++”
  154. 154. “...and Java”
  155. 155. “...and JavaScript”
  156. 156. “...and Go”
  157. 157. But hey, you say...
  158. 158. they really dislike supporting multiple platforms
  159. 159. “...and Dart”
  160. 160. But hey, you say...
  161. 161. they really are not into supporting the secondary platforms
  162. 162. “...and AngularJS”
  163. 163. 5. Not always sunday
  164. 164. Monitor in real time
  165. 165. Native support for Symfony2
  166. 166. Logs are first-class citizens
  167. 167. https://github.com/Seldaek/monolog
  168. 168. Sharp, as much as possible
  169. 169. I LIED A LOOOOOT
  170. 170. Symfony isn’t even the main point of this SOA talk
  171. 171. You can build SOAs with anything
  172. 172. ...or can you?
  173. 173. http://odino.org/why-we-choose-symfony2-over-any-other-php-framework/
  174. 174. By being decoupled and HTTP-centric Symfony2 has turned into an ideal application framework that can take (part of) the stage in a SOA
  175. 175. Full-stack is dead
  176. 176. PHP developers are dead
  177. 177. LONG LIVE API ENGINEERS!
  178. 178. All in all...
  179. 179. SOA is complex
  180. 180. like Symfony2
  181. 181. A puzzle with more pieces
  182. 182. like Symfony2
  183. 183. More things to keep in mind
  184. 184. like Symfony2
  185. 185. COMPLEX IS NOT COMPLICATED
  186. 186. Loose coupling
  187. 187. every service is independent, not forced to the constraints of a monolithic block
  188. 188. you have the freedom of changing or replacing services without the hassle of touching an entire system
  189. 189. State-of-the-art defense against outages
  190. 190. Fault tolerance
  191. 191. if one of the services has an outage, the rest of the architecture still works
  192. 192. if a service, listening for messages, is down, the publisher doesn't get stuck
  193. 193. Cleaner architecture
  194. 194. SoC happens at architectural, not application, level and you can perform large-scale refactorings without the fear of destroying the entire system
  195. 195. ...yawn...
  196. 196. Alessandro Nadalin
  197. 197. Alessandro Nadalin @_odino_
  198. 198. Alessandro Nadalin @_odino_ Namshi | Rocket Internet
  199. 199. Alessandro Nadalin @_odino_ Namshi | Rocket Internet VP Technology
  200. 200. Alessandro Nadalin @_odino_ Namshi | Rocket Internet VP Technology odino.org
  201. 201. Thanks! Alessandro Nadalin @_odino_ Namshi | Rocket Internet VP Technology odino.org
  202. 202. By the way
  203. 203. Wanna join?
  204. 204. We are looking for talented nerds!
  205. 205. We are looking for talented nerds! frontend engineer
  206. 206. We are looking for talented nerds! frontend engineer data engineer
  207. 207. We are looking for talented nerds! lead frontend engineer data engineer
  208. 208. Thanks! Alessandro Nadalin @_odino_ Namshi | Rocket Internet VP Technology odino.org
  209. 209. 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-e48XUmbu67nh-a7xaEr-8wLiNh-9aYU1k-9F4VUN-dYqzr1-9vosHb-8BtFuw-8P3h2e-9tqc6M-82qpt4-7UgkBJ-dgSnfS-aJiubZ-9Xji2U-9UVpkC7BSh7Y-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/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×