Quando abbiamo detto che la coesione cambia e l’accoppiamento resta abbiamo usato un esempio:

Se pensiamo ad un processo come ad una macchina a stati, in ogni stato del processo ci possono essere coinvolti uno o più servizi, coesi tra loro da quello specifico stato, ma non necessariamente nello stato successivo. Se non stiamo attenti e scivoliamo da coesione verso accoppiamento a questo punto ci ritroviamo che i servizi coinvolti in un processo sono per forza anche inutilmente coesi, proprio perché accoppiati, a prescindere dallo stato del processo stesso.

La macchina a stati

Un esempio minimalista potrebbe essere il seguente:

  1. Scelgo dal catalogo
  2. Aggiungo al carrello
  3. Inizio il checkout
  4. Imputo l’indirizzo di spedizione
  5. Scelgo il tipo di spedizione
  6. Imputo l’indirizzo di fatturazione
  7. Pago
  8. La merce viene reperita
  9. Il corriere espresso viene contattato
  10. La merce viene spedita
  11. L’ordine è evaso

Diciamo che dal punto 3 in poi è una tipica gestione ordini. Abbiamo vari servizi (logici) coinvolti, come Shipping, Finance, e Warehouse.

Un tentativo accoppiato

Un modo per modellare il processo di cui sopra potrebbe essere:

Il carrello manda un comando al gestore ordini per iniziare il checkout. Il checkout manda un comando a Shipping per far si che i dettagli della spedizione vengano definiti, Shipping quindi manda un comando a Finance per gestire il pagamento, Finance una volta gestito il pagamento manda un comando a Warehouse per iniziare la comporre l’ordine e infine Warehouse manda un comando nuovamente a Shipping per iniziare la spedizione, al termine della quale Shipping dice al gestore dell’ordine che l’ordine è completato e quindi evaso.

Se rileggete quello che abbiamo appena scritto e sostituite tutti i “manda un comando” con “invoca” avete un bel flusso procedurale, accoppiatissimo e monolitico. Flusso in cui magari i service boundary sono chiari e definiti ma monolitico resta, se ci mettete una coda e dei messaggi ottenete due cose:

  • più complessità
  • il solo vantaggio che la coda vi fa off-loading del carico quindi se il passaggio X è particolarmente lento gli altri non ne risentono

L’orchestratore

A questo punto tipicamente il passaggio che si fa è: ma se l’ordine è a tutti gli effetti una macchina a stati, allora un orchestrator, o un process manager, o una saga sono il modello ideale. Del resto prima in un paio di punti sono stato obbligato ad usare il termine “gestore ordini”.

La trasformazione che quindi si applica potrebbe essere ad esempio:

Il carrello manda un comando al gestore ordini per iniziare il checkout. Il gestore ordini manda un comando a Shipping per far si che i dettagli della spedizione vengano definiti, Shipping quindi manda un messaggio al gestore ordini per informare che i dettagli della spedizione sono definiti, il gestore ordini quindi dice a Finance che può gestire il pagamento, Finance una volta gestito il pagamento manda un messaggio al gestore ordini per aggiornarlo sulla situazione. A questo punto il gestore ordini può mandare un comando a Warehouse per iniziare la comporre l’ordine e, dopo aver ricevuto conferma da Warehouse, infine mandare un comando nuovamente a Shipping per iniziare la spedizione, al termine della quale Shipping dice al gestore dell’ordine che l’ordine è completato e quindi evaso.

È cambiato qualcosa?

Secondo me è peggiorato, lo si evince anche solo leggendo:

  • Abbiamo sempre una sorta di comportamento procedurale, questa volta nelle mani di qualcuno di specifico: il gestore ordini
  • è ancora più complesso perché adesso abbiamo un nuovo attore: il gestore ordini
  • è molto verboso, o “chatty” come direbbero i miei colleghi

Perché quindi?

Spesso si finisce comunque per disegnare una soluzione come la seconda con la convinzione che risolva due problemi:

  • non è monolitica, ma è distribuita, scala ed è disaccoppiata (solo perché c’è una coda nel mezzo)
  • abbiamo un posto solo dove la UI può andare a leggere per riportare al lettore lo stato dell’ordine

Tada: “Riportare al lettore lo stato dell’ordine”

Questo è quello che ci sta fregando, questa necessità ci sta dicendo che una porzione del sistema, quella in cui facciamo reportistica all’utente, ha bisogno di informazioni aggregate sull’ordine. Il processo che gestisce l’ordine assolutamente no. I singoli servizi hanno bisogno delle loro informazioni interne per poter fare il loro lavoro. Shipping non ha bisogno di sapere quanto ho pagato o come ho pagato, ha solo bisogno di sapere che ho pagato.

Quindi abbiamo due informazioni importanti adesso:

  • La UI sta generano coesione, alcuni servizi devono in alcuni momenti essere in grado di riportare al lettore informazioni
  • Gli stati dei singoli servizi coinvolti nella gestione dell’ordine sono coesi al fine di risolvere il problema della gestione dell’ordine, ergo tutti prima o poi devono partecipare in qualche modo facendo la loro parte

Perché la coesione può cambiare?

Nel processo di cui sopra il carrello è coeso con la gestione dell’ordine? Solo al primo passaggio, quando iniziamo il processo di checkout, se lo iniziamo dal carrello, se lo iniziassimo da un ordine telefonico no non sarebbe coeso.

Allo stesso modo la coesione evolve o cambia con l’evolvere o l’avanzamento del processo di gestione dell’ordine, c’è un momento, ma solo quello, in cui Shipping probabilmente ha bisogno di Warehouse per capire dimensione e valore dei beni da spedire per organizzare la spedizione e capire se deve assicurarli.

Infine nuovi requisiti potrebbero cambiare gli attuali rapporti di coesione ma sarebbero un terremoto se ci fosse accoppiamento, uno su tutti: arriva uno stakeholder e vi informa che il business vuole vendere anche musica in formato digitale. Tutta la parte di spedizione scompare in un batter d’occhio. Se c’è accoppiamento sono dolori. Se c’è solo coesione c’è solo da introdurre il nuovo processo.

Ricordiamoci che:

Siccome l’accoppiamento è per sempre è bene lavorare per ridurlo al minimo indispensabile, ricordandoci che accoppiamento zero è impossibile. La strategia per ridurre all’osso l’accoppiamento tra le parti è identificare i service boundary seguendo la coesione che è una buona linea guida.