Implementing reliable PHP-based microservices

Implementing reliable PHP-based microservices

Event: PHP Dublin 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.

7bb3a66a199daec275d5ad339724c754?s=128

Junior Grossi

May 13, 2020
Tweet

Transcript

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

    should be ;-) PHP Dublin, May/2020
  2. @junior_grossi IMPLEMENTING RELIABLE PHP-BASED MICROSERVICES or at least, how they

    should be ;-) PHP Dublin, May/2020
  3. @junior_grossi Hi, I'm Junior Grossi twitter.com/junior_grossi github.com/jgrossi

  4. @junior_grossi ⚠ Stutterer alert!

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

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

  7. @junior_grossi I ❤ #elePHPants https://elephpant.me/herd/junior_grossi

  8. @junior_grossi https://glofox.com/careers

  9. @junior_grossi ◽ 2004 php ◽ 2016 ◽ 2019 ◽ ✈

  10. @junior_grossi Ahhh the weather! I live in Galway, that's a

    bit worse
  11. @junior_grossi Let's talk about microservices

  12. @junior_grossi I WILL BE: Sharing what I've learned ~1 year

    Sharing what I'm learning
  13. @junior_grossi THE TITLE Implementing reliable PHP-based microservices

  14. @junior_grossi Implementing reliable PHP-based microservices

  15. @junior_grossi Implementing reliable PHP-based microservices

  16. @junior_grossi Implementing reliable PHP-based microservices

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

  18. @junior_grossi Microservices? Again? 1-minute tutorial

  19. @junior_grossi "Microservices" is an architectural style It's not gonna solve

    problems you don't have yet! (You may never have those problems)
  20. @junior_grossi What's a Monolith?

  21. @junior_grossi

  22. @junior_grossi What's Microservice architecture?

  23. @junior_grossi USING MICROSERVICES Need clear implementations Share implementation issues from

    monoliths
  24. @junior_grossi "Don't use microservices if you don't need them"

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

  26. @junior_grossi RELIABILITY Communication Logging Responsibility Productivity

  27. @junior_grossi COMMUNICATION Sync (HTTP, gRPC) Async (event-oriented)

  28. @junior_grossi SYNC COMMUNICATION

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

    B Service C book for a class BOOKING_REQUEST 204 (listening to B_R event) GET /v2/bar {"bar":"123"} BOOKING_CONFIRMED results via websockets if required (listening to B_C event) Send notification
  30. @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" }
  31. @junior_grossi RELIABILITY Communication Logging Responsibility Productivity

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

    B Service C book for a class BOOKING_REQUEST 204 (listening to B_R event) GET /v2/bar {"bar":"123"} BOOKING_CONFIRMED results via websockets if required (listening to B_C event) Send notification
  33. @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 "With microservices, logging is like a very close friend."
  34. @junior_grossi

  35. @junior_grossi RELIABILITY Communication Logging Responsibility Productivity

  36. @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
  37. @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)
  38. @junior_grossi RELIABILITY Communication Logging Responsibility Productivity

  39. @junior_grossi PRODUCTIVITY Avoid the "language soup" (pick 2 or 3

    max) 1. You hired John. He's good in PHP 2. He starts a new service in Scala 3. He keeps maintaining that service 4. John leaves the company 5. You need now PHP + Scala engineers
  40. @junior_grossi PRODUCTIVITY Use similar so ware architecture and patterns Easier

    to move between projects Easier to move between languages Easier to maintain Easier to integrate
  41. @junior_grossi PRODUCTIVITY Share processes along services Share Git/VCS conventions/flows Docker

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

  43. @junior_grossi TOPICS So ware Architecture Design Patterns Execution

  44. @junior_grossi SOFTWARE ARCHITECTURE We need something flexible We need something

    organized We need quality We need power
  45. @junior_grossi Robert C. Martin "The goal of so ware architecture

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

  47. @junior_grossi LAYERED ARCHITECTURE "Leave options opened" (believe me, they do

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

  49. @junior_grossi HEXAGONAL ARCHITECTURE "Hexagonal Architecture - Message-Oriented So ware Design"

    Matthias Noback - @matthiasnoback https://youtu.be/K1EJBmwg9EQ
  50. @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
  51. @junior_grossi CLEAN ARCHITECTURE

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

  53. @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?
  54. @junior_grossi "A good so ware architecture allows decisions about frameworks,

    databases, web-servers, and other environmental issues and tools, to be deferred and delayed." Uncle Bob
  55. @junior_grossi https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

  56. @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".
  57. @junior_grossi Fine, but how do I do that? Using Interfaces/Abstractions!

  58. @junior_grossi "Code is not reusable. Abstractions are." Marco Pivetta (@Ocramius)

  59. @junior_grossi

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

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

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

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

    the action (handler). Immutable class!
  64. @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() }
  65. @junior_grossi 3. HANDLER It's the logic of our use case

    Receives the data as a Command class
  66. @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() ),
  67. @junior_grossi "UI independent (Web / Console)" 1. A er creating

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

    1 Listener 2 Listener 3 CreateAdminUserListener.php Listener 4
  69. @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 ) ); } }
  70. @junior_grossi EXECUTION Leaving the comfort zone

  71. @junior_grossi "NodeJS is better for microservices"

  72. @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. > It extends some use cases for PHP https://www.swoole.co.uk/
  73. @junior_grossi How PHP naturally works php­fpm | "blocking I/O" CPU

    I/O
  74. @junior_grossi How Swoole & Node.js works "non-blocking I/O" | async

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

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

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

    Worker Worker Worker
  78. @junior_grossi

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

  80. @junior_grossi ADVICE ⚠ Use it for new services only (it's

    not a good idea for that legacy API you have)
  81. @junior_grossi COMMON PITFALLS ☢ Variable scope / DI container (Singleton)

    Exception handling Static content
  82. @junior_grossi https://docs.mezzio.dev/mezzio-swoole/ https://github.com/mezzio/mezzio-swoole

  83. @junior_grossi http://swo .io/

  84. @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
  85. @junior_grossi THANK YOU! http://twitter.com/junior_grossi http://speakerdeck.com/jgrossi