ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
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 71
Abonament PDF

Domain-Driven Design

Ovidiu Mățan
Fondator @ Today Software Magazine



PROGRAMARE


DDD reprezintă o metodă practică de a transforma o problemă complexă într-un model ce poate fi implementat ulterior de echipa de dezvoltare software. Există diferite abordări, astfel încât soluția finală să fie SOA, funcțională, bazată pe microservicii sau chiar reactive programming. Este prioritar ca la aceeași masă să se așeze experți din domeniul ce urmează a fi analizat dar și personalul tehnice. Ambele părți vor învăța să folosească un limbaj / tehnici comune, astfel încât transferul de cunoștințe să se poată realiza.

Această abordare este în general utilă pentru aplicațiile software practice care încearcă să rezolve o problemă tehnică de business / enterprise. Implicarea programatorilor în găsirea unei soluții este esențială. Sugestivă în acest sens afirmația lui Scott Wlaschin în lucrarea sa, Domain Modeling Made Functional:

As a developer, you may think that your job is to write code. I disagree.

A developer's job is to solve a problem through software, and coding is just one aspect of software development. Good design and communication are just as important, if not more so.

Abordarea DDD presupune o dezvoltare directă a soluției de către echipa de dezvoltare, fără a mai avea nivele / filtre introduse de către business analiști sau chiar de arhitecți software. Avantajul acestei abordări constă în înțelegerea exactă a problemei, direct de la sursă și dezvoltarea unei soluții pornind de la aceasta. Dezavantajul constă bineînțeles în timpul de dezvoltare mai mare în care echipa R&D trebuie să îl petreacă în dezvoltarea soluției.

Dat fiind timpul scurt impus pentru finalizarea proiectelor software, există riscul unei abordări superficiale în înțelegerea esenței problemei, ajungându-se ca echipa de dezvoltare să primească doar o listă de taskuri în backlog care trebuie rezolvate. Scopul final este ca toate să se mute în secțiunea done , cât se poate de repede. Pentru soluții simple, această abordare poate fi de succes dar pentru un produs complex, cu o estimare a duratei de viață mare vă propun o dezvoltare DDD.

Contextul problemei

Primul lucru cu care trebuie să începem este să definim problemele pe care dorim să le rezolvăm, vom încerca să le eliminăm de la început pe acelea care nu fac parte din context. Totodată vom defini un limbaj comun între developeri și experții în domeniu.

Începem prin a pune pe o tablă note cu acțiunile posibile, le grupăm pe domenii și adăugăm evenimentele de legătură între ele. În cazul de mai sus, order place și ack. sent to customer reprezintă acțiunile de legătură între un sistem de primire a comenzilor și sistemul de livrare.

Definirea unui limbaj comun

Există diferite abordări pentru definirea unui limbaj comun între programatori și experții în domeniu. O abordare clasică este folosirea unor cuvinte cheie precum: and, or, when, than. Un pseudocod mai simplu, care să poate fi înțeles de un expert al domeniului dar care nu este programator. Odată definite diferitele scenarii, se pot implementa și acceptant tests după cum este cel din exemplul următor:

Scenario: The product owner commits a backlog item to a sprint
  Given a backlog item that is scheduled for release 
  And the product owner of the backlog item 
  And a sprint for commitment 
  And a quorurn of team approval for commitment 
  When the product owner commits the backlog item to   the sprint Then the backlog item is committed to the sprint 
  And the backlog item committed event is created

[Test] 
public void ShouldCommitBacklogitemToSprint() 
{
  // Given 
  var backlogitem=BacklogitemScheduledForRelease(); 
  var productOwnner=ProductOwnerOf(backlogitem); 
  var sprint=SprintForCommitment(); 
  var quorum=QuorumOfTeamApproval(backlogitem
   , sprint); 
  // When 
  backlogitem.CommitTo(sprint, productOwner, quorum); 
  // Then 
  Assert. IsTrue(backlogitem. IsCommitted{)); 
  var backlogitemCommitted =
    backlogitem.Events.OfType() 
      .SingleOrDefault();

  Assert.IsNotNull(backlogitemCommitted);
}

Exemplu de scenariu / test de acceptanță folosit în dezvoltarea unui tool de management Agile Scrum. Sursa

Primul avantaj al acestei abordări este că majoritatea experților în domeniu, fie ea formată și din non-tehnici, va putea înțelege testul de mai sus. Al doilea avantaj va fi folosirea acestui cod în mod real, rularea fără eroare reprezentând implementarea cu succes a acestei acțiuni.

Definirea proceselor și a modelelor

Acțiunile concrete vor fi denumite comenzi: "Realizează taskul X". Rezultatul fiecărei comenzi va fi un eveniment "Taskul X a fost realizat", dacă acesta a fost terminat cu succes. În continuare, vă prezentăm o imagine de ansamblu a acestei abordări:

Se poate observa cum un eveniment va lansa o comandă și care la rândul său poate lansa mai multe evenimente pentru a fi finalizată.

Un amănunt important în această abordare este necesitatea de a se ignora nivelul de persistență. Dorim să definim modele corecte, fără a fi într-un fel sau altul constrânși de limitările bazelor de date. Un alt lucru periculos este să ne gândim în diagrame de clase legăturile dintre diferiți actori.

Sursă imagine

DTOs

Definirea structurilor de date se poate realiza într-un limbaj natural:

bounded context: Order-Taking

  data Order =
  CustomerInfo
  AND ShippingAddress
  AND BillingAddress
  AND list of OrderLines
  AND AmountToBill

Iar avantajul acestei abordări este că nu îi va speria pe cei care nu sunt programatori. Continuăm să detaliem și vom ajunge la un pseudocod, așa cum se poate vedea în continuare:

substep "ValidateOrder" =
  input: UnvalidatedOrder
  output: ValidatedOrder OR ValidationError
  dependencies: CheckProductCodeExists
              , CheckAddressExists

  validate the customer name
  check that the shipping and billing address exist
  for each line:
    check product code syntax
    check that product code exists in ProductCatalog

În contextul limitelor domeniului, DTO-urile vor fi validate la intrarea și la ieșirea din limitele acestuia:

Sursă imagine

Legătura între domenii

În literatura de specialitate, se mai numește context mapping și este o procedură menită să asigure o bună comunicare între ambele domenii. Există mai multe tipuri de abordări similare cu cele din mediul de business sau cu acelea din cadrul unei organizații:

  1. Shared Kernel - ambele domenii folosesc împreună un model comun. Echipele trebuie să fie de acord cu acesta, iar orice schimbare trebuie realizată de comun acord. Această abordare este în general dificil de realizat și de menținut.

  2. Customer/Supplier - este o modalitate de comunicare Vânzător / Client unde clientul își formulează nevoia. Cele două domenii evoluează independent atât timp cât Vânzătorul implementează protocolul clientului.

  3. Conformist - este opusul modelului Client / Vânzător. Vânzătorul definește limbajul, iar clientul trebuie să își implementeze nivelul de translatare. Orice API pus la dispoziția publicului de către un provider de servicii poate fi considerat un exemplu.

  4. Anti-corruption layer - este un nivel de transformare a modelului de intrare într-unul sigur pentru domeniu. Este un traducător al limbajelor folosite de echipele celor două domenii.

Exemplu de folosire în comunicarea diferitelor domenii. Referință imagine.

Abordarea funcțională

În cazul programării funcționale se folosește o abordare orientată spre comenzi, care nu alterează datele de intrare într-o funcție. Există întotdeauna o separare clară între date și comportament. Datele de intrare într-o funcție sunt immutable, iar acestea returnează întotdeauna valori noi în loc să le modifice pe cele existente. Rezultatul este un număr redus de side effects. Din perspectiva conceptuală, diferențele dintre o abordare OOP și una funcțională se poate vedea în următoarele diagrame de modelare ale aceluiași domeniu:

Abordarea OOP

Abordarea funcțională

Așa cum se poate observa, în cea de-a doua abordare, listenerul OrderPlaced pentru eveniment este adăugat în exteriorul contextului.

Merită menționată și arhitectura de tip Usturoi (Onion Architecture) folosită în programarea funcțională. Dacă în abordarea OOP, modelele și accesul la baza de date se realizează în interiorul domeniului, într-o abordare funcțională, acestea sunt poziționate la extremități astfel încât toate dependințele sunt pe intrarea / ieșirea din componentă. O funcție care scrie și citește dintr-o bază de date va fi considerată impură, iar folosirea acestora va fi evitată în partea de core a domeniului :

Sursa imagine: https://www.safaribooksonline.com/library/view/domain-modeling-made/9781680505481/f_0034.xhtml

Use case - procesarea cardurilor de credit

O abordare monolit este una în care baza de date este comună. Pe de o parte, se procesează datele clienților online, pe de altă parte, la finalul zilei, pe aceeași bază de date se procesează rapoartele și se optimizează datele acumulate în timpul zilei. Un exemplu în acest sens este clasicul ETL (extract/transform/load).

Un prim pas al evoluției acestei arhitecturi este să includem o bază de date în fiecare componentă, iar comunicarea dintre ele să fie prin intermediul unui API. Deși componentele vor fi mai clare, tot vom avea o complexitate destul de mare și va fi nevoie de o bună comunicare pentru integrarea celor două componente.

Următorul pas este să aplicăm DDD, să definim limitele domeniilor și să modelăm arhitectura în felul următor:

La final vom ajunge la concluzia că o arhitectură bazată pe micro-servicii, se va potrivi cel mai bine acestui exemplu. Vom avea o definire clară a domeniilor, a responsabilităților fiecărei componente și a modului în care acestea comunică.

Concluzie

Design-Driven Development reprezintă o modalitate de comunicare și reprezentare, folosind un limbaj comun al unui sistem complex. Este un proces ce aduce împreună experții în domeniu și echipa de dezvoltare. Rezultatul este unul de înțelegere profundă a problemei care trebuie rezolvată. Vom avea la final o definire a domeniilor, a modului de comunicare între acestea dar și descoperirea unui nucleu de bază (core-ul). Majoritatea conceptelor introduse în acest articol s-ar putea să vă fie familiare, dar ceea ce contează este abordarea DDD. Aceasta este diferită de clasicul PRD/FRD urmat de un backlog complex, unde clientul este abstract și de multe ori aparent inaccesibil echipei de dezvoltare.

Bibliografie

  1. Domain Modeling Made Functional, Scott Wlaschin. ed. The Pragmatic Programmers - 2018.
  2. Domain-Driven Design Distilled, Vaughn Vernon, ed. Addison-Wesley Professional - 2016
  3. Domain Driven Design for Services Architecture

NUMĂRUL 143 - Software Craftsmanship

Sponsori

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