poznáme.it Programovanie Vývoj softvéru ako stavba babylonskej veže

Vývoj softvéru ako stavba babylonskej veže

Podľa príbehu z Biblie babylonskú vežu ľudia nedostavali preto, lebo sa nemohli dohovoriť. Keď stavitelia hovoria rôznymi jazykmi, akýkoľvek komplikovaný projekt skončí nezdarom. Tento článok ukazuje, ako je možné postupovať pri implementácii domény tak, aby bola zachovaná hlavná myšlienka Domain Driven Designu: spoločný jazyk.

Hoci doslovný preklad pojmu „ubiquitous language“ je „všadeprítomný jazyk“, pojem „spoločný jazyk“ v slovenčine lepšie zodpovedá zamýšľanému významu.

Spoločný jazyk

Domain Driven Design je rozšírený prístup k navrhovaniu objektového modelu domény. Obvykle sa diskutujú jeho stavebné bloky ako „repository“, „factory“, „value object“, „aggregate“ atď. Všetky stavebné bloky však vychádzajú z jednej základnej myšlienky: „ubiquitous language“ – t.j. z existencie spoločného (všadeprítomného) jazyka. Spoločný jazyk je množina pojmov, ktoré používajú analytici, programátori, testeri, používatelia aplikácie aj doménoví experti, keď hovoria o doméne. Pričom pre všetkých má každý pojem rovnaký význam.

Technológie nie sú súčasťou domény

Pri písaní zdrojového kódu sa musia programátori vysporiadať s množstvom technologických konceptov, ktoré s doménou nesúvisia, ale pre beh programu sú nevyhnutné (databáza, prenos údajov po sieti, messaging, logovanie, bezpečnosť). Tieto koncepty je možné držať v samostatnej vrstve tak, aby do doménového modelu nezasahovali. Veľmi často sa využíva na tieto účely technika otočenia závislostí – „dependency inversion“.

Reprezentácia pojmov

Pri implementácii pojmov z domény je dôležité nájsť čo najvernejšiu reprezentáciu pojmu domény v analýze a v zdrojovom kóde. Objektovo-orientovaný prístup má v tomto smere výhodu v tesnej väzbe medzi analýzou a implementáciou. Ak analytik nájde správnu reprezentáciu pojmu pomocou tried, objektovo-orientovaný jazyk umožňuje túto reprezentáciu preniesť do zdrojového kódu. Analytik zodpovedá za to, aby špecifikovaná trieda čo najvernejšie odrážala doménu, t.j. chápanie pojmu používateľom. Programátor zase zodpovedá za to, aby implementácia triedy mala tesnú väzbu na analýzu.

Príklad: analýza rizík

V doméne „analýza rizík“ bude existovať pojem „Riziko“ s požiadavkou na výpočet miery rizika:

„Miera rizika sa počíta na základe pravdepodobnosti a dopadu podľa nasledujúcej tabuľky:“

Miera rizikaPravdepodobnosťDopad
VysokáVysokáVeľký
StrednáVysokáMalý
NízkaNízkaVeľký, Malý

 

Použitie novej technológie na projekte vývoja softvéru je typickým rizikom. Ak vývojársky tím ide prvýkrát použiť databázu „MongoDB“, projekt čelí riziku technologických problémov, ktoré má vysokú pravdepodobnosť a veľký dopad. A teda aj miera rizika je vysoká.

Analýza domény

Spoločný jazyk je v tomto diagrame zachovaný. Analytik narába s pojmami „Riziko“, „Pravdepodobnost“ a „Dopad“, pričom sa snaží pochopiť ich vzájomné vzťahy a závislosti.

Implementácia

public final class Riziko {
	
  private String nazov;
  private Pravdepodobnost pravdepodobnost;
  private Dopad dopad;
	
  public MieraRizika vypocetMieryRizika() {
    if ( pravdepodobnost == Pravdepodobnost.VYSOKA ) {
	return dopad == Dopad.MALY ? MieraRizika.NIZKA : MieraRizika.STREDNA;
    } else {
	return MieraRizika.NIZKA;
    }
  }
}

Spoločný jazyk je zachovaný aj v implementácii na úrovni pojmov. Ale metóda na výpočet miery rizika nepoužila spoločný jazyk, pretože použila algoritmický výpočet tam, kde bolo zadanie špecifikované deklaratívne vo forme tabuľky.

Táto implementácia má dva zásadné problémy:

  1. Zdrojový kód nezodpovedá zadaniu od analytika (hoci je implementovaný správne).
  2. Trieda Riziko má zodpovednosť za výpočet miery rizika.

Oba zásadné problémy sa naplno prejavia pri prvej požiadavke na zmenu.

Požiadavka na zmenu

„Malé rozšírenie tabuľky pre výpočet miery rizika:“

Miera rizikaPravdepodobnosťDopad
VysokáVysokáVeľký
StrednáStrednáVeľký
StrednáStrednáMalý
NízkaNízkaMalý

 

V porovnaní s pôvodnou implementáciou navrhol teda používateľ zaviesť strednú pravdepodobnosť, ktorej zodpovedá aj stredná miera rizika.

Dopady na existujúci kód

Takáto požiadavka na zmenu má dva nepríjemné dopady na existujúci zdrojový kód:

  1. Vedie k modifikácii triedy Riziko, čo je v doméne „Analýza rizík“ zásadný pojem, na ktorý budú mať závislosti iné triedy. Akákoľvek zmena tejto triedy môže zasiahnuť všetky ostatné, ktoré na nej závisia. Dôsledkom je nutnosť komplexne pretestovať celý systém.
  2. Keďže pôvodná požiadavka nebola implementovaná v zmysle analýzy, bude potrebné celý algoritmus znovu premyslieť a prerobiť. V tomto príklade je algoritmus triviálny a teda ani jeho opätovné napísanie nebude náročné.

Refactoring, alebo ako to malo byť správne

Obom neželaným dopadom na existujúci kód sa dalo vyhnúť správnym návrhom modelu. Princíp jednoduchej zodpovednosti („single responsibility principle“) v tomto príklade hovorí, že trieda Riziko nemôže mať metódu vypocetMieryRizika, lebo riziko tým získava novú zodpovednosť: vie vypočítať mieru rizika. Správny model teda vyzerá takto:

Trieda VypocetMieryRizika je funkcia, ktorá zo vstupných parametrov Pravdepodobnost a Dopad vypočíta výsledok MieraRizika. V tomto modeli trieda Riziko vôbec nepozná pojem MieraRizika, pretože nie je zaťažená logikou ohľadom výpočtu.

Druhá zásadná úprava spočíva v zmene zdrojového kódu:

public enum VypocetMieryRizika {
	VYSOKA ( MieraRizika.VYSOKA, Pravdepodobnost.VYSOKA, Dopad.VELKY ),
	STREDNA ( MieraRizika.STREDNA, Pravdepodobnost.VYSOKA, Dopad.MALY ),
	NIZKA1 ( MieraRizika.NIZKA, Pravdepodobnost.NIZKA, Dopad.VELKY ),
	NIZKA2 ( MieraRizika.NIZKA, Pravdepodobnost.NIZKA, Dopad.MALY );

	private final MieraRizika miera;
	private final Pravdepodobnost pravdepodobnost;
	private final Dopad dopad;
	
	private VypocetMieryRizika( MieraRizika miera, Pravdepodobnost pravdepodobnost, Dopad dopad ) {
		this.miera = miera;
		this.pravdepodobnost = pravdepodobnost;
		this.dopad = dopad;
	}

	public static Optional<MieraRizika> vypocet(Pravdepodobnost pravdepodobnost, Dopad dopad ) {
		return Arrays.stream(	VypocetMieryRizika.values() )
			.filter( vypocet -> vypocet.dopad == dopad && vypocet.pravdepodobnost == pravdepodobnost )
			.map( vypocet -> vypocet.miera )
			.findAny();
	}

Výhodnou takto napísaného zdrojového kódu je presný obraz analytického zadania. Analytik sa s používateľom dohodol na tabuľkovom vyhodnocovaní výpočtu miery rizika, čiže použili deklaratívny prístup k analýze. Pre zachovanie spoločného jazyka je vhodné udržať deklaratívny prístup aj v implementácii. Kontrola kódu oproti zadaniu je veľmi jednoduchá. Cenou za to je však komplikovanejšia metóda „vypocet“. Prvá implementácia pomocou jedného príkazu „if“ pôsobí ďaleko jednoduchšie.

Výhodnou takto napísaného zdrojového kódu je presný obraz analytického zadania. Analytik sa s používateľom dohodol na tabuľkovom vyhodnocovaní výpočtu miery rizika, čiže použili deklaratívny prístup k analýze. Pre zachovanie spoločného jazyka je vhodné udržať deklaratívny prístup aj v implementácii. Kontrola kódu oproti zadaniu je veľmi jednoduchá. Cenou za to je však komplikovanejšia metóda „vypocet“. Prvá implementácia pomocou jedného príkazu „if“ pôsobí ďaleko jednoduchšie.

Požiadavka na zmenu ešte raz

Ako by vyzerala požiadavka na zmenu, ak by bol model navrhnutý správne?

V správnom modeli by vôbec nezasiahla triedu Riziko, čiže by nemala nečakané dopady na iné triedy, ktoré s objektami typu Riziko pracujú. Prípadné chyby v implementácii by mohli ovplyvniť spôsob výpočtu miery rizika, ale iné nechcené dôsledky by nastať nemohli.

A v druhom rade by verne kopírovala zmenu analytických podkladov:

        VYSOKA (MieraRizika.VYSOKA, Pravdepodobnost.VYSOKA, Dopad.VELKY),
	STREDNA1 (MieraRizika.STREDNA, Pravdepodobnost.STREDNA,Dopad.VELKY),
	STREDNA2 (MieraRizika.STREDNA, Pravdepodobnost.STREDNA,Dopad.MALY),
	NIZKA1 (MieraRizika.NIZKA, Pravdepodobnost.NIZKA, Dopad.VELKY),
	NIZKA2 (MieraRizika.NIZKA, Pravdepodobnost.NIZKA, Dopad.MALY)

V uvedenej tabuľke chýba kombinácia pre vysokú pravdepodobnosť a malý dopad. Rovnakou chybou teda trpí aj zdrojový kód. Avšak chyby takéhoto typu sú ľahko komunikovateľné: keď si analytik, používateľ a programátor sadnú za spoločný stôl, budú mať v predstave rovnaký model a budú používať spoločný jazyk.

Desivý záver: nová požiadavka na zmenu

Namiesto tradičného happy endu končí tento článok desivou požiadavkou na zmenu. Pretože iné oddelenie sa rozhodlo úspešný softvér na analýzu rizík používať tiež.

  • Používateľ: „U nás sa miera rizika počíta veľmi jednoducho: pravdepodobnosť krát dopad.“
  • Analytik: „Krát?“
  • Používateľ: „Veď pravdepodobnosť je číslo od nula do jedna. A dopad je od 0 do 10.“

A úlohou analytika a programátora bude nájsť s používateľom opäť spoločný jazyk. Lebo veď stringy sa násobiť nedajú.


Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.



Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.

Junior / Senior Java Backend Software Engineer

JOB Position: Java Backend Software Engineer Contract type: TPP, Contract Location: Bratislava Salary Junior: 1500 EUR/Brutto Salary Senior: 2200 EUR/Brutto Rate: 3000 -4000 EUR/monthlyPrimary Job...

Senior Sieťový Špecialista

PRÁCA Pozícia: Sieťový špecialista Pracovný pomer:  TPP Miesto práce: Bratislava, on-site Plat: od 1700+ EUR/Brutto/mesačneHlavné zodpovednosti:Navrhuje architektúru, štruktúru a IP adresácie multiplatformových LAN/MAN/WAN sietí ...

Data Mining Expert

PRÁCA Pozícia: Data Mining Expert Pracovný pomer:  TPP Miesto práce: Bratislava, on-site Plat: od 2000+ EUR/Brutto/mesačneAké výzvy ťa čakajú?Vynikajúca pracovná príležitosť pre nadšenca práca...

Artificial Intelligence Consultant

JOB Position: Artificial Intelligence Consultant Contract type: Full-Time Location: Bratislava Salary: 2000 EUR/Brutto/monthsResponsibilities:conceptual processing and development of the new solutions in the area e.g....

Artificial Intelligence Expert

JOB Position: Artificial Intelligence Expert Contract type: Full-Time Location: Bratislava Salary: 2000 EUR/Brutto/monthsSuccessful adaptation of technologies like machine learning, deep learning, artificial inteligence will determine companies...

Senior Front-end / Angular Developer / Tvorba serverovej a aplikačnej logiky

PRÁCA Pozícia: FE/ Angular developer Pracovný pomer:  TPP, živnosť Miesto práce: Bratislava, Prievidza, Žilina Plat: od 1700+ EUR/Brutto/mesačneNáplň práce:Spolupráca na vývoji SW...

Senior .NET/Angular developer

PRÁCA Pozícia: .NET/Angular developer Pracovný pomer:  TPP Miesto práce: Bratislava, on-site Plat: od 1700+ EUR/Brutto/mesačneČo bude Vašou náplňou práce:spolupodieľanie sa na vývoji SW riešenia...

Čo sme stihli v roku 2019

Každý rok je niečím špeciálny a rok 2019 nie je výnimkou. V...

Srdečne ťa pozývame na stretnutie DDD Community – Analýza domény

Na začiatku nového roka sa zameriame na analytické uchopenie domény pomocou DDD....

TOP 10 skillov programátora

Chceš vedieť na akých 10 skillov by si sa ako programátor mal/a zamerať? Sú to skilly, ktoré ti pomôžu si nájsť prácu, či byť označovaný/á ako "guru".

Čítaj ďalej:

Dobrý článok? Chceš dostávať ďalšie?

Už viac ako 6 200 ITečkárov dostáva správy e-mailom. Nemusíš sa báť, nie každé ráno. Len občasne.

Súhlasím so spracovaním mojich osobných údajov. ( Viac informácií. )

Tvoj email neposkytneme 3tím stranám. Posielame naňho len informácie z robime.it. Kedykoľvek sa môžeš odhlásiť.