ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 147
Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 46
Abonament PDF

Colțul arhitectural: Introducere în CQRS

Georgiana Gligor
Owner @Tekkie Consulting



PROGRAMARE

Ce este CQRS?

Bertrand Meyer a fost cel care a introdus distincția între termenii Commandă și Interogare, când a afirmat că orice metodă trebuie să fie ori o Interogare (doar citește informații) fie o Comandă (execută o acțiune), dar nu ambele simultan.

CQRS este un acronim în limba engleză pentru Command-Query Responsibility Segregation. Este un șablon arhitectural care a fost discutat prima dată de Greg Young în 2010. Folosește ca bază principiul enunțat de Meyer și îl completează, sugerând ca responsabilitățile de citire și cele de scriere să fie împărțite în obiecte complet distincte. Această abordare se opune celei tradiționale CRUD, unde Modelul este creat, citit, modificat și șters în mod uniform în cadrul aceleiași clase, cu metode pentru fiecare dintre aceste acțiuni.

O privire mai profundă

Cele mai multe componente se pretează foarte bine pe abordarea CRUD și nu e indicat să le complicăm inutil doar de dragul de a folosi CQRS. Să ilustrăm modelul CRUD cu o diagramă în care o componentă foarte simplă interacționează direct cu utilizatorul final, dar și salvează și extrage informațiile din baza de date. Puteți oricând substitui atât utilizatorul cât și stocarea persistentă cu alte componente de sistem. Diagrama noastră CRUD folosește pattern-ul Repository pentru a executa acțiunile, acesta comunicând la rândul său cu modelul. În acest caz, deoarece comporatamentul este în același loc, există pericolul aglomerării codului, acesta devenind greu de menținut pe termen lung, precum și expus introducerii unor erori în părți ale sistemului în care nu ne-am aștepta.

Să aplicăm împreună separarea între Comenzi și Interogări pe un exemplu care tratează angajații.

// foloseste model Angajat  
public class EmployeeService {  
    public function obtineAngajat($id) { /* ... */}  
    public function obtineAngajatiiInConcediu() { /* ... */}  
    public function angajeaza(Persoana $persoana, $laData) { /* ... */}  
    public function promoveaza($id, $noulPost, $deLaData) { /* ... */}  
    public function maresteRemuneratia($id, $suma, $delaData) { /* ... */}  
    public function concediaza($id, $laData) { /* ... */}  
    public function modificaAdresa($id, Adresa $adresa) { /* ... */}  
}

La folosirea CQRS vom avea două servicii specializate,

// foloseste acelasi model Angajat  
public class EmployeeReadService {  
    public function obtineAngajat($id) { /* ... */}  
    public function obtineAngajatiiInConcediu() { /* ... */}  
}  

// foloseste acelasi model Angajat  
public class EmployeeWriteService {  
    public function angajeaza(Persoana $persoana, $laData) { /* ... */}  
    public function promoveaza($id, $noulPost, $deLaData) { /* ... */}  
    public function maresteRemuneratia($id, $suma, $delaData) { /* ... */}  
    public function concediaza($id, $laData) { /* ... */}  
    public function modificaAdresa($id, Adresa $adresa) { /* ... */}  
}

Aceasta este cea mai simplă separare pe care o putem face, dar deja ne ajută foarte mult. Am reprezentat în diagrama de mai jos modelul CQRS cu Model comun.

Frumusețea folosirii serviciilor distincte pentru interogări și comenzi rezidă în faptul că ne face codul suficient de flexibil încât să poată folosi și metode diferite. Imaginați-vă Modelul specializat! Cât de elegant este acum doar pentru că ați eliminat partea care se ocupa de scriere. De asemenea, pentru modelul specializat pe citire, nu a devenit deja mult simplificat când nu trebuie să se ocupe de aspectele specifice citirii?

Să avansăm încă un pas și să separăm complet baza de date, pentru a scrie într-una în care consistența datelor este îndeplinită și să citim din alta care are o eventuală consistență (poate fi în urma cu versiunea datelor), dar optimizată pentru citire. Vă rog să observați cum în partea dreaptă a diagramei am eliminat complet orice referință la Model și cum returnăm obiecte DTO direct din serviciile de Interogare.

Am adăugat de asemenea conceptul de Fațadă, care este foarte răspândit în arhitecturile software contemporane, acesta ajutându-ne să separăm limitele (en: boundaries) mult mai curat.

Beneficii

În abordarea CRUD, codul poate deveni strâns cuplat foarte repede. Una din cele mai frecvente probleme pe care le-am întâlnit ca programator a fost situația în care o modificare într-o parte a codului care se ocupă de o interogare să rezulte în efecte secundare nedorite ale comportamentului unei Comenzi. Aceste situații sunt foarte dificil de tratat, deoarece ceea ce părea o mică modificare de cod ajunge în realitate să fie o refactorizare nu atât de simplă. CQRS abordează această problemă foarte simplu, prin separarea fizică a codului care face scrierile de cel care citește.

Serviciile responsabile pentru comportamente diferite pot fi găzduite pe sisteme diferite, permițând așadar scalarea lor independentă. Cele mai multe dintre aplicații necesită citiri intensive, astfel că serviciile dedicate de interogare pot fi scalate orizontal folosing zeci de mașini. În același timp, serviciile de comenzi trebuie scalate la o scară mult mai mică, așadar vor fi necesare mai puține mașini.

A avea servicii diferite crește flexibilitatea în abordarea stocării datelor. Cel mai simplu model, cel al unei singure baze de date e foarte bun pentru început, dar libertatea de a scrie într-o bază de date normalizată unde sunt reguli stricte de consistență a datelor și mutarea datelor de citire într-una denormalizată optimizată pentru scopul său este o soluție foarte elegantă la o nevoie pe care am întâlnit-o destul de des în ultimul timp, fiind cea mai ieftin de pus în practică refactorizare a unor sisteme a căror performanță lăsa de dorit.

CQRS se potrivește foarte bine cu modelele bazate pe evenimente, deoarece permite event sourcing-ului să înlocuiască baza de date consistentă cu un storage pentru evenimente. Evenimentele sunt apoi aplicate și se obțin datele interogabile. Pe site-ul cqrsinfo există un articol foarte detaliat pe acest subiect.

Un avantaj mai puțin intuitiv vine din ușurința cu care se pot împărți task-uri atomice programatorilor din echipă. Astfel, echipele mai numeroase pot colabora mai bine pe cod. În timp ce componentele CRUD pot fi încredințate colegilor mai puțin experimentați, complexitatea CQRS poate fi manevrată de colegii seniori, care apoi distribuie informația întregii echipe.

Dezavantaje

Să presupunem că ai ales să folosiți modele diferite pentru cele două responsabilități, le numim aici generic CommandModel și QueryModel. Se adaugă în mod inevitabil un overhead din perspectiva mentenanței, câmpurile noi trebuie adăugate în ambele locuri.

Este necesară o abordare la nivel de componentă a utilității șablonului CQRS, nu una la nivelul întregii aplicații, deoarece doar o parte din componente vor avea nevoie de un tratament special, non-CRUD.

Cele mai bune locuri pentru a lua în calcul CQRS sunt limitele dintre componentele sistemului, unde segregarea permite cazurilor specifice să fie rafinate și mai în profunzime, spre exemplu prin folosirea abordării event sourcing dacă aceasta este necesară, fără afectarea altor componente ale aplicației.

Modelarea folosind CQRS este posibil a fi mai greu înțeleasă de o parte din colegii programatori, astfel încât folosită în locurile nepotrivite va cauza probleme. Dar așa cum am menționat mai sus, acest lucru se poate evita printr-o mai inteligentă distribuire a rolurilor în echipă și transferul de cunoștințe între colegi în timp.

Există în general mai multe clase care trebuie încărcate, mai mult spațiu pe disk este consumat atunci când se separă și datele, dar și mai mult spațiu este necesar la folosirea event sourcing. De aceea, sunt necesare capacități DevOps în cadrul echipei și în general o infrastructură mai bine organizată.

Note:

În aceeaşi ediţie ... (46)

▼ TOATE ARTICOLELE ▼

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects