Slide 1

Slide 1 text

@junior_grossi IMPLEMENTING RELIABLE PHP-BASED MICROSERVICES or at least, how they should be ;-)

Slide 2

Slide 2 text

@junior_grossi hey, I'm Junior Grossi twitter.com/junior_grossi github.com/jgrossi grossi.dev

Slide 3

Slide 3 text

@junior_grossi

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

@junior_grossi remote-first company php / golang / java / js https://glofox.com/careers

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

@junior_grossi ... to this perfect weather! I live in Galway, that's a bit worse

Slide 10

Slide 10 text

@junior_grossi let's talk about microservices

Slide 11

Slide 11 text

@junior_grossi I WILL BE: sharing what I've learned ~1.5 year sharing what I'm learning

Slide 12

Slide 12 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 13

Slide 13 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 14

Slide 14 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 15

Slide 15 text

@junior_grossi PART 1 Implementing reliable PHP-based microservices

Slide 16

Slide 16 text

@junior_grossi microservices? again? 1-minute tutorial

Slide 17

Slide 17 text

@junior_grossi "microservices" is an architectural style it will not solve problems you don't have yet! (you may never have those problems)

Slide 18

Slide 18 text

@junior_grossi what's a monolith?

Slide 19

Slide 19 text

@junior_grossi

Slide 20

Slide 20 text

@junior_grossi what's microservice architecture?

Slide 21

Slide 21 text

@junior_grossi USING MICROSERVICES need clear implementations share implementation issues from monoliths

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

@junior_grossi PART 2 Implementing reliable PHP-based microservices

Slide 24

Slide 24 text

@junior_grossi RELIABILITY communication logging responsibility productivity

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

@junior_grossi SYNC COMMUNICATION

Slide 27

Slide 27 text

@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

Slide 28

Slide 28 text

@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" }

Slide 29

Slide 29 text

@junior_grossi RELIABILITY communication logging responsibility productivity

Slide 30

Slide 30 text

@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

Slide 31

Slide 31 text

@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."

Slide 32

Slide 32 text

@junior_grossi

Slide 33

Slide 33 text

@junior_grossi RELIABILITY communication logging responsibility productivity

Slide 34

Slide 34 text

@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

Slide 35

Slide 35 text

@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)

Slide 36

Slide 36 text

@junior_grossi RELIABILITY communication logging responsibility productivity

Slide 37

Slide 37 text

@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

Slide 38

Slide 38 text

@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!

Slide 39

Slide 39 text

@junior_grossi PRODUCTIVITY share processes along services share Git/VCS conventions/flows docker conventions share CI/CD practices share deployment processes

Slide 40

Slide 40 text

@junior_grossi PART 3 Implementing reliable PHP-based microservices

Slide 41

Slide 41 text

@junior_grossi TOPICS software architecture design patterns PHP execution

Slide 42

Slide 42 text

@junior_grossi SOFTWARE ARCHITECTURE we need something flexible we need something organized we need quality we need power

Slide 43

Slide 43 text

@junior_grossi Robert C. Martin "The goal of software architecture is to minimize the human resources required to build and maintain the required system."

Slide 44

Slide 44 text

@junior_grossi changing the architecture is very expensive!

Slide 45

Slide 45 text

@junior_grossi LAYERED ARCHITECTURE "leave options opened" (believe me, they do change) independent layers independent use cases independent commands

Slide 46

Slide 46 text

@junior_grossi DOMAIN-DRIVEN DESIGN Implementing Domain-Driven Design Vaughn Vernon

Slide 47

Slide 47 text

@junior_grossi HEXAGONAL ARCHITECTURE "Hexagonal Architecture - Message-Oriented Software Design" Matthias Noback - @matthiasnoback https://youtu.be/K1EJBmwg9EQ

Slide 48

Slide 48 text

@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

Slide 49

Slide 49 text

@junior_grossi CLEAN ARCHITECTURE

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

@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?

Slide 52

Slide 52 text

@junior_grossi

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

@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".

Slide 55

Slide 55 text

@junior_grossi fine, but how do I do that? using interfaces/abstractions!

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

@junior_grossi

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

@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);

Slide 61

Slide 61 text

@junior_grossi 2. COMMAND class that stores the data to execute the action (handler). (immutable class!)

Slide 62

Slide 62 text

@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() }

Slide 63

Slide 63 text

@junior_grossi 3. HANDLER it's the logic of our use case receives the data as a Command class

Slide 64

Slide 64 text

@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() ),

Slide 65

Slide 65 text

@junior_grossi "UI independent (web/console)" after creating a new studio create a new admin user event-oriented (async) running in CLI

Slide 66

Slide 66 text

@junior_grossi STUDIO_CREATED Command Bus Event A Event B StudioCreatedEvent.php Listener 1 Listener 2 Listener 3 CreateAdminUserListener.php Listener 4

Slide 67

Slide 67 text

@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 ) ); } }

Slide 68

Slide 68 text

@junior_grossi PHP EXECUTION leaving the comfort zone

Slide 69

Slide 69 text

@junior_grossi "NodeJS is better for microservices"

Slide 70

Slide 70 text

@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/

Slide 71

Slide 71 text

@junior_grossi how PHP naturally works php-fpm / "blocking I/O" CPU I/O

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

@junior_grossi > pecl install swoole built-in async, coroutine support, multiple threads I/O modules async/await yield

Slide 74

Slide 74 text

@junior_grossi

Slide 75

Slide 75 text

@junior_grossi Master Reactor Reactor Reactor Timer Manager TaskWorker TaskWorker TaskWorker Worker Worker Worker

Slide 76

Slide 76 text

@junior_grossi

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

@junior_grossi ADVICE use it for new services only (it's not a good idea for that legacy API you have)

Slide 79

Slide 79 text

@junior_grossi COMMON PITFALLS variable scope / DI container (singleton) exception handling static content

Slide 80

Slide 80 text

@junior_grossi https://docs.mezzio.dev/mezzio-swoole/ https://github.com/mezzio/mezzio-swoole

Slide 81

Slide 81 text

@junior_grossi http://roadrunner.dev/

Slide 82

Slide 82 text

@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

Slide 83

Slide 83 text

@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

Slide 84

Slide 84 text

@junior_grossi THANK YOU! search on youtube for "grossi-code" http://twitter.com/junior_grossi http://speakerdeck.com/jgrossi