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
- l'utente può fare l'ordine
- l'utente paga
- i piatti devono essere disponibili
- l'indirizzo di consegna è coperto
- l'utente riceve una email di conferma
- 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!)
Microservice Decorator
By Tommaso Allevi
Microservice Decorator
- 1,506