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
Tweet

More Decks by Junior Grossi

Other Decks in Programming

Transcript

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

    problems you don't have yet! (you may never have those problems)
  2. @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
  3. @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" }
  4. @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
  5. @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."
  6. @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
  7. @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)
  8. @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
  9. @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!
  10. @junior_grossi Robert C. Martin "The goal of software architecture is

    to minimize the human resources required to build and maintain the required system."
  11. @junior_grossi LAYERED ARCHITECTURE "leave options opened" (believe me, they do

    change) independent layers independent use cases independent commands
  12. @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
  13. @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?
  14. @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".
  15. @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);
  16. @junior_grossi 2. COMMAND class that stores the data to execute

    the action (handler). (immutable class!)
  17. @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() }
  18. @junior_grossi 3. HANDLER it's the logic of our use case

    receives the data as a Command class
  19. @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() ),
  20. @junior_grossi "UI independent (web/console)" after creating a new studio create

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

    1 Listener 2 Listener 3 CreateAdminUserListener.php Listener 4
  22. @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 ) ); } }
  23. @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/
  24. @junior_grossi ADVICE use it for new services only (it's not

    a good idea for that legacy API you have)
  25. @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
  26. @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