Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Implementing reliable PHP-based microservices

Implementing reliable PHP-based microservices

Event: PHP London Meetup

Using a microservice architecture is totally possible using PHP. PHP has improved as a language and also as an ecosystem. Let's understand some new concepts and technologies that help us to create well-developed and reliable services.


Junior Grossi

July 02, 2020

More Decks by Junior Grossi

Other Decks in Programming


  1. @junior_grossi IMPLEMENTING RELIABLE PHP-BASED MICROSERVICES or at least, how they

    should be ;-)
  2. @junior_grossi hey, I'm Junior Grossi twitter.com/junior_grossi github.com/jgrossi grossi.dev

  3. @junior_grossi

  4. @junior_grossi https://github.com/corcel/corcel

  5. @junior_grossi PHPMG Conference 2019 21/09/2019 - Belo Horizonte, Brazil https://conf.phpmg.com

  6. @junior_grossi I do love #elephpants https://elephpant.me/herd/junior_grossi

  7. @junior_grossi remote-first company php / golang / java / js

  8. @junior_grossi / 2004 php / 2016 / 2019 relocated to

  9. @junior_grossi ... to this perfect weather! I live in Galway,

    that's a bit worse
  10. @junior_grossi let's talk about microservices

  11. @junior_grossi I WILL BE: sharing what I've learned ~1.5 year

    sharing what I'm learning
  12. @junior_grossi Implementing reliable PHP-based microservices

  13. @junior_grossi Implementing reliable PHP-based microservices

  14. @junior_grossi Implementing reliable PHP-based microservices

  15. @junior_grossi PART 1 Implementing reliable PHP-based microservices

  16. @junior_grossi microservices? again? 1-minute tutorial

  17. @junior_grossi "microservices" is an architectural style it will not solve

    problems you don't have yet! (you may never have those problems)
  18. @junior_grossi what's a monolith?

  19. @junior_grossi

  20. @junior_grossi what's microservice architecture?

  21. @junior_grossi USING MICROSERVICES need clear implementations share implementation issues from

  22. @junior_grossi "don't use microservices if you don't need them"

  23. @junior_grossi PART 2 Implementing reliable PHP-based microservices

  24. @junior_grossi RELIABILITY communication logging responsibility productivity

  25. @junior_grossi COMMUNICATION sync / HTTP, gRPC async / event-oriented

  26. @junior_grossi SYNC COMMUNICATION

  27. @junior_grossi ASYNC COMMUNICATION Frontend API Gateway Queue Service A Service

    B Service C book for a class BOOKING_REQUEST 202 (listening to B_R event) GET /v2/bar {"bar":"123"} BOOKING_CONFIRMED results via websockets if required (listening to B_C event) Send notification
  28. @junior_grossi ASYNC COMMUNICATION a service subscribes to an event use

    the data (payload) to do something // event identifier "BOOKING_REQUEST" // event payload { "userId": "1234567890", "classId": "2345", "classStartDate": "2020­05­14T15:19:21+00:00" }
  29. @junior_grossi RELIABILITY communication logging responsibility productivity

  30. @junior_grossi BUG INVESTIGATION Frontend API Gateway Queue Service A Service

    B Service C book for a class BOOKING_REQUEST 202 (listening to B_R event) GET /v2/bar {"bar":"123"} BOOKING_CONFIRMED results via websockets if required (listening to B_C event) Send notification
  31. @junior_grossi DON'T SKIMP ON LOGS log all requests sent and

    received log all responses sent and received log all data from fired events log all data from received events log all important decisions "in microservices, logging is like a very close friend."
  32. @junior_grossi

  33. @junior_grossi RELIABILITY communication logging responsibility productivity

  34. @junior_grossi RESPONSIBILITY each service needs a reason to exists it's

    allowed to duplicate code, but not data don't you have some data? ask another service! think twice before synchronizing/copying data
  35. @junior_grossi WHAT TO AVOID: 2 or more services sharing the

    same DB (which service changed the data?) partial domain implementations (when you have related logic splitted in 2+ services)
  36. @junior_grossi RELIABILITY communication logging responsibility productivity

  37. @junior_grossi PRODUCTIVITY avoid the "language soup" (pick 2 or 3

    max) you hired Jane. she's good in PHP she starts a new service in Scala she keeps maintaining that service Jane leaves the company you need now PHP + Scala engineers
  38. @junior_grossi PRODUCTIVITY use similar software architecture and patterns easier to

    move between projects easier to move between languages easier to maintain easier to integrate "DDD/Clean Architecture" are a perfect fit!
  39. @junior_grossi PRODUCTIVITY share processes along services share Git/VCS conventions/flows docker

    conventions share CI/CD practices share deployment processes
  40. @junior_grossi PART 3 Implementing reliable PHP-based microservices

  41. @junior_grossi TOPICS software architecture design patterns PHP execution

  42. @junior_grossi SOFTWARE ARCHITECTURE we need something flexible we need something

    organized we need quality we need power
  43. @junior_grossi Robert C. Martin "The goal of software architecture is

    to minimize the human resources required to build and maintain the required system."
  44. @junior_grossi changing the architecture is very expensive!

  45. @junior_grossi LAYERED ARCHITECTURE "leave options opened" (believe me, they do

    change) independent layers independent use cases independent commands
  46. @junior_grossi DOMAIN-DRIVEN DESIGN Implementing Domain-Driven Design Vaughn Vernon

  47. @junior_grossi HEXAGONAL ARCHITECTURE "Hexagonal Architecture - Message-Oriented Software Design" Matthias

    Noback - @matthiasnoback https://youtu.be/K1EJBmwg9EQ
  48. @junior_grossi ABOUT YOUR ARCHITECTURE: framework independent (framework-as-a-tool) testable without any

    external element UI independent (Web / Console) database independent independent of any external agent
  49. @junior_grossi CLEAN ARCHITECTURE

  50. @junior_grossi Clean Architecture Robert C. Martin (Uncle Bob)

  51. @junior_grossi SCREAMING ARCHITECTURE "the architecture says by itself" what is

    your service about? (answer by reading the code!) payments? bookings? authentication? or an app written in Laravel, Slim or Symfony?
  52. @junior_grossi

  53. @junior_grossi https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

  54. @junior_grossi THE DEPENDENCY RULE "source code dependencies must point only

    inward, toward higher-level policies" nothing from "inside" should know anything concrete about "outside".
  55. @junior_grossi fine, but how do I do that? using interfaces/abstractions!

  56. @junior_grossi "code is not reusable. abstractions are." Marco Pivetta (@Ocramius)

  57. @junior_grossi

  58. @junior_grossi USE CASE: CREATE USER Command Handler Pattern (UseCase)

  59. @junior_grossi 1. CONTROLLER ACTION POST /users UsersController::create() (SRP!!!) CreateUserController

  60. @junior_grossi final class CreateUserController { private CreateUserHandler $handler; public function

    __construct(CreateUserHandler $handler) { $this­>handler = $handler; } public function __invoke(RequestInterface $request): ResponseInterface { $body = json_decode((string) $request­>getBody()); $userId = $this­>handler­>handle( new CreateUserCommand( $body['email'], $body['firstName'], $body['lastName'], $body['password'], ) ); return new JsonResponse(compact('userId'), 201);
  61. @junior_grossi 2. COMMAND class that stores the data to execute

    the action (handler). (immutable class!)
  62. @junior_grossi final class CreateUserCommand { private string $email; private string

    $firstName; private string $lastName; private string $password; public function __construct( string $email, string $firstName, string $lastName, string $password ) { $this­>email = $email; $this­>firstName = $firstName; $this­>lastName = $lastName; $this­>password = $password; } // Getters: getEmail(), getFirstName(), getLastName(), getPassword() }
  63. @junior_grossi 3. HANDLER it's the logic of our use case

    receives the data as a Command class
  64. @junior_grossi final class CreateUserHandler implements HandlerInterface { private UserRepositoryInterface $userRepository;

    private PasswordEncrypterInterface $passwordEncrypter; public function __construct( UserRepositoryInterface $userRepository, PasswordEncrypterInterface $passwordEncrypter ) { $this­>userRepository = $userRepository; $this­>passwordEncrypter = $passwordEncrypter; } public function handle(CreateUserCommand $command): Uuid { return $this­>userRepository­>add( new User( Uuid::uuid4(), $command­>getEmail(), $command­>getFirstName(), $command­>getLastName(), $this­>passwordEncrypter­>encrypt( $command­>getPassword() ),
  65. @junior_grossi "UI independent (web/console)" after creating a new studio create

    a new admin user event-oriented (async) running in CLI
  66. @junior_grossi STUDIO_CREATED Command Bus Event A Event B StudioCreatedEvent.php Listener

    1 Listener 2 Listener 3 CreateAdminUserListener.php Listener 4
  67. @junior_grossi final class CreateAdminUserListener implements ListenerInterface { private CreateUserHandler $handler;

    public function __construct(CreateUserHandler $handler) { $this­>handler = $handler; } public function execute(StudioCreatedEvent $event): void { $this­>handler­>handle( new CreateUserCommand( // use the payload from $event ) ); } }
  68. @junior_grossi PHP EXECUTION leaving the comfort zone

  69. @junior_grossi "NodeJS is better for microservices"

  70. @junior_grossi Coroutine based Async PHP programming framework Build high-performance, scalable,

    concurrent TCP, UDP, Unix Socket, HTTP, WebSocket services with PHP and fluent Coroutine API. https://www.swoole.co.uk/
  71. @junior_grossi how PHP naturally works php-fpm / "blocking I/O" CPU

  72. @junior_grossi how Swoole & Node.js work "non-blocking I/O" / async

    CPU I/O
  73. @junior_grossi > pecl install swoole built-in async, coroutine support, multiple

    threads I/O modules async/await yield
  74. @junior_grossi

  75. @junior_grossi Master Reactor Reactor Reactor Timer Manager TaskWorker TaskWorker TaskWorker

    Worker Worker Worker
  76. @junior_grossi

  77. @junior_grossi Initialisation Execute Task Clean up PHP-FPM Execute Task Swoole

  78. @junior_grossi ADVICE use it for new services only (it's not

    a good idea for that legacy API you have)
  79. @junior_grossi COMMON PITFALLS variable scope / DI container (singleton) exception

    handling static content
  80. @junior_grossi https://docs.mezzio.dev/mezzio-swoole/ https://github.com/mezzio/mezzio-swoole

  81. @junior_grossi http://roadrunner.dev/

  82. @junior_grossi // install RoadRunner $ make // psr­worker.php while ($request

    = $psr7­>acceptRequest()) { $response = new \Zend\Diactoros\Response(); $response­>getBody()­>write("hello world"); $psr7­>respond($response); } // config file http: address: :8080 workers: command: "php psr­worker.php" pool: numWorkers: 4 // run $ ./rr serve ­v ­d
  83. @junior_grossi Coroutine Based Concurrency with PHP and Swoole Bruce Dou

    | @doubaokun Feb 2020 PHPUK2020 https://speakerdeck.com/doubaokun/coroutine-based-concurrency-with-php-and-swoole
  84. @junior_grossi THANK YOU! search on youtube for "grossi-code" http://twitter.com/junior_grossi http://speakerdeck.com/jgrossi