Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

@junior_grossi Hi, I'm Junior Grossi twitter.com/junior_grossi github.com/jgrossi

Slide 4

Slide 4 text

@junior_grossi ⚠ Stutterer alert!

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

@junior_grossi https://glofox.com/careers

Slide 9

Slide 9 text

@junior_grossi ◽ 2004 php ◽ 2016 ◽ 2019 ◽ ✈

Slide 10

Slide 10 text

@junior_grossi Ahhh the weather! I live in Galway, that's a bit worse

Slide 11

Slide 11 text

@junior_grossi Let's talk about microservices

Slide 12

Slide 12 text

@junior_grossi I WILL BE: Sharing what I've learned ~1 year Sharing what I'm learning

Slide 13

Slide 13 text

@junior_grossi THE TITLE Implementing reliable PHP-based microservices

Slide 14

Slide 14 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 15

Slide 15 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 16

Slide 16 text

@junior_grossi Implementing reliable PHP-based microservices

Slide 17

Slide 17 text

@junior_grossi PART 1 Implementing reliable PHP-based microservices

Slide 18

Slide 18 text

@junior_grossi Microservices? Again? 1-minute tutorial

Slide 19

Slide 19 text

@junior_grossi "Microservices" is an architectural style It's not gonna solve problems you don't have yet! (You may never have those problems)

Slide 20

Slide 20 text

@junior_grossi What's a Monolith?

Slide 21

Slide 21 text

@junior_grossi

Slide 22

Slide 22 text

@junior_grossi What's Microservice architecture?

Slide 23

Slide 23 text

@junior_grossi USING MICROSERVICES Need clear implementations Share implementation issues from monoliths

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

@junior_grossi PART 2 Implementing reliable PHP-based microservices

Slide 26

Slide 26 text

@junior_grossi RELIABILITY Communication Logging Responsibility Productivity

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

@junior_grossi SYNC COMMUNICATION

Slide 29

Slide 29 text

@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

Slide 30

Slide 30 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 31

Slide 31 text

@junior_grossi RELIABILITY Communication Logging Responsibility Productivity

Slide 32

Slide 32 text

@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

Slide 33

Slide 33 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 "With microservices, logging is like a very close friend."

Slide 34

Slide 34 text

@junior_grossi

Slide 35

Slide 35 text

@junior_grossi RELIABILITY Communication Logging Responsibility Productivity

Slide 36

Slide 36 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 37

Slide 37 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 38

Slide 38 text

@junior_grossi RELIABILITY Communication Logging Responsibility Productivity

Slide 39

Slide 39 text

@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

Slide 40

Slide 40 text

@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

Slide 41

Slide 41 text

@junior_grossi PRODUCTIVITY Share processes along services Share Git/VCS conventions/flows Docker conventions Share CI/CD practices Share deployment processes

Slide 42

Slide 42 text

@junior_grossi PART 3 Implementing reliable PHP-based microservices

Slide 43

Slide 43 text

@junior_grossi TOPICS So ware Architecture Design Patterns Execution

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

@junior_grossi Changing the architecture is very expensive!

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 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 51

Slide 51 text

@junior_grossi CLEAN ARCHITECTURE

Slide 52

Slide 52 text

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

Slide 53

Slide 53 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 54

Slide 54 text

@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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 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 57

Slide 57 text

@junior_grossi Fine, but how do I do that? Using Interfaces/Abstractions!

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

@junior_grossi

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 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 65

Slide 65 text

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

Slide 66

Slide 66 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 67

Slide 67 text

@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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 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 70

Slide 70 text

@junior_grossi EXECUTION Leaving the comfort zone

Slide 71

Slide 71 text

@junior_grossi "NodeJS is better for microservices"

Slide 72

Slide 72 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. > It extends some use cases for PHP https://www.swoole.co.uk/

Slide 73

Slide 73 text

@junior_grossi How PHP naturally works php­fpm | "blocking I/O" CPU I/O

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

@junior_grossi

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

@junior_grossi

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

@junior_grossi COMMON PITFALLS ☢ Variable scope / DI container (Singleton) Exception handling Static content

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

@junior_grossi http://swo .io/

Slide 84

Slide 84 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 85

Slide 85 text

@junior_grossi THANK YOU! http://twitter.com/junior_grossi http://speakerdeck.com/jgrossi