Microservice Decorator

coordinare i microservizi in modo semplice e dichiarativo

Tommaso Allevi

@allevo

Platform R&D Leader @ Mia-Platform

Perchè sviluppare a microservizi?

Disaccoppia componenti del progetto

Ogni microservizio può essere sviluppato con un suo linguaggio

Aiuta a dividere il problema in sottoproblemi

Scala i tuoi microservizi in modo indipendente

Riutilizzare i microservizi

è possibile testare la singola business logic

Sviluppo incrementale di feature

Riutilizzare i microservizi

Sviluppo incrementale di feature

Esempio #1

Login

Implementazione in un monolite

Auth

Service

Session

Manager

HTTP handler

Implementazione in un monolita

Risoluzione delle credenziali

Gestione della session HTTP

MVC, MVVM, etc...

app.post('/login', async function (request) {
  const { username, password } = request.body

  const user = await authService.findUserByUsernameAndPassword(username, password)
  if (!user) throw new httpError.BadRequest()

  sessionManager.loginUser(request, user)

  return user
})

Dividiamo il monolita in microservizi!

auth-service

session-manager

Esempio #1

Login

Dipendenza tra microservizi

Auth
Service

Session

Manager

Dipendenza tra microservizi

1

Abbiamo due microservizi!

Dipendenze forti tra microservizi

Esempio #1

Login

Aggregatore / Backend For Frontend

Aggregatore / Backend For Frontend

Auth
Service

Session

Manager

Aggregatore

1

2

Abbiamo tre microservizi

e un pattern architetturale

Cosa mai potrà andare storto?

Se dovessimo introdurre una nuova feature, quali componenti dovremmo modificare?

Dipendenza tra microservizi

  • auth service

 

Aggregatore / Backend for frontend

  • aggregatore

È una buona pratica?

Open/closed principle

Un'entità software dovrebbe essere aperta alle estensioni, ma chiusa alle modifiche.

Wikipedia

Esiste un modo per estendere senza modificare?

Microservice Decorator

Un decoratore è
un componente dell'architettura
che può:

  • Ricevere una notifica di quando c'è una richiesta/risposta
  • Modificare la richiesta/risposta aggiungendo/togliendo parametri (querystring, header, body...)
  • Fermare la richiesta stabilendo la risposta definitiva da dare al client (per esempio un check andato male)

Una possibile implementazione può essere fatta usando il protocollo HTTP nel quale il decoratore accetta in input la richiesta/risposta e risponde in base all'azione che deve essere compiuta

Business Logic
API

Decoratore pre1

Decoratore pre2

Decoratore pre3

...

Decoratore post1

Decoratore post2

Decoratore post3

...

Flow

{
  "/incoming_url": {
    "GET": {
      "pre": [ "pre1", "pre2" ],
      "call": "http://business-service/proxied-url",
      "post": [ "post1" ]
    }
  },
  "decorators": {
    "pre1": {
      "call": "http://some-service2/decorators/path1",
    },
    "pre2": {
      "call": "http://other-service/decorators/path2",
    },
    "post1": {
      "call": "http://some-service3/decorators/path3",
    }
  }
}

  GET /incoming_url?foo=bar

pre1

pre2

business-service

  GET /proxied-url?bar=foo

post3

Il Microservice Decorator impone un interfaccia HTTP che ogni decoratore deve implementare

Un microservizio non sono le API che espone

 ma la business logic che implementa

È una limitazione?

No

Ports and Adapter Pattern

Esempio #1

Login

Microservice Decorator

{
  "/login": {
    "POST": {
      "pre": [ ],
      "call": "http://auth-service/identify",
      "post": [ "createSession" ]
    }
  },
  "decorators": {
    "createSession": {
      "call": "http://session-manager/decorators/createSession",
    }
  }
}

Microservice Decorator

Auth
Service

Session

Manager

POST /login

Microservice
Decorator

POST /identify

POST
/decorators/createSession

POST /login

{ username: "aa", password: "bb" }

---> { userId: "1234" }

POST /decorators/createSession

{ userId: "1234" }

---> Set-Cookie: sid="abcd"

---> { userId: "1234" }

POST /identify

{ username: "aa", password: "bb" }

---> Set-Cookie: sid="abcd"

---> { userId: "1234" }

Esempio #2

Login con CSRF token

Dipendenza tra microservizi

Cross-site request forgery token

stringa random valida una volta sola che evita

di eseguire due volte la stessa richiesta

Auth
Service

Session

Manager

Dipendenza tra microservizi

CSRF

Service

1

2

Esempio #2

Login con CSRF

Aggregatore / Backend For Frontend

Aggregatore / Backend For Frontend

Auth
Service

Session

Manager

Aggregatore

2

3

CSRF

Service

1

È proprio necessario modificare il codice esistente per aggiungere un controllo?

Entrambe le soluzioni prevedono la modifica di un codice già esistente

Esempio #2

Login con CSRF

Microservice Decorator

Microservice Decorator

Auth

Service

Session

Manager

Microservice
Decorator

CSRF

Service

1

2

3

{
  "/login": {
    "POST": {
      "pre": [ "checkCSRF" ],
      "call": "http://auth-service/identify",
      "post": [ "createSession" ]
    }
  },
  "decorators": {
    "createSession": {
      "call": "http://session-manager/decorators/createSession",
    },
    "checkCSRF": {
      "call": "http://csrf-service/decorators/checkCSRF",
    }
  }
}

Nessuna linea di codice esistente è stata modificata!

Abbiamo aggiunto le linee di codice necessarie per la nuova feature in un microservizio separato e modificato un file di configurazione

Esempio #3

Registrazione

Microservice Decorator

{
  "/signup": {
    "POST": {
      "pre": [ ],
      "call": "http://auth-service/signup",
      "post": [ ]
    }
  },
  "decorators": {
  }
}

Se volessimo aggiungere il controllo per il CSRF token?

{
  "/signup": {
    "POST": {
      "pre": [ "checkCSRF" ],
      "call": "http://auth-service/signup",
      "post": [ ]
    }
  },
  "decorators": {
    "createSession": {
      "call": "http://session-manager/decorators/createSession",
    }
  }
}

Se volessimo che l'utente sia già

loggato dopo la registrazione?

{
  "/signup": {
    "POST": {
      "pre": [ "checkCSRF" ],
      "call": "http://auth-service/signup",
      "post": [ "createSession" ]
    }
  },
  "decorators": {
    "createSession": {
      "call": "http://session-manager/decorators/createSession",
    },
    "checkCSRF": {
      "call": "http://csrf-service/decorators/checkCSRF",
    }
  }
}

E la mail di benvenuto?

{
  "/signup": {
    "POST": {
      "pre": [ "checkCSRF" ],
      "call": "http://auth-service/signup",
      "post": [ "createSession", "sendWelcomeMail" ]
    }
  },
  "decorators": {
    ...
    "sendWelcomeMail": {
      "call": "http://mail-service/decorators/welcome"
    }
  }
}

Aggiungi comportamenti senza modificare i tuoi microservizi

  • Tieni la business logic separata da side effects
  • Aggiungi incrementalmente feature senza modificare il codice esistente
  • Descrivi la tua architettura attraverso un unico file di configurazione

Esempio #4

Fare un ordine

Microservice Decorator

Requisiti

  1. l'utente può fare l'ordine
  2. l'utente paga
  3. i piatti devono essere disponibili
  4. l'indirizzo di consegna è coperto
  5. l'utente riceve una email di conferma
  6. il fattorino viene notificato della nuova consegna

Simuliamo ora un sito di food delivery presso cui l'utente può selezionare i piatti che vorrebbe ordinare 

order-service

dish-service

payment-service

address-service

email-service

push-service

  • salva l'ordine sul database
  • marca un ordine come pagato
  • esegui il pagamento
  • censisce la disponibilità dei piatti
  • controlla che un indirizzo sia nella zona vicina
  • manda l'email di conferma
  • manda la push al fattorino per la consegna
{
  "/orders/": {
    "POST": {
      "pre": [ "checkDishesAvailability", "checkAddress" ],
      "call": "http://order-service/orders/",
      "post": [ "makePayment", "markTheOrderAsPaid", "notifyTheUser", "notifyTheDeliveryBoy" ]
    }
  },
  "decorators": {
    "checkDishesAvailability": { "call": "http://dish-service/decorators/checkAvailability" },

    "checkAddress": { "call": "http://address-service/decorators/checkAddress" },

    "makePayment": { "call": "http://payment-service/decorators/payForTheOrder" },

    "markTheOrderAsPaid": { "call": "http://order-service/decorators/markAsPaid" },

    "notifyTheUser": { "call": "http://email-service/decorators/sendOrderMail" },

    "notifyTheDeliveryBoy": { "call": "http://push-service/decorators/sendPushToDeliveryBoy" }
  }
}
Route /login - Method POST

                   +-------------------+                                   +-----------------------+                    
                   |                   |                                   |                       |                    
 request ---------->     checkCSRF     +---------> PROXY Request ---------->     createSession     +---------> response 
                   |                   |                                   |                       |                    
                   +---------^---------+                                   +-----------^-----------+                    
                             |                                                         |                                
                             |                                                         |                                
                             v                                                         v                                
                       csrf-service                                             session-manager                         
                   /decorators/checkCSRF                                   /decorators/createSession                    


Route /signup - Method POST

                   +-------------------+                                   +-----------------------+                    
                   |                   |                                   |                       |                    
 request ---------->     checkCSRF     +---------> PROXY Request ---------->     createSession     +---------> response 
                   |                   |                                   |                       |                    
                   +---------^---------+                                   +-----------^-----------+                    
                             |                                                         |                                
                             |                                                         |                                
                             v                                                         v                                
                       csrf-service                                             session-manager                         
                   /decorators/checkCSRF                                   /decorators/createSession                    


Route /orders/ - Method POST

                   +---------------------------+         +----------------------+                                   +------------------------+         +--------------------+         +-----------------------+         +-------------------------------+                    
                   |                           |         |                      |                                   |                        |         |                    |         |                       |         |                               |                    
 request ---------->  checkDishesAvailability  +--------->     checkAddress     +---------> PROXY Request ---------->      makePayment       +---------> markTheOrderAsPaid +--------->     notifyTheUser     +--------->     notifyTheDeliveryBoy      +---------> response 
                   |                           |         |                      |                                   |                        |         |                    |         |                       |         |                               |                    
                   +-------------^-------------+         +-----------^----------+                                   +------------^-----------+         +----------^---------+         +-----------^-----------+         +---------------^---------------+                    
                                 |                                   |                                                           |                                |                               |                                     |                                    
                                 |                                   |                                                           |                                |                               |                                     |                                    
                                 v                                   v                                                           v                                v                               v                                     v                                    
                           dish-service                      address-service                                             payment-service                   order-service                    email-service                         push-service                               
                   /decorators/checkAvailability         /decorators/checkAddress                                   /decorators/payForTheOrder         /decorators/markAsPaid         /decorators/sendOrderMail         /decorators/sendPushToDeliveryBoy                    

E la documentazione?

La configurazione del Microservice Decorator è già documentazione!

E per gli amanti dell'ASCII art:

Grazie!

(we are hiring!)

Made with Slides.com