razvoj modularnih aplikacija u .net okru zenjuunarske... · univerzitet u ni su prirodno...

141
Univerzitet u Ni ˇ su Prirodno matemati ˇ cki fakultet Departman za ra ˇ cunarske nauke Master rad Razvoj modularnih aplikacija u .NET okruˇ zenju Student: Marko Milenkovi´ c Mentor: Prof. dr Marko Petkovi´ c Niˇ s, Oktobar 2016.

Upload: others

Post on 12-Sep-2019

9 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Univerzitet u Nisu

Prirodno matematicki fakultet

Departman za racunarske nauke

Master rad

Razvoj modularnih aplikacija u .NETokruzenju

Student:Marko Milenkovic

Mentor:Prof. dr Marko Petkovic

Nis, Oktobar 2016.

Page 2: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Sadrzaj

1 Uvod 51.1 Zahtevi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51.2 Odabir tehnologija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

1.2.1 Baza podataka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.2.2 Korisnicki interfejs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

1.3 Arhitektura aplikacije . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2 Dizajn dirigovan domenom (eng. Domain Driven Design – DDD) 82.1 Arhitektura DDD Aplikacije . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

2.1.1 Slojevita arhitektura . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.2 Osnovni gradivni elementi . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.3 Paterni za modeliranje domena . . . . . . . . . . . . . . . . . . . . . . . . . 10

2.3.1 Entiteti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112.3.2 Vrednosni objekti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122.3.3 Servisi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132.3.4 Dogadaji domena . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162.3.5 Moduli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

2.4 Zivotni vek objekata u domenu . . . . . . . . . . . . . . . . . . . . . . . . . 182.4.1 Agregati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192.4.2 Fabrike . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212.4.3 Repozitorijum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

3 Razdvajanje odgovornosti komandi i upita (eng. Command Query Re-sponsibility Segregation – CQRS) 263.1 Strana upita . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283.2 Komandna strana . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4 Implementacija modela domena 304.1 Definisanje agregata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314.2 Dizajniranje sloja domena . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

4.2.1 Bazna klasa entiteta . . . . . . . . . . . . . . . . . . . . . . . . . . . 324.2.2 Bazna klasa vrednosnih objekata . . . . . . . . . . . . . . . . . . . . 334.2.3 Bazna klasa agregata . . . . . . . . . . . . . . . . . . . . . . . . . . . 354.2.4 Agregat Faktura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364.2.5 Vrednosni objekat Novac . . . . . . . . . . . . . . . . . . . . . . . . . 394.2.6 Dogadaji domena . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40

5 Implementacija aplikacionog sloja 455.1 Komandna strana . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

5.1.1 Komande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45

1

Page 3: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

5.1.2 Hendleri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465.1.3 Dispecer komande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46

5.2 Strana upita . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485.2.1 Model podataka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 485.2.2 Upiti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

6 Sloj pristupa podacima - komandna strana 546.1 Repozitorijum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

6.1.1 Interakcije repozitorijuma . . . . . . . . . . . . . . . . . . . . . . . . 546.1.2 Repozitorijum za snimanje podataka . . . . . . . . . . . . . . . . . . 566.1.3 Povezivanje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 566.1.4 Implementacija Repozitorijuma . . . . . . . . . . . . . . . . . . . . . 576.1.5 Implementacija bazne klase repozitorijuma . . . . . . . . . . . . . . . 586.1.6 Implementacija repozitorijuma ProizvodRepository . . . . . . . . . . . 60

6.2 Jedinica Rada (eng. Unit Of Work – UOW) . . . . . . . . . . . . . . . . . . 626.2.1 Implementacija jedinice rada . . . . . . . . . . . . . . . . . . . . . . . 63

6.3 Izlaz na tabelu podataka (eng. Table Data Gateway – TDG) . . . . . . . . . 676.3.1 Implementacija bazne TDG klase . . . . . . . . . . . . . . . . . . . . 686.3.2 Implementacija ProizvodGateway klase . . . . . . . . . . . . . . . . . 71

6.4 Fabrika . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 736.4.1 Implementacija bazne klasa fabrika . . . . . . . . . . . . . . . . . . . 736.4.2 Implementacija fabrike ProizvodFactory . . . . . . . . . . . . . . . . . 74

7 Sloj pristupa podacima - strana upita 767.1 Implementacija bazne klase hendlera upita . . . . . . . . . . . . . . . . . . . 76

7.1.1 Implementacija hendlera GetFakturaByIdQueryHandler . . . . . . . . 777.1.2 Implementacija bazne TDG klase . . . . . . . . . . . . . . . . . . . . 787.1.3 Implementacija FakturaDtoGateway TDG klase . . . . . . . . . . . . 79

8 Prezentacioni sloj – Model-View-ViewModel (MVVM) patern 808.1 Uvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808.2 Istorija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 808.3 Odgovornosti klasa i njihove karakteristike . . . . . . . . . . . . . . . . . . . 80

8.3.1 View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 818.3.2 View model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 828.3.3 Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82

8.4 Saradnja izmedu klasa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 838.4.1 Povezivanje podataka (eng. Data binding) . . . . . . . . . . . . . . . 838.4.2 Implementiranje interfejsa INotifyPropertyChanged . . . . . . . . . . 838.4.3 Komande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

8.5 Validacija podataka i izvestavanje o greskama . . . . . . . . . . . . . . . . . 868.5.1 Implementiranje interfejsa IDataErrorInfo . . . . . . . . . . . . . . . 86

8.6 Konstrukcija i povezivanje objekata . . . . . . . . . . . . . . . . . . . . . . . 88

2

Page 4: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.6.1 Povezivanje objekata deklarativno . . . . . . . . . . . . . . . . . . . . 888.6.2 Upotreba Sablona . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 898.6.3 Povezivanje objekata programski . . . . . . . . . . . . . . . . . . . . 90

8.7 Implementacija MVVM klasa . . . . . . . . . . . . . . . . . . . . . . . . . . 918.7.1 Klasa BasePropertyChanged . . . . . . . . . . . . . . . . . . . . . . . 918.7.2 Klasa BaseViewModel . . . . . . . . . . . . . . . . . . . . . . . . . . 928.7.3 Klasa BaseNavigableViewModel . . . . . . . . . . . . . . . . . . . . . 928.7.4 Klasa BaseNavSearchViewModel . . . . . . . . . . . . . . . . . . . . . 938.7.5 Klasa BaseNavEditViewModel . . . . . . . . . . . . . . . . . . . . . . 958.7.6 Klasa BaseEditEntityModel . . . . . . . . . . . . . . . . . . . . . . . 978.7.7 Klasa VMProizvodSel . . . . . . . . . . . . . . . . . . . . . . . . . . . 988.7.8 Klasa VMProizvodNavEdt . . . . . . . . . . . . . . . . . . . . . . . . 998.7.9 Klasa MProizvod . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1008.7.10 Klasa CommandQueryService . . . . . . . . . . . . . . . . . . . . . . 101

9 Podela aplikacije u module 1039.1 Modularne aplikacije . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1039.2 Razvoj modularne aplikacije upotrebom Prism biblioteke . . . . . . . . . . . 105

9.2.1 Osnovni gradivni elemenat modularnih aplikacija - IModule interfejs . 1059.2.2 Zivotni ciklus modula . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069.2.3 Katalog modula . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069.2.4 Integracija modula u aplikaciju . . . . . . . . . . . . . . . . . . . . . 1079.2.5 Komunikacija izmedu modula . . . . . . . . . . . . . . . . . . . . . . 107

9.3 Ubrizgavanje zavisnosti (eng. Dependency Injection – DI) . . . . . . . . . . . 1079.3.1 Upotreba kontejnera - osnovni scenariji . . . . . . . . . . . . . . . . . 108

9.4 Paterni za ubrizgavanje zavisnosti . . . . . . . . . . . . . . . . . . . . . . . . 1099.4.1 Princip inverzije zavisnosti (eng. Dependency inversion principle – DIP)1099.4.2 Inverzija kontrole (eng. Inversion of Control – IoC) . . . . . . . . . . 1109.4.3 Ubrizgavanje zavisnosti (eng. Dependency Injection – DI) . . . . . . 1119.4.4 Uklapanje pojmova . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

9.5 Implementiranje modula u aplikaciji . . . . . . . . . . . . . . . . . . . . . . . 1129.5.1 Horizontalni slojevi aplikacije . . . . . . . . . . . . . . . . . . . . . . 1129.5.2 Definisanje horizontalnih modula aplikacije . . . . . . . . . . . . . . . 1149.5.3 Vertikalni moduli aplikacije . . . . . . . . . . . . . . . . . . . . . . . 115

9.6 Inicijalizacija aplikacije i povezivanje modula . . . . . . . . . . . . . . . . . . 1169.6.1 Inicijalizacija modula . . . . . . . . . . . . . . . . . . . . . . . . . . . 117

10 Korisnicki interfejs 11910.1 Koncepti rasporedivanja UI elemenata . . . . . . . . . . . . . . . . . . . . . 119

10.1.1 Shell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11910.1.2 View . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12010.1.3 Kompozitni view . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12010.1.4 Konstruisanje view objekata . . . . . . . . . . . . . . . . . . . . . . . 120

3

Page 5: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

10.2 Navigacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12110.3 Prism navigacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

10.3.1 Statusno bazirana navigacija . . . . . . . . . . . . . . . . . . . . . . . 12110.3.2 View bazirana navigacija . . . . . . . . . . . . . . . . . . . . . . . . . 12210.3.3 Implementacija Regiona . . . . . . . . . . . . . . . . . . . . . . . . . 12310.3.4 Osnovna navigacija u regionu . . . . . . . . . . . . . . . . . . . . . . 12410.3.5 Uloga view i view model objekta u navigaciji . . . . . . . . . . . . . . 12510.3.6 Prosledivanje parametara tokom navigacije . . . . . . . . . . . . . . . 126

10.4 Interakcija sa korisnikom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12810.4.1 Komunikacija sa korisnikom upotrebom interakcionog servisa . . . . . 128

10.5 Implementacija korisnickog interfejsa u aplikaciji Prodavnica . . . . . . . . . 13010.5.1 Regioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13010.5.2 Navigacija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

11 Zakljucak 133

4

Page 6: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

1 Uvod

Tokom studiranja izvucavali smo predmete koji su se bavili razlicitim aspektima softvera.Takode, u toku izucavanja tih predmeta, imali smo dosta projekata gde je bilo potrebnoizraditi razlicite aplikacije. Uglavnom, to su bile male aplikacije, koje je izradivao jedan ilinekolicina studenata. Mene kao studenta zanimalo je kako izgledaju aplikacije iz “stvarnogsveta”, koje se koriste u ozbiljnim kompanijama. Shodno tome, osnovna ideja ovog rada jesteizucavanje arhitekture modernih enterprajz aplikacija kroz prakticnu izradu aplikacije nakoju se primenjuju izuceni koncepti. Za potrebe naseg rada izradena je aplikacija Prodavnicana kojoj su primenjeni principi opisani u njemu. Pretpostavlja se da je citalac upoznat saosnovama jezika c#, osnovnim konceptima objektno orijentisanog programiranja kao i sakonceptom dizajn paterna.

Rad se sastoji iz vise celina u kojima se opisuju razliciti delovi arhitekture aplikacije.Svaka celina u radu sastoji se, uglavnom, iz dva dela: prvi deo zasnovan je na teorijskomopisu koncepata, a drugi deo na nacinu na koji su ti koncepti implementirani u projekatProdavnica. Na pocetku naseg rada definisacemo zahteve za izradu aplikacije, a potom cemoobjasniti odabir koriscenih tehnologija za njen nastanak.

1.1 Zahtevi

Pre izrade bilo koje aplikacije potrebno je definisati zahteve. Zahtevi se definisu razgo-vorom sa klijentom.Funkcionalni zahtevi su:

• Evidencija podataka o dobavljacima (unos, pregled, izmena);

• Evidencija proizvoda (unos, pregled, pretraga i izmena);

• Evidencija faktura (unos faktura, pregled unetih faktura i izmena faktura, pre negosto budu kalkulisane);

• Evidencija kalkulacija (kalkulisanje faktura, listanje kalkulacija).

• Kucanje racuna (prva verzija bez povezivanja kase);

• Statistike (top 10 najprodavanijih proizvoda, top 5 dobavljaca, iznos pazara za zadatiperiod - graficki prikaz).

Tehnicki zahtevi su:

• U prvoj fazi izrade potrebno je kreirati desktop aplikaciju koja treba da se izvrsavana lokalnom racunaru. Nakon toga, kreira se serverska aplikacija koja ce opsluzivatirazlicite zahteve klijenta (desktop, web, mobilne aplikacije...).

• Windows je okruzenje u kome se aplikacija izvrsava.

5

Page 7: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Naravno, u stvarnom svetu, zahtevi su mnogo slozeniji jer sadrze mnogo vise detalja zasvaku iznetu stavku. Takode, obicno se komunicira sa osobom koja daje zahteve kako bi sena taj nacin izbegle moguce nejasnoce tokom rada, i kako bi klijent bio zadovoljan.

1.2 Odabir tehnologija

Kada korisnik iznese svoje zahteve, bira se najpogodnija tehnologija za izradu aplikacije.S obzirom na to da se radi o izradi desktop aplikacije koja treba da se izvrsava na Windowsokruzenju (tako da je moguce po potrebi ukljuciti i iskljuciti razlicite module aplikacije), mismo odabrali .NET tehnologiju i c# kao jezik za izradu aplikacije.

1.2.1 Baza podataka

Kako je primarna svrha ove aplikacije rad sa podacima, ona, potencijalno, moze da radisa velikim brojem razlicitih tipova podataka. Pred nama je zahtev da se pronade nacin nakoji ce se skladistiti podaci sa kojima korisnik radi. Za potrebe naseg rada, odlucili smo dapodatke smestamo u bazu podataka. Za upravljanje bazom podataka izabrali smo softverMySql. Razlozi su sledeci: besplatan je, ima veliku bazu korisnika, ne zahteva previse resursai postoji veliki broj alata za rad koji olaksavaju izradu aplikacije.

1.2.2 Korisnicki interfejs

.NET biblioteka sadrzi dve velike biblioteke za izradu korisnickog interfejsa. WindowsForms je starija biblioteka, ali je dalje zastupljena. Samim tim sto je biblioteka starija, znacida je stabilna i da sadrzi malo bagova. Forme se prave ili prevlacenjem kontrola na dizajnforme ili pisanjem programskog koda.

WPF je novija tehnologija za izradu Windows desktop aplikacija. Forme se mogu pravitipisanjem programskog koda ili upotrebom XAML koda. XAML predstavlja XML specifi-kaciju specijalno namenjenu za definisanje korisnickog interfejsa u WPF aplikacijama, kojiolaksava dizajniranje korisnickog interfejsa aplikacije (Postoji specijalna aplikacija za kre-iranje korisnickog interfejsa koju mogu koristiti dizajneri za izcrtavanje formi – MicrosoftBlend). WPF koristi hardversko ubrzanje za iscrtavanje korisnickih komponenti i veliki brojnovina koje nisu prisutne u staroj biblioteci. Kako je WPF novija biblioteka, korisnickiinterfejs aplikacije izraden je upotrebom ove biblioteke.

1.3 Arhitektura aplikacije

U daljem radu, bilo je potrebno definisati arhitekturu aplikacije. Aplikacija mora dapodrzava pristup bazi podataka ali takode treba i da bude nezavisna od konkretnog softveraza rad sa bazom podataka, sto se tice korisnickog interfejsa pocetni zahtev je desktop aplika-cija, ali korisnik je izjavio da bi, mozda, u buducnosti zeleo web aplikaciju. Stoga, aplikacijatreba da ima centralizovanu logiku i slojevitu arhitekturu. Deo za pristup podacima, kao ideo za prikaz korisnickog interfejsa, treba drzati modularnim i opcionim. U ovom delu rada,

6

Page 8: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

naisli smo na sledece probleme: kako definisati slojeve aplikacije, kako ih implementirati ikako ih, na kraju, povezati. S obzirom na to da se dizajn dirigovan domenom pokazao kaoizrazito popularna filozofija razvoja aplikacija, odrucili smo da bas nju proucavamo, a potomi prakticno primenimo prilikom izrade aplikacije Prodavnica.

7

Page 9: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

2 Dizajn dirigovan domenom (eng. Domain Driven

Design – DDD)

Dizajn dirigovan domenom (DDD) je filozofija razvoja aplikacija definisana od straneErika Evansa [1]. DDD je pristup razvoju softvera koji omogucava timovima efikasnu kon-strukciju i odrzavanje kompleksnog softvera. Glavni fokus DDD-a je mapiranje biznis kon-cepata u softverski kod. DDD predstavlja veliku temu o kome je napisano vise knjiga. Udaljem tekstu opisacemo samo najvaznije koncepte potrebne za izradu aplikacije.

2.1 Arhitektura DDD Aplikacije

Razvoj aplikacija pracenjem DDD principa ne zahteva upotrebu nijedne specificne ar-hitekture aplikacije, ali jedna stvar koju arhitektura mora da podrzi jeste izolacija logikedomena.

Kako bismo izbegli da kod preraste u veliki zamrseni spageti kod, i zbog toga oslabiintegritet i na kraju upotrebljivost modela domena, veoma je bitno da struktura aplikacijeomogucava odvojeno resavanje tehnickih problema od problema domena. Prezentaciona,skladisna i logika domena u aplikaciji menjaju se razlicitom brzinom i iz razlicitih razloga.Arhitektura koja razdvaja ove probleme moze da se prilagodi promenama bez velikog naporai nezeljenih efekata na nepovezane delove koda.

Dodatno, arhitektura aplikacije mora da abstraktuje kompleksnosti domena izlaganjemskupa servisa koji skrivaju implementacione detalje domena. Apstraktovanjem na visi nivosprecava se da promene u sloju domena uticu na prezentacioni sloj i obrnuto.

Postoji veliki broj nacina na koje softverski sitem moze da se podeli, ali kroz iskustvoi konvenciju, industrija je konvergirala, prema slojevitim arhitekturama i, posebno premadefinisanju nekoliko standarnih slojeva. Metafora “raslojavanje” se tako rasprostranjenokoristi da je njeno znacenje intuativno za vecinu programera. Osnovni princip je da bilo kojielemenat u sloju zavisi jedino od drugih elemenata u istom sloju ili od elemenata u slojevimakoji se nalaze “ispod” tog sloja. Komunikacija “na gore” mora da se vrsi kroz neki indirektnimehanizam.

Vrednost slojeva je ta da se svaki specijalizuje na odredeni deo programa. Ova speci-jalizacija dozvoljava kohezivan dizajn svakog dela i cini ih jednostavnijim za interpretaciju.Naravno, bitno je izabrati slojeve koji izoluju najvaznije koncepte dizajna. Ponovo, iskustvo ikonvencija su konvergirale u odredeni pravac. Istina, postoji mnogo varijacija. Najuspesnijearhitekture koriste neku varijaciju sledeca cetiri konceptualna sloja:

• UI (prezentacioni sloj). Verovatno, najednostavniji za razumevanje, ovaj sloj je od-govoran za prikaz informacija korisniku i tumacenje korisnikovih komandi. Ponekada,umesto coveka, korisnik moze biti druga aplikacija ili sistem.

• Aplikacioni sloj. Definise zadatke koje aplikacija moze da odradi i koordinise objek-tima domena kako bi se kompletirao biznis zadatak. Ovaj sloj treba da bude veomatanak, ne treba da sadrzi biznis pravila, niti znanje o domenu, niti stanje koje je

8

Page 10: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 1: Graficki prikaz slojevite arhitekture

potrebno snimiti na izvor podataka, jer su ovo sve zadaci domena. Aplikacioni slojupravlja biznis zadatkom i delegira akcije prema domenu. Ne sadrzi stanje biznis en-titeta, ali moze da sadrzi stanje koje prati trenutni zadatak. Veoma je bitno da seaplikacioni sloj ne mesa sa modelom domena.

• Sloj domena. Ovo je mesto gde biznis logika aplikacije zivi i predstavlja srz softvera.Zaduzen je za predstavljanje biznis koncepata, informacija o biznis situacijama i biznispravila. Stanje koje reflektuje biznis situaciju koristi se i kontrolise u ovom sloju, dokse snimanje stanja na izvor podataka prepusta infrastrukturnom sloju.

• Infrastrukturni sloj. Ovo je mesto gde se nalazi sav generalni tehnicki i instalacionikod, kao sto je: implementacija snimanja objekata u bazu podataka, slanje poruka,logovanje internog stanja aplikacije i ostali generalni zadaci.

2.1.1 Slojevita arhitektura

Za razliku od tipicnih pogleda na slojevitu arhitekturu, dijagram 2 prikazuje da se u srcuarhitekture nalazi sloj domena koji sadrzi svu biznis logiku. Okruzuje ga aplikacioni sloj kojiapstraktuje detalje domena iza aplikacionog interfejsa koji predstavlja upotrebne slucajeveaplikacije. Logika domena i aplikacioni slojevi su izolovani i zasticeni od kompleksnostiklijenata, eksternih biblioteka i infrastrukturnih zadataka.

Kako bi se obezbedilo razdvajanje odgovornosti, sloj domena i aplikacioni sloj (koji senalaze u centru arhitekture), ne zavise od drugih slojeva. Sve zavisnosti se krecu unapred,na taj nacin da se sloj domena nalazi u srcu aplikacije i nije zavisan od cega drugog, foku-sirajuci se samo na zadatke domena. Aplikacioni sloj jedino zavisi od domena. On upravljaprocesiranjem korisnickog zahteva, delegirajuci posao sloju domena.

Naravno, stanje objekata domena mora da se snimi negde. Kako bi se ovo postiglo bezuplitanja tehnickog koda u sloj domena, aplikacioni sloj definise interfejse koji omogucavaju

9

Page 11: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 2: Sloj domena u centru arhitekture

snimanje objekata domena. Ovi interfejsi se pisu iz perspektive aplikacionog sloja jezikomi stilom koji su jasni bez specificnih biblioteka i tehnickih zargona. Infrastrukturni slojevionda implementiraju te interfejse. Upravljanje transakcijama zajedno sa ostalim zadacima(kao sto su sigurnost i logovanje) pruza se na isti nacin.

2.2 Osnovni gradivni elementi

Paterni dobijeni iz najboljih DDD praksi obicno se nazivaju takticki DDD paterni. Mnogipaterni nisu novi, ali Evans je prvi koji ih je grupisao na ovaj nacin i pomogao programerimada kreiraju efektivne modele domena. U daljem tekstu opisujemo takticke paterne kojipredstavljaju gradivne elemente DDD-a.

Uloga takticnih paterna u DDD-u je da pojednostave upravljanje kompleksnostima kojeproisticu iz biznis zahteva i da osiguraju jasnocu ponasanja u okviru modela domena. Ovipaterni se izgraduju oko cvrstih objektno orijentisanih principa, i veci deo njih nalazi se usiroko rasprostranjenim knjigama o dizajnu [2] i [16].

Svaki patern je dizajniran tako da se njegova odgovornost moze svesti na sledece: mozepredstavljati koncept u domenu, kao sto su to entiteti ili vrednosni objekati, ili moze dapostoji kako bi osigurao da koncepti u domenu ne budu prenatrpani logikom zivotnog ciklusaobjekata, kao sto su to fabrike i repozitorijumi.

2.3 Paterni za modeliranje domena

Sledeci paterni cine logiku i politiku implementacije sloja domena. Oni predstavljaju vezeizmedu objekata, pravila u modelu i vezuju biznis detalje za kod implementacionog modela.Ovo su paterni koji predstavljaju elemente modela u kodu.

10

Page 12: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

2.3.1 Entiteti

Veliki broj objekata nije fundamentalno definisan svojim atributima, vec tokom identi-teta. Osoba ima svoj identitet, koji se prostire od rodenja do smrti, pa cak i posle smrti.Fizicke osobine osobe se menjaju, i na kraju nestaju. Licno ime i adresa mogu se promeniti,pa, cak, i maticni broj. Dakle, ne postoji osobina osobe koja je nepromenljiva, a, ipak,postoji identitet.

Neki objekti nisu primarno definisani po svojim atributima. Oni predstavljaju tok iden-titeta koji se krece kroz vreme i cesto kroz razlicite reprezentacije. Nekada konceptualnotakav objekat moze biti isti sa drugim objektom, iako im se atributi razlikuju. Objekat semora razlikovati od ostalih objekata iako je moguce da on ima iste atribute kao sto imaju ineki drugi objekti. Pogresan identitet moze voditi do ostecenja podataka.

Posebnu paznju treba posvetiti modeliranju i dizajniranju entiteta. Oni imaju zivotnivek tokom koga se njihov oblik i sadrzina mogu radikalno promeniti, ali identitet mora bitiocuvan. Njihov identitet mora biti definisan tako da mozemo efektivno da ih pratimo.

Vecina entiteta u softverskom sistemu ne predstavlja osobu ili entitet u uobicajenom smi-slu te reci. Entitet je svaki objekat koji ima kontinuitet kroz vreme i identifikuje se nezavisnood atributa koji su bitni za korisnika aplikacije. Zavisno od domena problema, entiteti mogupredstavljati osobu, grad, automobil, bankovnu transakciju ili neki drugi pojam.

Sa druge strane, nisu svi objekti entiteti. Ovo moze biti zbunjujuce za OOP programerezbog cinjenice da objektno orijentisani jezici ugraduju operacije identiteta u svaki objekat(operator “==”). Ove operacije odreduju da li dve reference pokazuju na isti objekat takosto uporeduju njihove lokacije u memoriji ili po nekom drugom mehanizmu. U tom smislu,svaka instanca objekta ima svoj identitet. U domenu .NET runtime okruzenja svaka instancaobjekta moze biti entitet, ali ovaj mehanizam identiteta ne znaci mnogo u drugim domenimaaplikacije(na primer, kada se entitet snimi u bazu podataka ili fajl).

Razmotrimo transakcije u bankarskoj aplikaciji. Dve uplate sa istom kolicinom novca,na isti racun, istog dana i dalje predstavljaju dve razlicite transakcije, tako da one imajusvoj identitet i predstavljaju entitete. Sa druge strane, iznosi za dve transakcije su verovatnoinstance nekog novcanog objekta. Ove vrednosti nemaju identitet jer se ne dobija nikakvakorist ukoliko mozemo da razlikujemo dve razlicite novcane instance koje imaju istu vrednost.Dva objekta mogu imati isti identitet i ukoliko nemaju iste atribute i cak ne moraju dapredstavljaju istu klasu. Kada korisnik svodi racun svoje cekovne knjizice sa izvodom koji jedobio iz banke, njegov zadatak je upariti transakcije iz cekovne knjizice i bankovnog izvodakoje imaju isti identitet, iako su one unesene od strane razlicitih ljudi, u razlicito vreme. Brojceka sluzi kao jedinstveni identifikator, bilo da se uparivanje vrsi kompjuterskim programomili rucno. Uplate i isplate novca, koje nemaju identifikacioni broj, mogu biti varljive, ali istiprincip vazi: svaka transakcija je entitet (koja se pojavljuje u najmanje dva oblika).

Uobicajeno je da identitet bude koriscen izvan odredenog softverskog sistema, kao uslucaju sa bankovnim transakcijama. Nekada je identitet bitan samo u kontekstu sistema,kao sto je to u primeru identiteta procesa operativnog sistema.

Kada se objekat izdvaja po svom identitetu, radije nego po svojim atributima, potrebnoje ugraditi ovo pravilo u definiciju modela. Takode, potrebno je definisati operaciju, koja

11

Page 13: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

garantovano vraca jedinstveni rezultat za svaki objekat, verovatno dodavanjem simbola, kojije garantovano jedinstven. Ovo znaci da identifikator moze doci izvan sistema.

Identitet nije unutrasnja stvar u sistemu. To je vestacki uvedeno znacenje, jer je korisno.U stvari, ista stvar u realnom zivotu moze biti, ali i ne mora, biti predstavljena kao entitetu modelu domena.

Aplikacija za rezervisanje sedista na stadijumu moze tretirati sedista i goste kao entitete.U slucaju kada se kupcu (korisniku) dodeljuje broj sedista, ono postaje entitet. Njegovidentifikator je u tom slucaju broj sedista koji je jedinstven na stadijumu. Sediste moze daima ostale atribute, kao sto je njegova lokacija, da li je pogled zaklonjen, cena i tako dalje, alijedino broj sedista, ili jedinstveni red i pozicija, mogu da se koriste za njegovu identifikaciju.

Sa druge strane, ukoliko je dogadaj tipa generalni ulaz, u smislu da vlasnik karte sedigde god ima slobodnog mesta, nema potrebe razlikovati pojedinacna sedista. Jedino je bitanukupan broj sedista. Iako je broj sedista fizicki i dalje urezan na sedistima, nije potrebnovrsiti evidenciju o njima kompjuterskim softverom. U tom slucaju, sedista ne predstavljajuentitete i jedinstveni identifikator nije potreban.

2.3.2 Vrednosni objekti

Veliki broj objekata nema konceptualni identitet. Ovi objekti opisuju karakteristike nekogobjekta.

Kada dete crta, njega zanima boja markera koja mu je potrebna ili koliko je vrh markeraostar. Ali, ukoliko postoje dva markera iste boje i istog oblika, detetu nece biti bitno kojimarker koristi od ta dva. Ukoliko mu se dogodi da marker izgubi, te zameni drugim iste boje,iz novog paketa, ono moze nastaviti sa crtanjem ne brinuci o tome sto je marker zamenjen.

Ukoliko pitamo dete o crtezima na frizideru, dete brzo moze da odvoji njegove crteze odcrteza koje je nacrtala njegova sestra. On i njegova sestra imaju korisne identitete, kao injihovi zavrseni crtezi. Zamislimo koliko bi komplikovano bilo ukoliko bi dete moralo da znaza svaku liniju na crtezu kojim markerom je nacrtana. Crtanje vise ne bi bilo decja igra.

Zato sto su najupadljiviji objekti u modelu obicno entiteti, i zato sto je bitno pratiti iden-titet svakog entiteta, prirodno je razmotriti opciju dodeljivanja identiteta svakom objektu udomenu. Postoje neka radna okruzenja koja upravo to i rade.

Sistem treba da se nosi sa pracenjem identiteta, i zbog toga nije moguce uvesti veliki brojoptimizacija performansi. Potreban je analiticki napor za definisanje smislenih identiteta irazraditi sigurne nacine za pracenje identiteta objekata u distribuiranim sistemima ili u bazipodataka. Jednako vazno, postavljanje vestackih identiteta dovodi u zabludu i komplikujemodel, forsirajuci sve objekte u isti kalup.

Pracenje identiteta entiteta je esencijalno, ali dodavanje identiteta na objekte kojimato nije potrebno moze skoditi performansama sistema i moze zamutiti model cineci da sviobjekti izgledaju isto.

Ukoliko razmisljamo o ovim objektima kao o objektima koji nemaju identitet, onda ihmozemo dodati u listu alata. U stvari, ovi objekti imaju svoje karakteristike i znacaj umodelu. Ovo su objekti koji opisuju stvari.

12

Page 14: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Objekat koji predstavlja opisan aspekt domena bez konceptualnog identiteta zove seVREDNOSNI OBJEKAT. Vrednosni objekti se instanciraju kako bi predstavili elementedizajna koji su nama bitni ne u smislu koga ili sta predstavljaju, vec u smislu njihove namene.

Boje su primer vrednosnih objekata koji se nalaze u osnovnim bibliotekama skoro svihrazvojnih sistema. Takodje, stringovi i brojevi su isto vrednosni objekti. Ovi osnovni pri-meri su jednostavni, ali vrednosni objekti ne moraju da budu takvi. Na primer, programkoji sadrzi paletu za mesanje boja moze imati bogat model u kome objekti boje mogu bitikombinovani kako bi dobili nove boje. Ovi objekti mogu imati kompleksne algoritme zamedusobnu saradnju koji omogucavaju kreiranje novih rezultujucih vrednosnih objekata.

Vrednosni objekat moze da bude sacinjen od drugih objekata. U softveru za dizajniranjeplanova kuca, objekat moze biti kreiran za svaki stil prozora. Stil prozora moze biti elementobjekta prozora, zajedno sa visinom i duzinom, kao i pravila koja upravljaju nacinom pro-mena vrednosti atributa i njihovim kombinovanjem. Prozori predstavljaju vrednosne objektesacinjene od drugih vrednosnih objekata. Takode, oni se ugraduju u vece elemente plana,kao sto su objekti zidovi.

Vrednosni objekti mogu referencirati entitete. Na primer, ukoliko upitamo servis mapa zarutu puta od Nisa do Beograda, moguce je izvesti objekat ruta povezivanjem Nisa i Beogradaautoputem E75. Objekat ruta predstavlja vrednosni objekat, iako referencira tri objekata isvaki je entitet.

Vrednosni objekti se cesto prosleduju kao parametri u porukama izmedu objekata. Onisu cesto prolazni – kreiraju se za neku operaciju i, potom, unistavaju. Vrednosni objekticesto se koriste kao atributi entiteta. Osoba moze biti modelirana kao entitet sa identitetom,ali ime osobe predstavlja vrednost.

Atributi koji cine vrednosni objekat trebalo bi da cine konceptualnu celinu. Na primer,ulica, grad i postanski kod ne bi trebalo da budu posebni atributi objekta osoba. Oni susvi deo objekta adresa, sto dalje pojednostavljuje objekat osoba i cini vrednosni objekatpovezanim.

Vrednosni objekti trebalo bi da budu nepromenljivi. Ukoliko je potrebno promeniti nekiatribut u vrednosnom objektu, potrebno je, onda, kreirati novi vrednosni objekat koji jeklon postojeceg objekta sa izmenjenom vrednoscu. Ovo pojednostavljuje programski kod irukovanje vrednosnim objektima.

2.3.3 Servisi

U nekim situacijama, najcistiji i najprakticniji dizajn ukljucuje operacije koje konceptu-alno ne pripadaju nijednom objektu. Umesto da forsiramo problem, mozemo pratiti njegovekonture i uvesti servise u modelu.

Postoje bitne operacije u domenu koje ne mogu naci prirodno mesto u entitetu ili vred-nosnom objektu. Neke od njih sustinski su aktivnosti ili akcije, nisu objekti, ali kako jeobjektna paradigma najvise u upotrebi, programeri pokusavaju da ih smeste u objekte.

Cesta greska je prerano odustajanje od ugradivanja ponasanja u odgovarajuci objekat, jerse time postepeno klizi prema proceduralnom programiranju. Takode, kada se operacija, kojane odgovara definiciji objekta forsirano ubaci u objekat, on gubi svoju konceptualnu jasnocu

13

Page 15: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

i postaje tesko razumljiv i odrziv. Kompleksne operacije mogu lako pretrpati jednostavneobjekte skrivajuci njegovu pravu ulogu. Zato sto te operacije cesto okupljaju vise objekatadomena, ukljucuju njihovo koordinisanje i pustanje u pogon, dodatna odgovornost kreiradodatne zavisnosti izmedu svih tih objekata, komplikujuci koncepte koji bi trebalo da budunezavisno shvaceni.

Ponekada se servisi maskiraju kao objekti modela, pojavljuju se kao objekti bez drugogznacenja osim sto predstavljaju operaciju koju izvrsavaju. Ovakvi “izvrsioci” obicno dobijajunazive koji se zavrsavaju sa “Manager”. Oni nemaju stanje niti bilo kakvo znacenje u domenuizvan operacije koju sadrze. Ipak, ovo resenje, barem, daje dom razlicitim ponasanjima bezmesanja u objekte realnog modela.

Neke koncepte iz domena nije prirodno modelirati kao objekte. Forsiranjem da odgova-rajuca funkcionalnost bude odgovornost entiteta ili vrednosnih objekata narusava se definicijaobjekta i rezultuje dodavanjem besmislenih vestackih objekata.

Servis je operacija, koja se nudi kao interfejs koji je nezavisan u modelu, bez enkapsuli-ranja stanja. Servisi su uobicajen patern u tehnickim radnim okruzenjima, ali oni, takode,mogu da se primene i na sloj domena.

Ime servisa oznacava vezu sa drugim objektima. Za razliku od entiteta i vrednosnihobjekata, servisi su definisani u terminima akcija koje mogu izvrse za klijenta. Preciznije,servisima se daju imena na osnovu aktivnosti koje oni vrse, i to je razlog sto su oni, najcesce,naslovljeni glagolom, a ne imenicom. Servis bi i dalje trebalo da ima definisanu odgovoronost,te da zajedno sa njom i interfejsom, koji ga cine, bude definisan kao deo modela domena.Imena operacija treba da proisticu iz biznis-jezika ili ukoliko taj termin ne postoji potrebnoga je, onda, dodati u biznis jezik. Parametri i rezultati su objekti domena.

Servise treba koristiti promisljeno kako ne bi dopustili curenje logike iz entiteta i vred-nosnih objekata. Kada je operacija bitan koncept u domenu, servis predstavlja prirodni deodizajna. Deklarisanjem operacije kao servisa, umesto kreiranja laznog objekta u domenukoji zapravo ne predstavlja nista, cini kod jednostavnim za razumevanje.

Dobro definisani servis ima sledece tri osobine:

1. Operacija se odnosi na koncept u domenu koji nije prirodni deo entiteta ili vrednosnogobjekta.

2. Interfejs je definisan u terminima elemenata modela domena.

3. Operacija nema stanje.

Bez stanja, ovde znaci da bilo koji klijent moze koristiti bilo koju instancu odredenogservisa, bez brige o istoriji njenog koriscenja. Izvrsavanje servisa koristi informacije kojesu globalno dostupne, servisi mogu cak i promeniti globalne informacije, ali servisi ne drzestanje koje moze uticati na njihovo ponasanje, kao sto moze vecina objekata u domenu.

2.3.3.1 Upotreba servisa i razlicite vrste servisa

Do sada smo se fokusirali samo na servise koji se koriste u sloju domena, ali servisi sekoriste i na drugim mestima. Potrebno je voditi racuna i razlikovati servise koji pripadaju

14

Page 16: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

u sloju domena od onih u drugim slojevima i potrebno je rasporediti odgovornosti kako bito razdvajanje bilo ostro.

Vecina servisa koji su navedeni u litaraturi su cisti tehnicki servisi i pripadaju infrastruk-turnom sloju. Servisi domena i aplikacioni servisi saraduju sa infrastrukturnim servisima.Na primer, banka moze imati aplikaciju koja salje e-mail klijentu kada njegov balans padneispod odredene granice. Interfejs koji okruzuje e-mail sistem, predstavlja servis u infrastruk-turnom sloju.

Moze biti teze razlikovati aplikacione servise od servisa domena. Bankarska aplikacijamoze biti odgovorna za transfer novcanih sredstava. Ukoliko je servis osmisljen da napraviodgovarajuca zaduzenja i uplate sredstava u transferu, ta odgovornost pripada sloju domena.Transfer novcanih sredstava ima znacenje u biznis jeziku banke i ukljucuje fundamentalnubiznis logiku. Tehnicki servisi ne bi trebalo da imaju nikakvo biznis znacenje.

Veliki broj servisa domena i aplikacionih servisa je izgradeno nad populacijom entitetai vrednosnih objekata, ponasajuci se kao skripte koje organizuju podencijal domena kakobi se odradila neka konkretna akcija. Entiteti i vrednosni objekti cesto su previse dobrogradeni kako bi obezbedili konvencionalni pristup mogucnostima sloja domena. Tako dobi-jamo veoma lepu liniju razdvajanja odgovornosti izmedu sloja domena i aplikacionog sloja.Na primer, ukoliko bankarska aplikacija ima mogucnost konvertovanja i eksportovanja trans-akcija u eksel fajl, onda je za taj eksport zaduzen aplikacioni servis. Ne postoji biznis znacajda formiranje eksel fajlova bude u domenu bankarstva, i ne postoje biznis pravila ukljucenau to.

Sa druge strane, opcija za prenos nocanih sredstava sa jednog racuna na drugi racunpripada servisu domena zato sto sadrzi znacajna biznis pravila (zaduzivanje i uplacivanjenovcanih sredstava sa jednog na drugi racun) i zato “transfer sredstava” predstavlja smislenibankarski termin. U ovom slucaju, servis ne sadrzi mnogo logike. Potrebno je samo izvrsitiinstrukcije nad dve instance objekta racuna koje obavljaju vecinu posla. Ali, ako bismostavili operaciju transfera u objekat racuna bilo bi cudno, zato sto operacija ukljucuje dvaracuna i jos neka globalna pravila.

Moguce je kreirati objekat novcanog transfera kako bismo predstavili dva entiteta pluspravila i istoriju transfera. Ali, i dalje nam ostaju pozivi servisa u medubankarskoj mrezi.Povrh toga, u vecini razvojnih sistema, cudno je napraviti direktan interfejs izmedu objekatadomena i eksternih resursa. Mozemo prerusiti takve eksterne servise fasadom koja preuzimaulaze u terminima modela i vraca objekte transfera novca kao rezultat. Ali, koje god po-srednike da koristimo, cak i da oni pripadaju internom sistemu, ti servisi nose odgovornostvan domena transfera novca.

Podela servisa u slojeve na osnovu bankarskog primera:

• Aplikacioni sloj - Aplikacioni servis za transfer novcanih sredstava obavlja sledecezadatke.

– Parsira ulaz (npr. XML zahtev).

– Salje poruku servisu domena za obavljanje zadatka.

– Ceka na potvrdu.

15

Page 17: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

– Odlucuje da li je potrebno poslati poruku upotrebom infrastrukturnog servisa.

• Sloj domena - Servis domena zaduzen za transfer novcanih sredstava obavlja sledecezadatke:

– Saraduje sa objektima racuna tako sto pravi odgovarajuca zaduzenja i uplate.

– Vraca poruku o rezultatu operacije (transfer je dozvoljen ili nije).

• Infrastrukturni sloj - Infrastrukturni servis salje poruku sa obavestenjem.

– Salje e-mail, pismo, i ostale vrste komunikacije odredene od strane aplikacije.

2.3.3.2 Granularnost

Ovaj patern, takode, je vredan u smislu kontrolisanja granularnosti pristupa interfejsimasloja domena, kao i razdvajanja klijenata od entiteta i vrednosnih objekata.

Srednje granulisani servisi bez stanja mogu se lako ponovo upotrebiti u velikim sistemimazato sto enkapsuliraju znacajnu logiku iza jednostavnog interfejsa. Detaljno granulisaniservisi mogu voditi ka neefikasnom prosledivanju poruka u distribuiranim sistemima i mogudoprineti curenju znanja iz domena u aplikacioni sloj. Nasuprot tome, razumno uvodenjeservisa domena moze pomoci odrzavanju jasne linije izmedu slojeva.

2.3.4 Dogadaji domena

Ovaj patern nije originalno uveden u knjigu Erika Evansa [1], ali i on je sam rekao dazali zbog toga i da ovaj patern, takode, predstavlja osnovni gradivni element DDD-a.

DDD prakticionisti su otkrili da mogu bolje razumeti domen problema ucenjem o dogadajimakoji se desavaju u okviru domena.

Odgovornost entiteta je pracenje njegovog stanja kroz njegov zivotni vek. Ali, ukolikoje potrebno znati uzrok promene stanja, moze biti tesko objasniti kako je sistem dosao dotrenutnog stanja. Audit tragovi mogu omoguciti pracenje, ali oni, obicno, nisu namenjeni zaupotrebu u implementaciji biznis logike sistema. Istorija promena entiteta moze dopustitipristup prethodnim stanjima, ali one ignorisu znacenje tih promena.

Poseban, povezan skup problema izranja u distribuiranim sistemima. Stanje distribuira-nih sistema ne moze da bude konzistentno sve vreme. Agregati se drze cvrsto sve vreme, dokse ostale promene desavaju asinhrono. Kako se promene propagiraju kroz cvorove u mrezi,moze biti tesko resiti visestruka azuriranja koja stizu izvan redosleda ili iz razlicitih izvora.

Dogadaji domena oznacavaju nesto sto se dogodilo u domenu, a sto je bitno za biznis.Moguce je koristiti dogadaje kako bi se snimile promene na modelu u audit stilu, ili kori-stiti dogadaje za komunikaciju izmedu agregata. Cesto operacija na jednom agregatu mozerezultovati bocnim efektima koji su izvan granica agregata. Drugi agregati u modelu moguosluskivati odredene dogadaje i preduzeti odredene akcije.

Na primer, razmotrimo korpu u okviru sajta za elektronsku kupovinu. Svaki put kadakupac smesti stavku u korpu, bitno je azurirati preporucene proizvode koji su prikazani na

16

Page 18: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

sajtu. U ovom scenariju, kada se dogadaj domena podigne sa detaljima korpe, preporuke zakupca se menjaju. Bez dogadaja u domenu, potrebno je eksplicitno spojiti modul korpe samodulom preporucenih proizvoda. Dogadaji u domenu daju prirodniji nacin komunikacije ifokusiraju se na vreme.

Dogadaji su obicno nepromenljivi, buduci da su oni samo zapis necega sto se dogodilo uproslosti. Dodatno, dogadaji domena, tipicno, sadrze podatak vremenu stvaranja dogadajai identitete entiteta ukljucenih u dogadaj.

Kada se dogadaji isporuce zainteresovanim strankama, lokalnim ili eksternim sistemima,oni se koriste kako bi se ispunila eventualna konzistentnost u nekim sistemima. Upotrebomdogadaja moguce je ukloniti potrebu za globalnim transakcijama kada se komunicira saeksternim sistemima.

Dogadaji domena obicno se identifikuju prilikom razgovora sa biznis ekspertom. Bitnoje obratiti paznu na reci koje pocinju sa: “Kada...”, “Ukoliko se dogodi...”, “Obavesti meukoliko...”, i slicne. Zavisno od organizacione kulture moguce je imati drugacije fraze.

2.3.5 Moduli

Moduli su stari, dobro utvrdeni dizajn-elementi. Postoje i tehnicki razlozi za uvodenjemodularnosti, ali primarna motivacija za to je kognitivno preopterecenje. Moduli dozvolja-vaju dva pogleda na model: moguce je pogledati detalje modula kao samostalnu celinu, ali ivideti relacije izmedu modula bez zadiranja u detalje svakog od njih. Moduli u sloju domenatreba da nastanu kao smisleni deo modela, pricajuci o domenu na vecem obimu.

Svi koriste module, ali mali broj programera ih tretira kao punopravne clanove modela.Kod se razbija na razlicite nacine u kategorije, od pogleda na tehnicku arhitekturu sistemado pogleda na konkretne programerske zadatake. Cak i programeri, koji dosta refaktorisukod teze da module, kreirane u ranoj fazi projekta, ostave netaknutim.

Istina, trebalo bi imati slabu zavisnost izmedu modula i veliku saradnju unutar njih.Zahtev za slabu meduzavisnost izmedu modula i veliku saradnju objekata unutar modula,mozda, zvuci kao tehnicka metrika koja bi se odredivala mehanicki zasnovano na raspodelamaveza i medusobnim interakcijama. Ipak, ne deli se samo kod u module, vec se dele i koncepti.Postoji granica o tome koliko stvari osoba moze imati u glavi u istom trenutku (zbog togaje i zahtev za slabu meduzavisnost).

Slaba meduzavisnost i velika saradnja su generalni dizajn-principi koji se primenjuju naindividualne objekte i module, ali oni su posebno bitni za modeliranje na visem nivou.

Kadgod se dva elementa u modelu razdvoje u razlicite module, veza izmedu njih postanemanje direktna, sto povecava poteskoce pri razumevanju njihovog mesta u dizajnu. Malazavisnost izmedu modula minimizira ove poteskoce i omogucava analiziranje sadrzaja jednogmodula sa minimalnim referenciranjem na druge module sa kojima saraduje.

U isto vreme, elementi dobrog modela imaju sinergiju, i dobro odabrani moduli spajajuzajedno elemente modela sa bogatim konceptualnim vezama. Ova velika saradnja objektasa povezanim odgovornostima omogucava da se posao modeliranja i dizajniranjdizajniranjakoncentrise unutar jednog modula, skala kompleksnosti kojom ljudski mozak moze lako darukuje.

17

Page 19: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Moduli i manji elementi trebalo bi da zajedno evoluiraju, ali, tipicno, to nije slucaj.Moduli se biraju kako bi organizovali rani izgled objekata. Kasnije, objekti teze da se pro-mene tako da i dalje ostanu u granicama definicije modula. Refaktorisanje modula pred-stavlja vise posla i opasnije je od refaktorisanja klasa i ,verovatno, ne moze biti tako cestosprovodeno. Kao sto objekti modela teze da startuju naivno i konkretno, gde se onda poste-peno transformisu kako se znanje prosiruje, tako moduli mogu postati suptilni i apstraktni.Dopustanjem da moduli odslikavaju promene razumevanja domena daje se vise slobodeobjektima da evaluiraju unutar modula.

Kao i sve drugo u DDD-u moduli predstavljaju mehanizam komuniciranja. Znacenjeda su objekti razdvojeni, mora diktirati izbor njihovih modula. Kada stavimo neke klasezajedno u modulu, mi porucujemo sledecem programeru koji gleda u dizajn, da misli o njimau celini. Ako model pripoveda pricu, onda moduli predstavljaju poglavlja u toj prici. Nazivmodula saopstava njegovo znacenje. Imena modula su obicno zasnovana na biznis terminima,na primer: klijentski modul.

2.4 Zivotni vek objekata u domenu

Svaki objekat ima zivotni vek. Objekat se stvara, prolazi kroz razna stanja i na kraju seunistava tako sto se arhivira ili brise. Naravno, veliki broj njih su jednostavni prolazni objektikreirani jednostavnim pozivom njihovih konstruktora, upotrebljeni u nekom izracunavanju ina kraju obrisani iz memorije. Ne postoji potreba za komplikovanjem takvih objekata, alineki objekti imaju duzi vek trajanja i veci deo svog zivota ne provode aktivni u memoriji. Oniimaju kompleksne meduzavisnosti sa ostalim objektima i prolaze kroz razlicita stanja tokomsvog zivotnog veka. Rad sa takvim objektima predstavlja izazov koji moze lako izbaciti izkoloseka programera koji primenjuje DDD.

Dijagram 3: Zivotni vek objekta u aplikaciji

Izazove mozemo podeliti u dve kategorije:

18

Page 20: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

1. Odrzavanje integriteta kroz zivotni vek.

2. Sprecavanje da model bude “progutan” komplikacijama odrzavanja njegovog zivotnogveka.

U daljem tekstu opisujemo resenje ovih problema kroz tri paterna. Prvo, agregati ve-zuju model definisanjem vlasnistva i granica, izbegavanjem implementiranja umrsene mrezeobjekata. Ovaj patern je krucijalan za odrzavanje integriteta u svim fazama zivotnog veka.

Sledece, upotrebom fabrika za kreiranje i rekonstituisanje kompleksnih objekata i agre-gata, kao i cuvanjem interne strukture skrivenom od klijenta, fokus se stavlja na pocetakzivotnog veka.

Na kraju, repozitorijumi adresiraju sredinu i kraj zivotnog ciklusa, pruzajuci sredstva zapronalazenje i preuzimanje uskladistenih objekata skrivajuci infrastrukturu iza svog inter-fejsa.

2.4.1 Agregati

Recimo da brisemo objekat osoba iz baze podataka. Zajedno sa osobom, nestaje ime,datum rodjenja i opis posla. Ali, sta je sa adresom? Mogu postojati i druge osobe sa istomadresom. Ukoliko obrisemo adresu, ostali objekti klase osoba imace reference na izbrisaniobjekat. Ukoliko ipak ne obrisemo adresu, onda akumuliramo nepotrebne podatke o adre-sama u bazi podataka. Automatsko sakupljanje smeca moze eliminisati nepotrebne adrese,ali to je tehnicka popravka, i ukoliko je dostupno u sistemu za upravljanje bazom podataka,ignorise se osnovni problem modeliranja. Cak i kada posmatramo izolovanu transakciju,mreza relacija tipicnog objektnog modela ne daje jasnu granicu potencijalnog efekata pro-mene. Nije prakticno ponovo ucitati svaki objekat u sistemu (za svaki slucaj), radi ponovneprovere, ukoliko postoji neka veza sa izmenjenim objektom. Problem je akutan u sistemusa konkurentnim pristupom istim objektima od strane vise klijenata. Kako vise korisnikaistovremeno koriste i azuriraju razlicite objekte u sistemu moramo zabraniti istovremene pro-mene meduzavisnih objekata. Postavljanje pogresnog obima promene moze imati ozbiljneposledice.

Tesko je garantovati konzistentnost promena nad objektima u modelu sa kompleksnimmedusobnim vezama. Invarijante moraju da se odrzavaju na taj nacin da se primenjujuna usko povezane grupe objekata, a ne samo na pojedinacne objekte. Takode, seme za-kljucivanja mogu izazvati medusobno ometanje korisnika i zbog toga je moguce da sistembude nestabilan.

Pogledajmo to iz drugog ugla, kako znamo gde pocinje i gde se zavrsava objekat kojije sacinjen od drugih objekata? U svakom sistemu sa trajnim snimanjem podataka, morapostojati okvir za transakciju koja menja podatke i nacin za odrzavanje konzistentnostipodataka (tj. odrzavanje njihovih invarijanti). Baze podataka dopustaju razlicite semezakljucavanja i moguce je kreirati testove kako bi se osiguralo da sve radi. Ali, takva brzinskaresenja samo skrecu paznju sa modela, i ubrzo se vracamo hakovanju sistema.

U stvari, potrebno je dublje razumevanje domena od strane programera kako bi sepronaslo balansirano resenje za ovakve vrste problema dodavanjem faktora kao sto je ucestalost

19

Page 21: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

promena instanci odredenih klasa. Potrebno je pronaci model koji ostavlja tacke slabe sa-radnje labavim i cini invarijante zategnutijim. Resenje treba da ucini model jednostavnijimza razumevanje i da pojednostavi komunikaciju u dizajnu. Kako ne postoje jasno definisanegranice modela, problem, koji se sada javlja, odnosi se na formiranje transakcija.

Razvijeni su razni obrasci za definisanje vlasnickih relacija u modelu. Sledeci jednostavanali rigorozan sistem, destilise koncepte, ukljucuje skup pravila za implementiranje transakcijakoje mogu promeniti svojstva objekata i njihovih vlasnika.

Prvo potrebna nam je apstrakcija za enkapsulaciju referenci u okviru modela. Agregatje grupa povezanih objekata koje tretiramo kao jedinica u svrsi izmene podataka. Svakiagregat ima svoj koren i granicu. Granica definise sta je unutar agregata. Postoji samo jedankoren agregata, tj. poseban entitet odreden kao agregat. Koren je jedini clan agregata kojispoljasnji elementi mogu da referenciraju, objekti u okviru granice mogu imati medusobnereference. Entiteti koji nisu koren agregata imaju lokalni identitet, ali taj identitet vazijedino u okviru agregata, zato sto spoljni objekti nikada ne mogu da ih vide izvan kontekstakorena agregata.

Model automobila moze biti upotrebljen u softveru za mehanicarsku radnju. Kola pred-stavljaju entitet sa globalnim identitetom. Mi zelimo da razlikujemo taj auto od ostalihautomobila na svetu, cak, i sa onima koji su veoma slicni. U ovom slucaju, mozemo upo-trebiti broj sasije. Mozda zelimo da pratimo istoriju rotiranja guma kroz 4 pozicije, mozda,zelimo da znamo kilometrazu i iskoriscenost svake gume. Kako bismo razlikovali gume, i onemoraju predstavljati entitete, takode. Ali, veoma je malo verovatno da zelimo da brinemoo identitetu tih guma izvan konteksta specificnog automobila. Ukoliko zamenimo gume iposaljemo stare na recikliranje, ili nas softver to nece pratiti, ili ce one postati anonimniclanovi neke gomile guma. Niko nece brinuti o njihovoj istoriji rotiranja. Cak, i onda kadasu ugradene na auto, niko nece vrsiti upit nad sistemom kako bi prvo nasao odredenu gumu,a nakon toga pogledao u kojim kolima je guma ugradena. Korisnici ce izvrsiti upit u bazuda pronadju auto i onda ce pitati za prolaznu referencu na gume. Stoga, automobil predsta-vlja koren agregata, cije granice obuhvataju gume. Sa druge strane, blokovi motora imajuserijske brojeve ugravirane na njima i ponekada se prate nezavisno od automobila. U nekimaplikacijama, motor moze biti koren svog sopstvenog agregata.

Invarijante, koje predstavljaju pravila doslednosti moraju biti odrzavane kadgod se pro-mene podaci, ukljucujuci veze izmedu clanova agregata. Za pravila koja se prostiru izmeduagregata ne mozemo ocekivati da budu azurna sve vreme. Procesiranjem dogadaja, serijskimprocesiranjem, ili upotrebom drugih mehanizama azuriranja, zavisnosti mogu biti resene uokviru nekog predodredenog vremenskog perioda. Ali, invarijante primenjene u okviru agre-gata moraju biti kompletirane u okviru jedne transakcije.

Ukoliko konceptualni agregat prevedemo u implementaciju, postoje pravila koja morajuda se ispune prilikom svake transakcije:

• Entiteti koji se nalaze u korenu agregata imaju globalni identitet i odgovorni su zaproveravanje invarijanti.

• Entiteti u korenu imaju globalni identitet. Entiteti unutar granice imaju lokalni iden-titet, jedinstveni su jedino u okviru agregata.

20

Page 22: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Nista izvan granice agregata ne moze drzati referencu ni na jednom objektu unutaragregata, izuzev korena agregata. Koren agregata moze prepustiti reference internihentiteta drugim objektima, ali ti objekti mogu da ih koriste jedino prolazno, i oni nemogu drzati prave reference. Koren moze vratiti kopiju u formi vrednosnog objektadrugom objektu, i nije bitno sta moze da mu se desi, zato sto je to samo vrednosniobjekat i vise nema nikakvu povezanost sa agregatom.

• Kao posledica prethodnog pravila, jedino koreni agregata mogu biti preuzeti direktnoiz baze podataka. Svi ostali objekti moraju biti preuzeti kroz asocijacije.

• Objekti u okviru agregata mogu imati reference na druge agregate.

• Operacija brisanja mora obrisati sve u okviru granice agregata odjednom.

• Kada se promena nad bilo kojim objektom unutar agregata komituje, sve invarijanteceline agregata moraju biti ispunjene.

2.4.2 Fabrike

Kada kreiranje objekta, ili celog agregata, postane komplikovano ili previse otkriva stuk-turu, fabrike pruzaju dobro resenje za enkapsulaciju.

Veliki deo snage objekata sastoji se u slozenoj konfiguraciji njihovih internih delova injihovih medusobnih veza. Problemi nastaju kada se kompleksni objekti preopterete logikomza svoje kreiranje.

Motor automobila je komlikovana masinerija. On sadrzi nekoliko desetina delova, kojisaraduju zajedno, kako bi odradili odgovornost motora – da okrece radilicu. Mozemo zami-sliti motor koji moze sam da ugradi svecice na blok motora, ubaci klipove u svoje cilindrei da sve to zasrafi zajedno. Ali, izgleda neverovatno da ce takva komplikovana masinerijabiti pouzdana ili efikasna kao danasnji tipicni motori. Umesto toga, mi prihvatamo da namnesto ili neko drugi sastavi delove motora zajedno. Mozda je to mehanicar ili industrijskirobot. I robot i mehanicar su u stvari kompleksniji od samog motora kojeg oni sastavljaju.Posao sastavljanja delova je kompletno nepovezan sa poslom okretanja radilice. Funkcijamontazera traje jedino dok se proizvodi automobil, nije nam potreban robot ili mehanicarkada je automobil u voznom stanju. Zato se automobili nikada ne sastavljaju i voze u istovreme. Ne postoji vrednost u kombinovanju obe funkcije u isti mehanizam. Isto tako, sasta-vljanje kompleksnog objekta je posao za koji bi najbolje bilo da bude odvojen od funkcijeobjekta koju ce on imati kada se konstruise.

Pomeranje odgovornosti kreiranja na klijentski objekat, vodi ka vecim problemima. Kli-jent zna koji posao treba da obavi i oslanja se na objekte domena kako bi odradili neophodnaizracunavanja. Ukoliko klijent treba da kreira/konstruise objekat domena koji mu je potre-ban, onda mora nesto znati i o internoj strukturi tog objekta. Kako bi osigurao da invarijantebudu zadovoljene, klijent mora znati i neka pravila objekta. Cak i pozivanje konstrukturavezuje klijenta na konkretne klase objekta koje on kreira. Nije moguce promeniti objekatdomena bez promene u klijentu, cineci refaktorisanje i odrzavanje tezim.

21

Page 23: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Klijent koji preuzima odgovornost kreiranja objekta postaje nepotrebno komplikovan ito zamagljuje njegovu odgovornost. Probija enkapsulaciju objekata domena i agregata kogakreira. Cak i gore, ukoliko je klijent deo aplikacionog sloja, onda odgovornosti domena cure uaplikacioni sloj. Ovo cvrsto oslanjanje aplikacije na detalje implementacije domena odbacujenajvecim delom dobrobiti koje dobijamo apstrakcijom u sloju domena i cini buduce promeneveoma skupim.

Kreiranje objekta moze biti posebna operacija, ali kompleksne operacije kreiranja nisuodgovornost kreiranih objekata. Kombinovanje tih odgovornosti moze proizvesti nezgrapandizajn koji je tesko razumeti. Ukoliko klijent direktno konstruise objekte, onda zamaglju-jemo klijenta, krsimo enkapsulaciju objekta koji se pravi i previse vezujemo implementacijuklijenta na implementaciju kreiranog objekta.

Kreiranje kompleksnih objekata je odgovornost sloja domena. Ipak taj zadatak ne pri-pada objektima koji predstavljaju model. Postoje slucajevi u kojima je kreiranje objekta injegovo konstruisanje veoma bitno za domen, kao sto je otvaranje bankovnog racuna. Ali,kreiranje objekta i njegovo konstruisanje obicno nema nikakvo znanje u domenu; neophodnoje jedino za implementaciju koda. Kako bismo resili problem, moramo dodati nove konstruk-cije u domenu. Ovo odstupa od definicije prethodnih elemenata i veoma je bitno naglasitii odrzati poentu cistom: u dizajn dodajemo elemente koji ne odgovaraju nicemu u modelu,ali koji su i, pored toga, deo odgovornosti sloja domena.

Svaki objektno orijentisani jezik pruza mehanizam za kreiranje objekata (konstruktori),ali potreban je absktraktniji mehanizam konstrukcije koji nije vezan za postojece objekte.Element programa, cija je odgovornost kreiranje drugih objekata, nazivamo Fabrika.

Dijagram 4: Osnovna interakcija sa fabrikom

Kao sto interfejs objekta skriva njegovu implementaciju, dopustajuci klijentu da koristiponasanja objekta bez znanja kako je to ucinjeno, tako fabrika sakriva znaje potrebno zakreiranje kompleksnih objekata ili agregata. Fabrika pruza interfejs koji zadovoljava ciljeveklijenta i pruza apstraktni pogled na kreirani objekat.

Postoji vise nacina dizajniranja i implementiranja fabrika. Nekoliko paterna specijalnodizajniranih za kreiranje objekata (Factory Method, Abstract Factory i Builder) temeljno suprouceni u knjizi o dizajn-paternima [16]. Ta knjiga detaljno izucava paterne cak i za najtezeprobleme konstrukcije objekata. Trenutno, cilj nije ici duboko u dizajniranje fabrika, vec jepokazati da su fabrike bitne komponente domena. Pravilna upotreba fabrika moze pomociodrzavanju DDD dizajna u pravom smeru.

22

Page 24: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dva osnovna zahteva za bilo koju dobro dizajniranu fabriku su:

1. Svaki konstruktorski metod je atomican i zahteva da sve invarijante kreiranog objektabudu zadovoljene. Fabrika bi trebalo da kreira samo objekte koji se nalaze u konzi-stentnom stanju. Za entitet, ovo znaci kreiranje celog agregata gde su sve invarijantezadovoljene. Za nepromenljive vrednosne objekte, ovo znaci da su svi atributi ini-cijalizovani na njihovo pravo i finalno stanje. Ukoliko nije moguce kreirati korektnozahtevani objekat, onda je potrebno baciti izuzetak ili implementirati neki drugi me-hanizam, koji bi se pozvao, ne bi li se osigurao povratak korektnog objekta.

2. Povratna vrednost fabrike treba da bude apstrakcija tipa, koju klijent zahteva, a nekonkretna klasa, koja se kreira.

2.4.3 Repozitorijum

Ukoliko zelimo da radimo nesto sa objektom, potrebno je da imamo referencu na njega.Kako dobiti referencu? Jedan nacin je kreirati objekat, kako bi operacija kreiranja moglada vrati referencu na novi objekat. Obilaskom objekta, takode, mozemo dobiti reference.Startujemo sa objektom, koga vec znamo i pitamo za povezane objekte. Svaki objektnoorijentisani program radi mnogo ovakvih stvari. Te veze daju objektnim modelima punoizrazajne snage. Ali, potrebno je dobiti prvi objekat.

Pretraga po bazi podataka je globalno dostupna i omogucava direktan pristup objektu.Nema potrebe da svi objekti budu medusobno povezani, sto nam omogucava da drzimomrezu objekata pogodnom za rukovanje. Bilo da koristimo obilazak ili koristimo pretragu,to je biznis odluka. Da li bi trebalo da objekat korisnika drzi kolekciju svih kupovina kojeje napravio? Ili bi trebalo da narudzbine budu pronadene iz baze podataka, sa pretragompo ID kupca? Odgovarajuca kombinacija pretrage i udruzivanja cini dizajn razumljivim.Nazalost, programeri obicno ne razmisljaju mnogo o takvim finocama u dizajnu, zato sto oniplivaju u moru mehanizama koji omogucavaju skladistenje objekata, njihovo vracanje nazadi, eventualno, brisanje iz baze podataka.

Sa tehnicke strane gledano, preuzimanje uskladistenog objekta je stvarno podskup krei-ranja zato sto se podaci iz baze koriste za kreiranje novog objekta. Ali, konceptualno, ovo jesredina zivotnog veka objekta. Objekat korisnika ne predstavlja novog korisnika zato sto smoga mi uskladistili u bazu podataka i opet preuzeli. Kako bismo ovo predstavili razlicito umislima, bolje je uvesti termin za kreiranje instance iz skladista kao sto je to rekonstituisanje.

Cilj DDD-a je kreirati bolji softver fokusiranjem na model domena pre nego na tehnolo-giju. Dok programer konstruise SQL upit, prosledi ga servisu za upite u infrastrukturnomsloju, dobije redove tabele kao rezultat, preuzme neophodne informacije i prosledi ih kon-struktoru ili fabrici, fokus na model nestaje. Postaje prirodno razmisljati o objektima kaokontejnerima za podatke koje upiti obezbeduju. Tada, ceo dizajn se pomera prema stiluprocesiranja podataka. Detalji o tehnologiji variraju, ali problem ostaje da se klijent bavitehnologijom, umesto konceptima modela. Infrastruktura kao sto su slojevi mapiranja poda-taka dosta pomaze, praveci jednostavniju konverziju rezultata upita u objekte, ali programeri dalje misli o tehnickim mehanizmima, a ne o domenu. Jos gore, klijentski kod koristi bazu

23

Page 25: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

podataka direktno, programeri pokusavaju da zaobidu karakteristike modela kao sto su agre-gati, ili cak enkapsulaciju objekata. Umesto toga direktno pricaju i rade sa podacima koji suim potrebni. Sve vise i vise pravila domena postaju ugradena u kod upita ili se jednostavnogube. Objektne baze podataka eliminisu problem konverzije, ali mehanizmi pretrage su,obicno, i dalje mehanicki pa programeri, cesto, dolaze u iskusenje da preuzmu objekte kojeoni zele.

Klijentu je potrebno prakticno znanje kada zahteva referencu na postojece objekte do-mena. Ukoliko infrastruktura to omogucava, programeri klijentstkog koda lako mogu dodatinove objekte bazirane na vezama sa preuzetim objektima, sto na kraju samo jos vise zama-gljuje model. Sa druge strane, oni mogu koristiti upite kako bi dobili trazene podatke kojisu im potrebni iz baze podataka, ili mogu da preuzmu nekoliko specificnih objekata, umestoda koriste navigaciju kroz agregate. Na taj nacin, logika domena seli se u upite i klijentskikod, entiteti i vrednosni objekti postaju samo kontejneri podataka. Tehnicka kompleksnostprimene pristupa bazi podataka brzo zamenjuje klijentski kod, sto programere navodi na toda od sloja domena naprave dubriste. Sve to, na kraju, cini model domena nebitnim.

Izvlacenjem dizajn principa, o kojima smo vodili polemiku do sada, uz pretpostavkuda smo pronasli metod za pristup koji odrzava fokus na model dovoljno ostar da uposliove principe, uocavamo da nacin smanjenja podrucja problema pristupa objektima postoji.Za pocetak, nije potrebno da brinemo o prolaznim objektima. Prolazni objekti (tipicnovrednosni objekti) imaju kratak vek, i koriste se u klijentskoj operaciji, koja ih kreira ionda odbacuje. Takode, nisu nam potrebni upiti za uskladistene objekte koje je lakse naciobilaskom objekta. Na primer, adresa osobe zahteva se iz samog objekta osoba. I najbitnije,bilo kom objektu koji je interni za agregat zabranjen je pristup izuzev obilaskom pocevsi odkorena agregata.

Uskladisteni vrednosni objekti obicno se nalaze obilaskom, kada se krene od odredenogentiteta, koji se ponasa kao koren agregata, koji ga enkapsulira. U stvari, globalna pretragavrednosnih objekata cesto je besmislena, zato sto je trazenje vrednosnog objekta po njegovimatributima ekvivalentno kreiranju nove instance, sa tim istim atributima. Ipak ima izuzetaka.Na primer, kada korisnik planira putovanje na internetu, ponekada zeli da sacuva nekolikopotencijalnih marsuta i vrati se nazad kasnije kako bi jednu od njih rezervisao. Te marsutepredstavljaju vrednosne objekte, ali one su povezane sa korisnickim imenom i vracaju sekorisniku netaknute. Drugi slucaj je nabrajanje, u slucaju kada je tip striktno limitiranu preodredeni skup mogucih vrednosti. Globalni pristup vrednosnim objektima je mnogomanje uobicajen nego sto je to slucaj sa entitetima. Ukoliko se, ipak, nademo u takvojsituaciji, potrebno je dobro razmotriti da li je rec o entitetu koji, mozda, nismo uspeli daprepoznamo.

Iz prethodne diskusije, zakljucujemo da se velikom broju objekata ne pristupa globalnimpretrazivanjem. Sada problem mozemo izraziti preciznije.

Podskup uskladistenim objektima mora biti globalno dostupan kroz pretragu baziranuna atribute tih objekata. Takav pristup je potreban za korene agregata kojima nije pogodnopristupiti obilaskom. To su obicno entiteti, ponekad vrednosni objekti sa kompleksnom in-ternom strukuturom i ponekad nabrojive vrednosti. Pruzanjem pristupa drugim objektima

24

Page 26: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

zamagljuju se vazna razaznavanja. Slobodni upiti baze mogu, zapravo, prekrsiti enkapsula-ciju objekata domena i agregata. Izlaganje tehnicke infrastrukture i mehanizama pristupabazi klijentu komplikuje rukovanje i, takode, zamagljuje dizajn.

Repozitorijum predstavlja sve objekte odredenog tipa kao skup. Ponasa se kao kolekcija,plus ima dodatni mehanizam upita. Objekti odgovarajuceg tipa mogu da se dodaju i obrisu,masinerija implementirana unutar repoziturijma radi stvarno ubacivanje i brisanje podataka.Ova definicija okuplja kohezivan skup odgovornosti za pruzanje pristupa korenima agregatakroz njihov zivotni vek.

Klijent zahteva objekte od repozitorijuma upotrebom metode upita. Vraceni objekti ba-zirani su na kriterijume koji su zadati od strane korisnika. Tipicno, to su vrednosti odredenihatributa. Repozitorijum vraca zahtevane objekte, skrivajuci masineriju pravljenja i izvrsenjaupita i mapiranja iz baze podataka. Repozitorijumi mogu implementirati raznovrsne upitekoji selektuju objekte bazirane na razlicitim kriterijumima koje klijent odabere. Oni cakmogu vratiti i rezime izracunavanja, kao sto su totali kroz sve objekte koji zadovaljajuuslove po nekom numerickom atributu.

Repozitorijum skida veliki teret klijentu, koji sada moze imati jednostavan interfejs kojiotkriva njegovu nameru i moze zahtevati objekte u terminima modela. Kako bi se podrzalosve ovo, potrebno je dosta kompleksnog infrastrukturnog koda. Na kraju dobijamo interfejskoji je jednostavan i konceptualno povezan na model domena.

Za svaku klasu, za koju je potreban globalni pristup, kreiramo objekat, koji pruza iluzijukolekcije svih objekata tog tipa u memoriji. Obavljamo sledece funkcije: pruzamo pristupkroz dobro poznati globalni interfejs, pruzamo metode za dodavanje i brisanje objekatakoji skrivaju tehnicko znanje o dodavanju i brisanju objekata, kao i metode koje selektuju(prema zadatom kriterijumu), potpuno instancirane objekte ili kolekciju objekata (cije vred-nosti atributa zadovoljavaju kriterijum skrivanjem stvarne tehnologije smestanja podataka),obezbedujemo repozitorijume samo za korene agregata kojima je zapravo potreban direktanpristup i drzimo klijenta fokusiranim na model (delegiranjem logike pristupa bazi repozito-rijumima).

Repozitorijumi imaju vise prednosti medu kojima su:

• Daju klijentu jednostavni model za preuzimanje uskladistenih objekata i rukovanjenjihovim zivotnim vekom.

• Razdvajaju aplikaciju i dizajn domena od tehnologije pristupa podacima (moguce jeimpelementirati visestruke baze podataka).

• Komuniciraju dizajnerskim odlukama o pristupu objektima.

• Omogucavaju jednostavnu zamenu laznom implementacijom (za potrebe testranja).

25

Page 27: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

3 Razdvajanje odgovornosti komandi i upita (eng. Com-

mand Query Responsibility Segregation – CQRS)

U okviru DDD cilj programera je razbiti znanje o domenu i implementirati ga kroz mrezumedusobno povezanih objekata, koji imaju odredena stanja i ponasanja. Model je jedinstven,a namera je potpuno opisati biznis-domen. U pocetku je to izgledalo kao stvar koju je laksereci nego uraditi.

Neki projekti koji su prihvatili DDD bili su uspesni, drugi, ipak, nisu. Na teorijskomdelu deluje da ima uspeha, ali dosta ljudi i dalje veruje da je DDD tesko implementirati,iako, njegova korist moze biti znacajna. Poenta je da ljudi misle da su benefiti koje DDDmoze doneti mnogo manji od problema koji mogu nastati prilikom implementiranja DDD-au slucaju neuspeha.

Analiticki deo DDD-a ima malo veze sa kodom i softverskim dizajnom. Sve je u osmisljavanjuarhitekture najviseg nivoa upotrebom alata (kao sto je opsteprisutan jezik Ubiquitis langu-age). Ovo je odlican pristup za bilo koji projekat. U kompleksnim scenarijima, razumevanjesire slike pomaze pri rasporedivanju modula i servisa. U jednostavnim scenarijima, svodi sena jedan kontekst i jedan modul.

Najveci broj poteskoca, sa kojima su se suocili rani usvojioci DDD-a, svodi se na problemsto se dizajn jednog modela brine za sve aspekte modela. Naime, isti model za citanje iza upis vodi kompleksnim modelima, koje je tesko odrzavati i optimizovati. Specijalno zaveoma komplikovane biznis scenarije, jedan model ubrzo postaje neodrziv. Ne samo da rasteeksponencijalno po velicini i kompleksnosti, vec ne obavlja posao na pravi nacin. Nije mogucekreirati optimalno resenje za pretragu, izvestavanje i procesiranje transakcija upotrebomjednog modela. Uvedimo sada patern koji razdvaja model domena na dva modela, postizucivise od jednostavnog razdvajanja odgovornosti.

CQRS je skracenica za “razdvajanje odgovornosti komandi i upita” (Command QueryResponsibility Segregation). Vecina ljudi misli da je CQRS arhitektura, ali to je pogresno.CQRS je mali patern. Njega su uveli Gregory Young i Udi Dahan. Dobili su inspiraciju izCommand Query Separation principa, koji je definisao Bertrand Meyer. Glavna ideja CQSprincipa je: “Metod bi trebalo ili da menja stanje objekta ili samo da vrati rezultat, aline oba”. Drugim recima, postavljanje pitanja ne bi trebalo da promeni odgovor. Formalno,metodi bi trebalo da vrate vrednost jedino ako nemaju sporedne efekte. Zbog ovoga mozemopodeliti metode u dva skupa:

• Komande - menjaju stanje sistema ili objekata, ne vracaju rezultat izuzev status-kodaili potvrde o izvrsenju.

• Upiti - vracaju rezultate i ni na koji nacin ne menjaju stanje sistema ili objekta.

U realnim situacijama, veoma je jednostavno razlikovati metode i odrediti njihov skup.Upiti deklarisu povratni tip, dok komande ne vracaju rezultat. Ovaj patern je siroko pri-menljiv i cini razumevanje objekata jednostavnijim.

CQRS koristi dva razlicita domena umesto samo jednog. Razdvajanje se postize grupi-sanjem operacija koje predstavljaju upite u jedan sloj i operacija koje su komande u drugi

26

Page 28: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

sloj. Svaki sloj, onda, ima i svoju arhitekturu i svoj skup servisa, posvecenih samo upitimai komandama. Sledeci dijagram prikazuje razlike:

Dijagram 5: Vizueno poredenje modela domena i CQRS arhitektura

Mozda zvuci iznenadujuce, ali jednostavno raspoznavanje da su komande i upiti dverazlicite stvari ima veliki uticaj na celokupnu arhitekturu sistema. Upiti su sada samopodaci koje je potrebno prikazati na korisicki interfejs, tako da im nije potreban modeldomena. Komande se ne izvrsavaju nad podacima koji su dobijeni iz upita. Kao takvi,agregati postaju jednostavniji. Model na strani upita moze biti jednostavno kolekcija DTOobjekata. Prateci ovo razmatranje, servisi postaju samo klase koje implementiraju biznislogiku koristeci anemicni model domena.

Ovo razdvajanje pojacava pojam da komandna strana i strana upita imaju razlicite po-trebe. Arhitekturna svojstva, vezana sa upotrebnim slucajevima svake strane veoma sudrugacija. U sledecem tekstu opsujemo odredene razlike:

• Konzistentnost

– Komanda: Mnogo je lakse procesirati transakcije sa konzistentnim podacimanego baratati eventualnom konzistentnoscu u komandnom sloju.

– Upit: Vecina sistema mogu biti eventualno konzistentni na strani upita.

• Skladistenje podataka

– Komanda: Komandna strana kao procesor transakcija zeli da skladisti podatkena normalizivani nacin u relacionoj strukturi – treca normalna forma.

– Upit: Strana upita zeli podatke na denormalizovani nacin kako bi se minimiziraobroj spajanja tabela za dobijanje zeljenog skupa podataka – prva normalna forma.

27

Page 29: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Skalabilnost

– Komanda: Kod vecine sistema, specijalno web sistema, komandna strana gene-ralno procesira veoma mali broj transakcija u odnosu na ukupan broj transakcija.Skalabilnost zbog toga nije uvek bitna.

– Upit: Kod vecine sistema, specijalno web sitema, strana upita generalno procesiraveliki broj transakcija u odnosu na ukupan broj transakcija. Skalabilnost je,najverovatnije, potrebna na strani upita.

Prava snaga ova dva paterna je u mogucnosti razdvajanja metoda u one koje menjajustanje od onih koje to ne cine. Ovo razdvajanje je veoma korisno u situacijama kada imamoproblema sa performansama. Moguce je optimizovati stranu upita sistema odvojeno odkomandne strane.

Druga korist ovog paterna je kod velikih aplikacija. Moguce je razdvojiti programere namanje timove koji rade na razlicite strane sitema (citanje i upis) bez obzira na saznanje kakoje implementirana druga strana. Na primer, programeri koji rade na strani upita, ne morajuda razumeju model domena.

Imati potpunu implementaciju modela domena na strani upita nije praksa. Strana upitaje specijalizovana za izvestavanje. Generalno, upiti bi trebalo da budu ekstremno jednostavni.Dodatno, moguce je imati odvojene baze podataka za stranu upita i stranu komandi.

3.1 Strana upita

Kao sto smo napomenuli, strana upita jedino sadrzi metode za preuzimanje podataka. Izoriginalne arhitekture ovo mogu da budu sve metode koje vracaju DTO objekte koje klijentkoristi za prikaz na ekranu.

U originalnoj arhitekturi izgradnja DTO objekta postize se projekcijom objekata domenana DTO objekte. Ovaj proces moze izazvati dosta poteskoca. Veliki izvor poteskoca prepo-znajemo u tome sto su DTO objekti razliciti po strukturi od objekata domena, i zbog togaje potrebno uvesti mapiranje izmedu njih. Najveci problem je taj sto je optimizacija upitaekstremno teska. Zato sto svi upiti operisu nad objektnim modelom, a onda se transformisuu DTO objekte.

DTO objekte je potrebno optimalno napraviti, kako bi odgovarali prikazima na ekranuklijenta i kako bi sprecili visestruke pozive ka serveru. U slucajevima kada treba raditi samnogo razlicitih klijenata, posao bi, mozda, bio olaksan ako bismo napravili univerzalnimodel koji bi svi oni koristili. U svakom, slucaju DTO model je veoma razlicit od modeladomena koji je izgraden za procesiranje transakcija.

Upotrebom CQRS paterna moguce je izbeci te projekcije. Uvodi se novi nacin projekto-vanja DTO objekata. Zaobilazi se model domena i DTO objekti se preuzimaju direktno izbaze podataka. Kada aplikacija zahteva podatke, ovo moze biti postignuto jednim pozivomstrane upita, koja vraca jedan DTO sa svim potrebnim podacima.

Strana upita nije kompleksni deo koda i moze biti dosadan za odrzavanje. Takode,dobra ideja je koristiti procedure u bazi podataka za citanje, sto opet zavisi od tima i

28

Page 30: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

nefunkcionalnih zahteva sistema. Direktna veza sa izvorom podataka, upite cini veomajednostavnim za pisanje, odrzavanje i optimizovanje. Ima smisla i denormalizovati podatke.Razlog za to je sto se upiti nad podacima, obicno, mnogo vise puta izvrsavaju, nego sto sepodaci azuriraju. Denormalizacija moze povecati performanse aplikacije.

3.2 Komandna strana

Sveukupno komandna strana ostaje veoma slicna tipicnoj DDD arhitekturi. Glavnarazlika je ta sto je domen sada koncentrisan na ponasanje, umesto na centralizovani pristuppodacima.

U tipicnoj DDD arhitekturi domen je zaduzen za rad sa upitima i komandama. Ovouzrokuje veliki broj problema u domenu. Neki od problema su:

• Veliki broj metoda za citanje u repozitorijumima.

• Unutrasnje stanje objekata domena se izlaze kroz geter metode kako bi se izgradilirezultujuci DTO objekti.

• Ucitava se vise agregata kako bi se sagradio rezultujuci DTO objekat sto uzrokujeneoptimalno zadavanje upita za izgradnju modela podataka.

Kada se strana upita razdvoji od modela domena, domen se onda fokusira na procesiranjekomandi. Ranije pomenuti problemi odjednom nestaju. Objekti domena odjednom nemajupotrebe za izlaganjem internog stanja, repozitorijumi imaju veoma malo ili nemaju ni jedanmetod pored GetById metode, i agregati se fokusiraju na ponasanje.

U poredenju sa originalnom arhitekturom, ova promena je uradena jeftino ili bez troskova.U mnogo slucajeva, razdvajanje smanjuje troskove, kako je optimizacija SQL upita jedno-stavnija. U najgorem slucaju, troskovi bi trebalo da budu jednaki. Sve sto je potrebnouraditi, jeste pomeranje odgovornosti. Moguce je, takode, imati stranu upita koja, koristiobjekte domena.

29

Page 31: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

4 Implementacija modela domena

Kod DDD-a izrada projekta pocinje dizajniranjem modela domena. Dizajniranje nijejednostavan proces, i u realnim slucajevima treba vremena i iskustva za dizajniranje dobrogmodela. Na osnovu specifikacija, dizajniran je sledeci model:

Dijagram 6: Dijagram koji predstavlja model domena projekta prodavnica

Kao sto vidimo iz dijagrama, centralna stavka u modelu je proizvod. On moze da budeubacen u grupu proizvoda radi lakse pretrage i oznacavanja. Takode, on mora da imaproizvodaca.

Sledecu bitnu stavku modela predstavljaju fakture, kojima se (kada se kreiraju) zadajedobavljac i koje se sastoje iz vise stavki. Takode, kada se faktura kreira, moguce je fak-turu kalkulisati, odredujuci cenu, pri tom, za svaku njenju stavku zasebno. Takode, kadase kalkulise faktura, moguce je promeniti cenu proizvoda (ukoliko je to potrebno), a kakokalkulacija sadrzi referencu na veci broj proizvoda, moguce je promeniti cenu vecem projuproizvoda. To se postize tako sto se prvo kreira nivelacija, a onda se promeni cena proizvoda.

Takode, bitna stavka aplikacije je ukucavanje prodajnih racuna, gde jedan racun mozesadrzati referencu na vise proizvoda.

30

Page 32: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

4.1 Definisanje agregata

Sada kada imamo dizajnirani model domena, potrebno je odabrati agregate i definisatinjihove granice.

Dijagram 7: Dijagram koji predstavlja model domena podeljen na agregate

Za dizajniranje agregata potrebno je, isto, iskustvo upotrebe DDD-a, ali i znanje o do-menu. Prethodni dijagram prikazuje definisane agregate1 u projektu Prodavnica, sledi i maloobjasnjenja:

Proizvod je centralni objekat u modelu, veoma je bitan za dalje referenciranje i upo-trebu, stoga se on definise kao poseban agregat. Moguce je definisati grupu proizvoda, grupeproizvoda je moguce nezavisno pretrazivati i rukovati sa njima, stoga i grupa proizvodapredstavlja poseban agregat. Medutim, clanstvo proizvoda u grupi nema smisla pretrazivati

1Pored agregata prikazanih na slici, u implementaciji projekta Prodavnica dodata su jos dva agregata, kojisu deo objekta Proizvod. To su ProizvodCena i StanjeProizvodaUProdajnomObjektu, razlog za definisanjeovih dodatnih agregata jesu performanse. Kako se cesto menja stanje, a i cena proizvoda, bez promeneostalih atributa proizvoda, i kako se ove dve vrednosti nezavisno menjaju, moguce je definisati posebneagregate za njih.

31

Page 33: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

bez konteksta proizvoda ili grupe proizvoda. Kako se clanstvo vise posmatra iz perspek-tive proizvoda, a ne iz perspektive grupe proizvoda, ono je smesteno u agregatu proizvoda.Proizvodac je, takode, poseban agregat, s obzirom na to da se posebno vrsi evidencija oproizvodacima u projektu.

Faktura predstavlja jos jedan koren agregata. Ona se sastoji od liste stavki gde se nalazeproizvodi. Pretrazivanje stavki fakture, bez znanja o kojoj fakturi se radi, nema mnogo smi-sla, tako da stavka fakture pripada agregatu fakture. Takode, za svaku fakturu je potrebnozadati dobavljaca, posto je potrebno vrsiti evidenciju o dobavljacima, i moguce je imati visefaktura sa istim dobavljacem, a on predstavlja jos jedan koren agregata.

Postoji zahtev za ukucavanje racuna, kako bi se omogucila prodaja proizvoda. Zbog togapostoji jos jedan agregat, gde su zadati prodajni racun i njegove stavke.

4.2 Dizajniranje sloja domena

Kada su definisani agregati, moze se krenuti sa implementacijom. Sa tim, se, opet, kreceiz sloja domena, gde se prvo definisu bazne klase. Kako agregati, entiteti i vrednosni objektizive u ovom sloju, definise se bazna klasa za svaki od ovih tipova. U daljem tekstu videcemonajvaznije detalje tih klasa. Bazne klase nisu deo logike domena. One su samo pomocnosredstvo koje olaksava rad sa objektima domena. Zbog, toga, sve ove klase smestaju se uinfastrukturnom kodu.

4.2.1 Bazna klasa entiteta

Bazna klasa entiteta predstavlja osnovnu klasu entiteta koju sve klase, koje predstavljajuentitet, nasleduju. U projektu je to abstraktna klasa sa nazivom EntityBase. Svi ID-evi ubazi su definisani celim brojem. Tip long moze da pokrije veliki skup vrednosti celih brojevai, zbog toga, je polje “id” tipa long. Sluzi samo za citanje i inicijalizuje se prilikom kreiranjaobjekta.

Listing 1: Osnovna svojstva bazne klase entiteta

public abstract class EntityBase{

private readonly long _id;

protected EntityBase(long id){

if (id == 0) throw new ValidationException("Id mora biti zadat");_id = id;

}

public long Id{

get { return _id; }}...

Sledeca odgovornost ove klase je definisati metode za ispitivanje jednakosti dva objekta.

32

Page 34: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Kako se svaki entitet poredi po vrednosti id-a, zgodno je to implementisati u ovoj klasi.Ove metode se kasnije koriste za poredenje u kolekcijama, gde je potrebno naci odgovarajucielement. Generalno, koriste se svuda i, zbog toga, veoma je bitno definisati ove metodekorektno.

Listing 2: Metode za poredenje entiteta

public override bool Equals(object entity){

if (ReferenceEquals(entity, null) || !(entity is EntityBase)){

return false;}return (this == (EntityBase) entity);

}

public static bool operator ==(EntityBase base1, EntityBase base2){

if (ReferenceEquals(base1, null) && ReferenceEquals(base2, null)){

return true;}

if (ReferenceEquals(base1, null) || ReferenceEquals(base2, null)){

return false;}

if (base1.Id != base2.Id){

return false;}

return true;}public override int GetHashCode(){

return _id.GetHashCode();}

Ove metode su, takode, bitne za poredenje, sortiranje i uparivanje entiteta. Takode, ovopredstavlja infrastrukturni kod, jer se ne bavi problemom domena, a entitete ostavljamoslobodne od ovakvog tehnickog koda.

4.2.2 Bazna klasa vrednosnih objekata

Za razliku od entiteta, vrednosni objekti nemaju identitet, i zbog toga, ne mogu imatiatribut ID. Svaki vrednosni objekat ima svoj poseban skup promenljivih i strategiju zaporedenje. Postoje primeri na internetu gde se logika za poredenje implementira u baznuklasu vrednosnih objekata, ali kako svaki vrednosni objekat ima svoj poseban skup promen-ljivih i strategiju poredenja, nije dobro generalizovati celu logiku poredenja u baznoj klasi.Takode, veoma je lako izostaviti metode Equals i GetHashCode za svaki vrednosni objekatposebno. Zbog toga bazna klasa sadrzi apstraktne metode EqualsCore i GetHashCodeCorekoje moraju da implementiraju sve ostale klase.

33

Page 35: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 3: Metode za poredenje vrednosnih objekata

public abstract class BaseValueObject<T> where T : BaseValueObject<T>{

public override bool Equals(object obj){

var valueObject = obj as T;

if (ReferenceEquals(valueObject, null)){

return false;}if (!HasValue && !valueObject.HasValue) return true;if (!HasValue || !valueObject.HasValue) return false;return EqualsCore(valueObject);

}

public override int GetHashCode(){

return GetHasCodeCore();}

protected abstract int GetHashCodeCore();protected abstract bool EqualsCore(T valueObject);

public static bool operator ==(BaseValueObject<T> a, BaseValueObject<T> b){

if (ReferenceEquals(a, null) && ReferenceEquals(b, null))return true;

if (ReferenceEquals(a, null) || ReferenceEquals(b, null))return false;

return a.Equals(b);}

Kao sto mozemo da vidimo, metode za poredenje vrednosnih objekata su implementiraneapstraktno. Ovde se centralizuje kod za poredenje referenci i provere da nije poslat nullobjekat, kako se ta logika ne bi kopirala u svaki vrednosni objekat. Kada se sve proverereferenci zavrse, poziva se apstraktna metoda EqualsCore, koja vrsi poredenje parametrai daje konacan rezultat. Takode, implementirana je i metoda GetHashCode, koja samoprosleduje poziv apstraktnoj metodi GetHashCodeCore, kako bi se odredio hes kod.

Bazna klasa vrednosnih objekata, moze da sadrzi i drugu logiku, koja se tice svih vred-nosnih objekata.

Kako se veoma cesto desava da vrednosni objekti predstavljaju null vrednost i kako bi sesmanjila kolicina koda potrebna za odrzavanje null vrednosti u entitetima, dodata je podrskaza vrednosne objekte, koji predstavljaju null vrednost. Prvo se u konstruktoru prosledujeparametar, koji indikuje da li postoji vrednost u trenutnoj instanci vrednosnog objekta.Ova vrednost se prosleduje iz klasa, koje nasleduju ovu baznu klasu proveravanjem svojihvrednosti u konstruktoru.

34

Page 36: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 4: Konstruktor

protected BaseValueObject(bool hasValue){

_hasValue = hasValue;}

Kako je u nekim slucajevima pozeljno dozvoliti konstrukciju nevalidnog vrednosnogobjekta koji se onda kasnije validira, dodate su metode za validaciju koje podrzvaju ovajscenario.

Listing 5: Validacija

public bool Validate(bool checkForNull = true){

return GetErrorMessage(checkForNull, null) == null;}

public void ValidateWithException(bool checkForNull, string fieldName){

DomainException.ThrowIfHasMessage(GetErrorMessage(checkForNull, fieldName));}

public string GetErrorMessage(bool checkForNull, string fieldName){

if (!checkForNull && !HasValue) return null; // if it is null value and null check is disabledif (_isValid) return null; // if it is already validated then return nullif (!HasValue) return "Vrednost nije zadata";var errorMsg = GetErrorMessageCore();_isValid = errorMsg == null;if (errorMsg != null && fieldName != null){

return fieldName + " : " + errorMsg;}return errorMsg;

}

Naravno, moguce je sprovoditi validaciju odmah prilikom konstrukcije objekta, sto je,uglavnom, i pozeljno, ali, i u tom slucaju, ove metode ne gube na znacaju. Zbog toga,moguce je upotrebiti ih za validaciju prilikom konstruisanja objekta.

4.2.3 Bazna klasa agregata

Ova klasa nasleduje baznu klasu entiteta, jer su svi koreni agregata, u stvari, entiteti.Kako ne postoji nikakva specijalna logika koja je vezana samo za agregate, za sada, je ovaklasa prazna. Videcemo kasnije da je u ovoj klasi implementirana logika za procesiranjedogadaja u domenu.

35

Page 37: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 6: Bazna klasa agregata

public abstract class AggregateRoot : EntityBase{}

4.2.4 Agregat Faktura

Sada, kada imamo infrastrukturni kod za postavljen na mestu, moguce je poceti sa im-plementiranjem agregata. Agregate smo vec definisali, a sada prikazujemo primere koda onacinu implementiranja konkretnog agregata Faktura, kako umnogome oslikava koncepte okojima se diskutovalo u teorijskom delu.

Dijagram 8: Dijagram zavisnosti klasa agregata Faktura

Kao sto se moze videti iz dijagrama, klasa Faktura nasleduje baznu klasu agregata Ag-gregateRoot, koja, pak, nasleduje baznu klasu entiteta EntityBase. Faktura sadrzi razliciteatribute. Za neke od njih koriste se vrednosni objekti. Tako, faktura je zavisna od klasavrednosnih objekta Procenat, Novac i BrojFakture. Takode, faktura sadrzi listu stavki fak-ture pa je zavisna i od klase FakturaStavka. Zavisnosti na vrednosne objekte mozemo takode,videti i iz koda, ukoliko pogledamo listu promenljivih:

36

Page 38: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 7: Promenljive agregata fakture

public Procenat FakturaRabat { get; private set; }public BrojFakture BrojFakture { get; private set; }public DateTime DatumFakture { get; private set; }public DateTime? DatumValute { get; private set; }public long DobavljacId { get; private set; }public long KorisnikId { get; private set; }private IList<FakturaStavka> FakturaStavkaList { get; set; }

Iz prethodnog koda, takode, mozemo zapaziti da su svim promenljivima seteri privatni. Ra-zlog je taj sto agregat upravlja stanjem svih ovih promenljivih i sto je njihove vrednostimoguce promeniti samo kroz specijalizovane metode.

Takode, promenljiva FakturaStavkaList je privatna, cak, nema ni javni geter. Razlogeza to valja traziti u zabrani izmene sadrzaja ove kolekcije, bez posredstva agragata Faktura(jer je agregat duzan da upravlja svojim internim stanjem). Definisan je poseban properti uklasi, koji vraca kolekciju klijentu iskljucivo za citanje:

Listing 8: Pristup stavkama fakture

public IEnumerable<FakturaStavka> StavkeFakture{

get { return FakturaStavkaList; }}

Pogledajmo sada konstruktor.

Listing 9: Konstruktor

protected Faktura(long fakturaId, string brojFakture, DateTime datum, DateTime? datumValute,long dobavljacId, long korisnikId, bool isKalkulisana,double? rabatProcenat, IList<FakturaStavka> stavke): base(fakturaId)

{if (dobavljacId == 0) throw new DomainException("Dobavljac mora biti zadat");if (korisnikId == 0) throw new DomainException("Korisnik nije zadat");

BrojFakture = new BrojFakture(brojFakture);BrojFakture.ValidateWithException(true, "Broj Fakture");

DatumFakture = datum;DatumValute = datumValute;DobavljacId = dobavljacId;KorisnikId = korisnikId;IsKalkulisana = isKalkulisana;FakturaRabat = new Procenat(rabatProcenat);

FakturaStavkaList = new List<FakturaStavka>(stavke);}

Konstruktor je zaduzen za inicijalizaciju i kreiranje pocetnog stanja objekta. Takode,konstruktor mora da obezbedi postavljanje objekta u konzistentno pocetno stanje. Zbog

37

Page 39: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

toga, na pocetku, dodate su provere za obavezne parametre. Konvencija u programu je daukoliko id polje ima vrednost 0, to znaci da vrednost nije inicijalizovana, pa se ta proveraradi za polja dobavljacId i korisnikId. Ukoliko vrednost nekog od obaveznih polja nije zadata,baca se DomainException, klasa koja oznacava sve izuzetke, koje se desavaju u sloju domena.

Iz koda konstruktora, takode, moze se primetiti da je vidljivost konstruktora protected.To je zato sto direktan poziv konstruktora nije dozvoljen, vec se objekat inicijalizuje pozivomstatickih metoda fabrike, koje su definisane u samoj klasi:

Listing 10: Fabricke metode

public static Faktura CreateNew(long fakturaId, string brojFakture, DateTime datumFakture,DateTime? datumValute, long dobavljacId, long korisnikId, double? rabatIznos)

{return new Faktura(fakturaId, brojFakture, datumFakture,

datumValute, dobavljacId, korisnikId, rabatIznos, new List<FakturaStavka>());}

public static Faktura Reconstituite(long fakturaId, string brojFakture, DateTime datumFakture,DateTime? datumValute, long dobavljacId, long korisnikId, double? rabatIznos,IList<FakturaStavka> stavke)

{return new Faktura(fakturaId, brojFakture, datumFakture, datumValute,

dobavljacId, korisnikId, rabatIznos, stavke);}

Kako za sada postoji samo jedan scenario za izmenu vrednosti fakture, dodata je posebnametoda koja to omogucava.

Listing 11: Izmena podataka

public void IzmenaFakture(string brojFakture, DateTime datumFakture, DateTime? datumValute,long dobavljacId, long korisnikId, double? rabatIznos, IList<FakturaStavka> noveStavke)

{if (dobavljacId == 0) throw new DomainException("Dobavljac mora biti zadat");if (korisnikId == 0) throw new DomainException("Korisnik mora biti zadat");

BrojFakture = new BrojFakture(brojFakture);BrojFakture.ValidateWithException(true, "Broj Fakture");

DatumFakture = datumFakture;DatumValute = datumValute;DobavljacId = dobavljacId;KorisnikId = korisnikId;FakturaRabat = new Procenat(rabatIznos);FakturaRabat.ValidateWithException(false, "Procenat rabata za fakturu");

FakturaStavkaList = noveStavke;}

Kao sto sto se iz prilozenog koda vidi, prilikom promene vrednosti promenljivih, postojeobavezna polja i za njih se vrsi validacija. Klasa sadrzi i ostale funkcije rad sa instancomfakture. Tako postoji: metoda DodajStavku koja dodaje novu stavku u fakturi, metodaGetIznosFakture, koja izracunava vrednost fakture sumirajuci iznose svojih stavki, metodaRabatUkupno, koja vraca ukupan rabat za fakturu sumirajuci rabate stavki fakture i tako

38

Page 40: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

dalje.

4.2.5 Vrednosni objekat Novac

Vrednosni objekat tipa Novac se najvise koristi u projektu, takode, novac se cesto koristiu literaturi prilikom objasnjavanja vrednosnih objekata. Zbog toga, nalazimo da je on dobarkandidat za opis.

Listing 12: Promenljive i konstruktor

public class Novac : BaseValueObject<Novac>{

private const double PragOsetljivosti = 0.01;private const int DecimalPlaces = 2;public static readonly Novac NulaNovac = new Novac(0);private readonly double? _iznos;

public Novac(double? iznos) : base(iznos.HasValue){

if (iznos.HasValue)_iznos = Math.Round(iznos.Value, DecimalPlaces);

}

Vidimo da Novac nasleduje baznu klasu vrednosnih objekata BaseValueObject. Napocetku klase definisane su konstante koje se koriste u okviru klase Novac, ali, takode,postoje i konstante koje se mogu koristiti i van klase kao sto je to NulaNovac. Prilikomkonstruisanja, baznoj klasi se salje parametar koji indikuje da li je zadata vrednost za novaci postavlja se vrednost vrednosnog objekta. Vrednosni objekti predstavljaju sjajno mestoza centralizovanje logike oko nekog pojma, za koji programeri obicno misle da nije potrebnoimati posebnu klasu. Takve metode su na primer:

Listing 13: Utilty metode

public string OznakaValute{

get { return DinarOznaka; }}

public static string DinarOznaka{

get { return "Din."; }}

public bool IsNulaIznos(){

return Math.Abs(_iznos) < PragOsetljivosti;}

public static string GetErrorMessage(double iznos){

return iznos < 0 ? "Iznos novca mora biti pozitivan" : null;}

39

Page 41: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Ukoliko ne bi postojala ova klasa, ove provere i zakucane vrednosti bile bi svuda po kodu,sto bi otezalo odrzavanje koda, uvelo potencijalne greske, itd. U najboljem slucaju, bile biu nekoj Utility klasi, sto, i dalje, nije najbolje resenje.

Kako su potrebne matematicke operacije sa novcem, klasa Novac, dalje, sadrzi operatoresa rad za razlicitim tipovima podataka.

Listing 14: Operatori

public static Novac operator +(Novac n1, Novac n2){

return new Novac(n1.Iznos + n2.Iznos);}

public static Novac operator +(Novac n, double d){

return new Novac(n.Iznos + d);}

Nisu sve metode operatora prikazane gore, buduci da one predstavljaju standardnu im-plementaciju operatora. Na kraju, implementirane su apstraktne metode iz baznog tipa.Metod GetErrorMessageCore vrsi validaciju objekta, metod GetHashCodeCore vraca heskod i metod EqualsCore poredi dva objekta.

Listing 15: Validacija

protected override string GetErrorMessageCore(){

return Iznos < 0 ? "Iznos novca mora biti pozitivan" : null;}

protected override int GetHashCodeCore(){

return (GetHashCode()*397) ˆ _iznos.GetHashCode();}

protected override bool EqualsCore(Novac valueObject){

return Math.Abs(IznosChecked - valueObject.IznosChecked) < PragOsetljivosti;}

4.2.6 Dogadaji domena

Dogadaji domena treba da rade kao i svi ostali tipovi dogadaja, sa kojima se srecemou programiranju. Tipican scenario je da se kreira novi tip dogadaja u klasi koja sadzineophodne detalje za izazivanje dogadaja. Nakon toga, potrebno je napisati kod koji prihvatai obraduje dogadaj.

Implementiranje pocinje definisanjem interfejsa, koji predstavlja sve dogadaje u domenu.Interfejs trenutno sluzi samo za oznacavanje svih objekata koji predstavljaju dogadaje.

40

Page 42: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 16: Bazni interfejs za sve klase dogadaja

public interface IDomainEvent{}

Sledeci korak je definisati interfejs za sve klase koje obraduju dogadaje domena. Interfejssadrzi jednu metodu, koja prihvata dogadaj kao parametar i nema povratni parametar.

Listing 17: bazni interfejs za sve klase hendlera

public interface IDomainEventHandler<T> where T : IDomainEvent{

void Handle(T domainEvent);}

Posto smo definisali interfejse za sve klase koje predstavljaju dogadaje domena, i zaklase koje obraduju dogadaje, potrebno je implementirati kod, koji omogucava izazivanjetih dogadaja. Kako je patern dogadaja u domenu prilicno genericki patern, postoje razlicitinacini na koje ova funkcionalnost moze da se implementira. Ovde se koristi verzija gde sedogadaji domena izazivaju kroz baznu klasu agregata. U baznoj klasi agregata dodaje sesledeci kod:

Listing 18: Integracija sa agregatima

public abstract class AggregateRoot : EntityBase{

private readonly IList<IDomainEvent> _domainEvents;

protected AggregateRoot(long id): base(id)

{_domainEvents = new List<IDomainEvent>();

}

public IList<IDomainEvent> Events{

get { return _domainEvents; }}

protected virtual void AddDomainEvent(IDomainEvent newEvent){

_domainEvents.Add(newEvent);}

public virtual void ClearEvents(){

_domainEvents.Clear();}

}

Prethodni kod pruza infrastrukturu klasama agregata za izazivanje dogadaja pozivommetode AddDomainEvent, gde je parametar konkretni objekat, koji sadrzi detalje dogadaja.

41

Page 43: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Takode, obezbeduje se interfejs za rad dispeceru dogadaja kroz kolekciju dogadaja Events imetode ClearEvents, koja prazni listu dogadaja nakon njihove obrade.

4.2.6.1 Dispecer dogadaja

Sada je potrebno implementirati dispecer dogadaja. Zadatak dispecera je da za svedogadaje u agregatu pozove odgovarajuce metode za obradu, koje se nalaze u objektimaregistrovanim za obradu tih dogadaja. Takode, dispecer treba da ponudi interfejs za regi-strovanje svim klasama, koje zele da obraduju dogadaje. Stoga, potrebno je kreirati klasukoja implementira sledeci interfejs.

Listing 19: Bazni interfejs dispecera

public interface IDomainEventsRegistrator{

void RegisterHandler<TEventType, THandlerType>() where TEventType : IDomainEventwhere THandlerType : IDomainEventHandler<TEventType>;

}

Interfejs deklarise metodu RegisterHandler<TEventType, THandlerType> koja sluzi zaregistrovanje klase za obradu dogadaja.

Za dati agregat, dispecer mora da prode kroz sve dogadaje koji su generisani u okviruagregata. Za svaki dogadaj potrebno je pronaci odgovarajucu klasu (ili klase) koje su regi-strovane za obradu trenutnog dogadaja. Nakon toga, mora da kreira instance klasa hendlerai pozove metodu za obradu dogadaja. Ovo se obicno postize refleksijom, a kako se u projektukoristi Prism biblioteka, implementacija dispecera potpomognuta je Unity kontejnerom.

Listing 20: Implementacija dispecera

public class DomainEventsDispatcher : IDomainEventsDispatcher{

private readonly IUnityContainer _container;private readonly IDictionary<Type, IList<Type>> _eventHandlers =

new Dictionary<Type, IList<Type>>();

public DomainEventsDispatcher(IUnityContainer container){

_container = container;}

public void RegisterHandler<TEventType, THandlerType>() where TEventType : IDomainEventwhere THandlerType : IDomainEventHandler<TEventType>

{var key = typeof (TEventType);IList<Type> handlers = null;if (_eventHandlers.ContainsKey(key)){

handlers = _eventHandlers[key];handlers.Add(typeof (THandlerType));

}else{

handlers = new List<Type>();

42

Page 44: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

handlers.Add(typeof (THandlerType));_eventHandlers.Add(key, handlers);

}}

public void DispatchEvents(AggregateRoot item){

foreach (var domainEvent in item.Events){

Dispatch(domainEvent);}item.ClearEvents();

}

public void Dispatch(IDomainEvent domainEvent){

foreach (var eventHandler in _eventHandlers[domainEvent.GetType()]){

dynamic handler = _container.Resolve(eventHandler);handler.Handle((dynamic) domainEvent);

}}

}

Na pocetku klase definisane su promenljive koje su potrebne za normalno funkcionisanjedispecera. container koristi se prilikom kreiranja nove instance klase za obradu dogadaja,dok hes mapa eventHandlers cuva listu registrovanih klasa za obradu dogadaja.

RegisterHandler metoda ima dva tipovska argumenta, gde prvi predstavlja tip dogadaja,a drugi tip hendlera. Kako u listu dodaje samo tip hendlera za dati dogadaj, a onda se talista smesta u hes mapu eventHandlers, implementacija ove metode je prilicno jednostavna.

DispatchEvents metoda prolazi kroz sve dogadaje u agregatu i poziva metodu Dispatch,koja je odgovorna za poziv svih hendlera prosledenog objekta dogadaja. Na kraju metodebrisu se svi dogadaji iz agregata, kako su svi obradeni.

Kako bi se obradio dogadaj, Dispatch metoda prihvata dogadaj kao parametar, identifi-kuje sve hendlere datog dogadaja, kreira instancu klase hendlera uz pomoc Unity kontejnerai poziva metodu Handle.

4.2.6.2 Pozivanje dispecera prilikom procesiranja transakcije

Na kraju obrade inicijalne akcije, potrebno je ugraditi poziv dispecera na kraju obradeinicijalne akcije. Vecina implementacija UnitOfWork paterna podrzava mogucnost pozivametoda na kraju ili na pocetku transakcije, pre nego sto se komituju promene. To je, uovom slucaju, upravo i potrebno. Dogadaje je moguce procesirati i izvan jedne transakcije,ali u tom slucaju je potrebno imati kod koji obraduje slucaj kada se desi greska prilikomprocesiranja dogadaja. Zbog tih kompleksnosti, ovde je cilj procesirati dogadaje u istojtransakciji. Kako se u projektu ne koristi nijedna gotova implementacija paterna UnitO-fWork, vec je ovaj patern implementiran manuelno, lako je dodati kod za poziv dispecera napocetku transakcije.

43

Page 45: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 21: Integracija sa jedinicom rada

public void Commit(){

// Process Domain Events before transaction is startedProcessDomainEvents();// Persist data...

}

private void ProcessDomainEvents(){

// process domain eventsint loopCnt = 0;bool processDomainEvents = true;

while (processDomainEvents){

var aggregatesWithEvents =_uowPersistActions.Where(action => action.Aggregate.Events.Count > 0).ToList();

processDomainEvents = aggregatesWithEvents.Count > 0;

foreach (var action in aggregatesWithEvents){

_dispatcher.DispatchEvents(action.Aggregate);}

loopCnt++;if (loopCnt == MaxDomainEventsDepth)

throw new Exception("Maximum number of cycles " +"for domain events reached: LoopCount:" + loopCnt);

}}

U prethodnom kodu vidimo da se poziva metoda ProcessDomainEvents, pre nego stose agregati snime u bazu podataka. U okviru metode za procesiranje dogadaja domenaizdvajaju se svi agregati u okviru kojih postoje kreirani dogadaji. Zatim, za svaki od tihagregata poziva se dispecer dogadaja koji pretrazuje odgovarajuce klase za njihovu obradu.Ovaj process ponavlja se sve dok postoje agregati sa neprocesiranim dogadajima ili dok sene dostigne maksimalni broj iteracija (u tom slucaju se baca izuzetak).

44

Page 46: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

5 Implementacija aplikacionog sloja

5.1 Komandna strana

Infrastruktura komande sastoji se iz tri osnovne komponente:

• Komande predstavljaju objekte koji sadrze podatke i koji se predaju na izvrsavanje.

• Hendleri predstavljaju objekte koji sadrze biznis logiku potrebnu za obradu komandi.

• Dispecer predstavlja objekat koji je zaduzen za povezivanje komandi i njihovih hen-dlera, kao i za instanciranje potrebnih objekata.

5.1.1 Komande

Najpre, potrebno je kreirati Command interfejs:

Listing 22: Osnovni interfejs komande

public interface ICommand{}

Intefejs ICommand ne definise nikakve metode za implementaciju, zato sto su komandesamo obicni DTO objekti koji nose podatke. Ipak interfejs ima dosta znacaja, jer cini dakod bude citljiviji i jednostavniji za razumevanje. Sledi primer konkretnog objekta komande,koji sluzi za brisanje fakture:

Listing 23: Primer komande

public class BrisanjeFaktureCommand : ICommand{

public BrisanjeFaktureCommand(long fakturaId){

FakturaId = fakturaId;}

public long FakturaId { get; private set; }}

Kao sto vidimo iz primera, komande su prosti DTO objekti bez ponasanja cija je svrhalogicki predstaviti komandu koja je nalozena od strane korisnika i prenos podataka doobjekta, koji je zaduzen za obradu te komande. Komande sadrze atribute, koji se inicijali-zuju na strani klijenta, dok se njihove vrednosti koriste na strani hendlera. Nije dozvoljenomenjati vrednosti atributa komandnih objekata u hendleru.

U konkretnom primeru, umesto parametra FakturaId komandni objekat moze da sadrzireferencu na fakturu koju je potrebno obrisati. Medutim, takva praksa, generalno, smatrase nepozeljnom, buduci da se prilikom kreiranja i izvrsavanja komande stvaraju zavisnosti,

45

Page 47: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

koje su suvisne. Komande treba da budu prosti DTO objekti, koji sadrze atribute osnovnihtipova podataka.

5.1.2 Hendleri

Zadatak hendlera je da obrade i procesiraju komandu. Oni predstavljaju implementacijuservisa, koji se nudi klijentima kroz komande. Interfejs hendlera definisemo na sledeci nacin:

Listing 24: Osnovni interfejs hendlera

public interface ICommandHandler<in TCommand>where TCommand : ICommand

{void Handle(TCommand command);

}

Svaki hendler mora da sadrzi metod Handle, koji prihvata komandu kao parametar. Slediprimer hendlera komande za brisanje fakture:

Listing 25: Primer hendlera

public class BrisanjeFaktureCommandHandler : ICommandHandler<BrisanjeFaktureCommand>{

private readonly IFakturaRepository _repository;

public BrisanjeFaktureCommandHandler(IFakturaRepository repository){

_repository = repository;}

public void Handle(BrisanjeFaktureCommand command){

var faktura = _repository[command.FakturaId];faktura.ValidirajBrisanje(true);_repository.Remove(faktura);

}}

Komande i hendleri u aplikaciji, zavisno od mesta u kome su definisani, mogu da predsta-vljaju razlicite vrste servisa. Kako se hendler nalazi u aplikacionom sloju, on ne sme da sadrzibiznis logiku. Njegov zadatak je da koordinira procesom, a biznis odluke treba da prepustiagregatima ili servisima domena. U ovom slucaju, hendler prepusta validaciju za brisanjeagregatu fakture. Sve zavisnosti, koje su potrebne za izvrsavanje komande pribavljaju sekroz konstruktor.

5.1.3 Dispecer komande

Sada kada su definisane komande i hendleri za komande moguce je krenuti sa njihovimimplementiranjem u projektu. Medutim, da bi ceo proces radio kako treba, potreban namje dispecer. Dispecer je odgovoran za instanciranje i poziv metode za obradu komandeodgovarajuceg hendlera, za komandu koju dobije na ulazu.

46

Page 48: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 26: Osnovni interfejs dispecera

public interface ICommandDispatcher{

void Execute<TCommand>(TCommand command)where TCommand : ICommand;

}

Dispecer definise genericki metod Execute koji prihvata i izvrsava komandu zadatog tipa.Ne definise povratni rezultat, jer komande uglavnom ne vracaju rezultat. Izuzetak su krei-rajuce komande, gde je rezultat definisan u samoj komandi.

Dispecer ima istu namenu kao i aplikacioni servisi. Medutim, dok je njihova namena ista,izmedu njih primecujemo razlike. Aplikacioni servisi su komplikovaniji zato sto grupisu visepovezanih zadataka, sto znaci da je klasa veca, tj. zavisna od veceg broja drugih objekata ida nosi veliku odgovornost.

Implementiranje dispecera je jednostavnije zato sto svaka komanda ima svoj jedinstvenihendler. Svaki hendler bavi se obradom samo jedne komande, i samim tim, kod je jednostav-niji. Sa druge strane, nekada moze biti komplikovanije implementirati i koristiti dispecer.Takve situacije su kada dispecer ne vraca nikakvu povratnu vrednost nakon izvrsavanjakomande (kao sto je to na primer ID novokreiranog objekta).

Kao sto je ranije napomenuto, zadatak dispecera je da poveze komandu za njenim hendle-rom i pozove hendler kako bi on izvrsio tu komandu. Prethodna arhitektura nam omogucujeveoma lako razresavanje hendlera preko tipa komande, jer je svaki hendler vezan za jednukomandu. Kako bi to implementirali u praksi, ukljucujemo Unity kontejer koji umnogomepojednostavljuje implementaciju. Definisemo UnityCommandDispatcher na sledecin nacin:

Listing 27: Unity dispecer

public class UnityCommandDispatcher : ICommandDispatcher, IInternalCommandDispacher{

private readonly IUnityContainer _container;

public UnityCommandDispatcher(IUnityContainer container){

_container = container;}

public void Execute<TCommand>(TCommand command) where TCommand : ICommand{

if (command == null){

throw new CommandProcessingException("Command object is null");}

var handler = _container.Resolve<ICommandHandler<TCommand>>();

if (handler == null){

throw new CommandProcessingException("Command handler not found, type:"+ typeof (TCommand));

}

47

Page 49: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

try{

handler.Handle(command);}catch (DomainException e){

throw new CommandProcessingException("Domain processing error" + e.Message);}

}}

Prvo se proverava da li je uopste zadata komanda, onda se razresava hendler i na krajuse hendler poziva. Ukoliko dode do greske iz domena, ona se obraduje tako sto se izazivagreska procesiranja komande (kako bi se sloj domena zastitio od klijenta).

Ovakva implementacija veoma je ekstenzibilna. Na primer, ovde je potrebno kreiratitransakcije prilikom procesiranja komande. To se radi bez promene originalne klase dispecera– kreiranjem dekoratora:

Listing 28: Transakcioni dispecer

public class TransactionalCommandDispatcher : ICommandDispatcher{

private readonly ICommandDispatcher _dispatcher;private readonly IUnitOfWork _unitOfWork;

public TransactionalCommandDispatcher(ICommandDispatcher dispatcher, IUnitOfWork unitOfWork){

_dispatcher = dispatcher;_unitOfWork = unitOfWork;

}

public void Execute<TCommand>(TCommand command) where TCommand : ICommand{

using (_unitOfWork){

_dispatcher.Execute(command);_unitOfWork.Commit();

}}

}

5.2 Strana upita

Strana upita aplikacije razlikuje se od komandne strane. Na strani upita potrebno jekoncentrisati se na podatke koji su potrebni klijentu, tj. aplikaciji za prikaz. Model podatakai upiti za pretragu podataka implementirani su u aplikacionom sloju.

5.2.1 Model podataka

Klase modela ne sadrze biznis logiku, vec samo podatke koji su potrebni aplikaciji zaprikaz. Takode, kako sluze samo za citanje, nema potrebe brinuti o granicama agregata.

48

Page 50: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Iako model sluzi samo za citanje, pozeljno je razlikovati objekte koji nose identitet (tj.predstavljaju entitete) od ostalih objekata. U tu svrhu, kreirana je bazna klasa Identi-tyCarier, koja dosta podseca na baznu klasu entiteta BaseEntity, s tim sto se u ovoj klasine forsira zadavanje vrednosti id atributa prilikom kreiranja objekta. Kako je ovo jedinarazlika, detalji klase nece biti prikazani.

Strana upita fokusira se na pretragu podataka i vracanje rezultata. Kako je na prikazupotrebno pretrazivati objekte nekog agregata, a zatim omoguciti detaljniji prikaz odredenogagregata, u aplikaciji postoje dva modela prikaza podataka. Prvi model koristi se za sumarniprikaz (prikazuje najosnovnije podatke o agregatu i koristi se prilikom pretrazivanja i listanjaobjekata). Drugi je detaljni model, koji se koristi za detaljni prikaz objekta agregata.

5.2.1.1 Sumarni model

Kako je skoro uvek potrebno pretraziti elemente, a zatim odabrati odredeni elemenat,kako bi se smanjilo vreme procesiranja, pogodno je u tom slucaju vratiti samo osnovne isumarne detalje o agregatima. Takode, nije ni potrebno prikazati sve informacije korisnikujer bi se prikaz previse iskomplikovao. Sledi primer klase za listanje faktura.

Listing 29: Primer sumarnog objekta

public class FakturaFilterResultItem : IdentityCarier{

Konstruktori...

public string BrojFakture { get; set; }public DateTime Datum { get; set; }public DateTime? DatumValute { get; set; }public double Iznos { get; set; }public string ValutaOznaka { get; set; }public long DobavljacId { get; set; }public string DobavljacNaziv { get; set; }public long KorisnikId { get; set; }public string KorisnickoIme { get; set; }public bool Kalkulisana { get; set; }

}

Kao sto vidimo na osnovu primera fakture, model sadrzi osnovne sumarne informacijevezane za stavku fakture. Takode, ukljuceni su podaci o dobavljacu i korisniku koji je primiofakturu, jer je potrebno prikazati te informacije prilikom listanja faktura.

5.2.1.2 Detaljni model

Kada se korisnik odluci koji objekat zeli detaljno da pregleda ili izmeni, potrebno je pri-kazati detaljne informacije njemu. Ovde je potrebno prikazati sve moguce detalje objekta,i, cesto, je potrebno ukljuciti informacije iz razlicitih agregata, kako bi forma bila pregled-nija. Iz tih razloga, detaljniji model ne sadrzi samo ID atribute koji pokazuju prema drugimagregatima, kao sto je to slucaj na komandnoj strani, vec se povlace sve neophodne infor-macije i iz drugih agregata. Kako implementacija ovih klasa ne sadrzi biznis logiku, a klase,

49

Page 51: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

uglavnom, lice na DTO objekte, jer uglavnom samo sadrze podatke, izlaze se samo dijagramklasa agregata fakture kao predstavnik detaljnog modela.

Dijagram 9: Dijagram klasa modela za prikaz detalja fakture

Na osnovu prethodnog dijagrama, mozemo da zakljucimo da se model sacinjava iz po-dataka koji se nalaze u vise od jednog agregata. Takode, vidimo da sve klase koje sadrzeidentitet nasleduju apstraktnu klasu IdentityCarier i to je uglavnom zbog jednostavnijegprocesiranja u prezentacionom sloju. Takode, vidimo da klase sadrze samo propertije i nesadrze nikakve metode koje ukljucuju biznis logiku, sto ih cini veoma nezanimljivim.

5.2.2 Upiti

Upit predstavlja zahtev za podacima iz izvora podataka koji dodatno sadrzi filtere i drugedodatne podatke, koji olaksavaju pretragu podataka i definisu nacin vracanja rezultata. Iakoimaju veoma razlicitu ulogu od komandi, implementacija baznih klasa upita veoma je slicnaimplementaciji baznih klasa komandi. Jedina veoma bitna razlika je u tome sto se handleriupita implementiraju u sloju pristupa podacima, umesto u aplikacionom sloju, kao sto je toslucaj na komandnoj strani.

Pocinje se definisanjem baznih interfejsa, koji nasleduju svi upiti. Interfejs jedino definisetip rezultata koji upit vraca.

50

Page 52: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 30: Osnovni interfejs upita

public interface IQuery<TResult>{}

Kako u aplikaciji cesto postoji zahtev za zadavanjem upita, koji vraca vise od jednogelementa, ukupan broj elemenata i podrzava stranicenje, definisana je apstraktna klasa upitaBaseFilterQuery koju nasleduju svi upiti te vrste.

Listing 31: Osnovna klasa filter upita

public abstract class BaseFilterQuery<T> : IQuery<BaseFilterResult<T>>{

Konstruktori ...public int Limit { get; set; }public int Offset { get; set; }

}

Ova klasa, takode, je genericka i nasleduje interfejs IQuery sa parametrom tipa Base-FilterResult, koji sadrzi listu elemenata tipa T i ukupan broj elemenata, koji zadovolja-vaju kriterijume upita. Takode, klasa sadrzi i parametre koji ogranicavaju rezultat. Limitogranicava broj elemenata u rezultatu, dok Offset sluzi za stranicenje rezultata.

Listing 32: Objekat za filter rezultate

public class BaseFilterResult<T>{

public uint TotalCount { get; set; }public IList<T> Results { get; set; }

}

Implementacija upita i hendlera za BaseFilterQuery vrstu upita nece biti opisana u da-ljem tekstu, s obzirom na to da je implementacija interfejsa BaseFilterQuery analogna imple-mentaciji intrefejsa IQuery. Sledeci korak je definisanje interfejsa za sve klase koje obradujuupit.

Listing 33: Osnovni interfejs hendlera upita

public interface IQueryHandler<in TQuery, out TResult> where TQuery : IQuery<TResult>{

TResult Handle(TQuery query);}

Svaka klasa koja obraduje upit mora da zada dva genericka parametra interfejsu IQue-ryHandler. Prvi parametar TQuery predstavlja tip upita koji obraduje, dok drugi parametarTResult predstavlja tip rezultata koji se vraca. Definisan je metod Handle, koji je zaduzenza obradu upita i koji ima povratni tip TResult.

51

Page 53: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Sada definisemo osnovni interfejs za sve dispecere upita.

Listing 34: Osnovni interfejs dispecera upita

public interface IQueryDispatcher{

TResult Execute<TQuery, TResult>(TQuery query)where TQuery : IQuery<TResult>;

}

Intefejs IQueryDispatcher sadrzi metodu Execute koja je genericka sa dva parametra,TQuery, koji predstavlja tip upita i nalazi se kao ulazni parametar metode i TResult, kojipredstavlja tip rezultata obrade upita. Konkretna implementacija interfejsa postignuta jeupotrebnom Unity kontejnera.

Listing 35: Unity dispacer upita

public class UnityQueryDispatcher : IQueryDispatcher{

private readonly IUnityContainer _container;

public UnityQueryDispatcher(IUnityContainer container){

_container = container;}

public TResult Execute<TQuery, TResult>(TQuery query) where TQuery : IQuery<TResult>{

if (query == null){

throw new ArgumentNullException("query");}var handler = _container.Resolve<IQueryHandler<TQuery, TResult>>();

if (handler == null){

throw new QueryHandlerNotFoundException(typeof (TQuery));}

return handler.Handle(query);}

}

Kao i za komandni dispecer, zahteva se referenca na Unity kontejner, koji se kasnijekoristi kod razresavanja tipa klase i za kreiranje njene instance, kako bi se obradio upit. Umetodi Execute prvo se proverava da li je uopste upit poslat, ukoliko jeste, onda se zahteva odkontejnera da kreira instancu klase koja obraduje upit. Ukoliko je uspesno kreirana instancaklase za obradu upita, poziva se njegova metoda za obradu upita.

Sledi implementacija objekta upita koji se koristi za preuzimanje detalja o fakturi prekojedinstvenog identifikatora FakturaId.

52

Page 54: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 36: Primer upita

public class GetFakturaByIdQuery : IQuery<FakturaEdit>{

public long FakturaId { get; set; }}

Upit GetFakturaByIdQuery sluzi za pretragu fakture po ID-u fakture. Implementiradirektno interfejs IQuery sa generickim tipom rezultata FakturaEdit.

Kako je implementacija hendlera upita komplikovana, i kako se hendleri implementirajuu sloju pristupa podacima, njihova implementacija opisuje se kasnije.

53

Page 55: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

6 Sloj pristupa podacima - komandna strana

U danasnje vreme, izvor podataka za aplikaciju gotovo je uvek baza podataka. Za imple-mentiranje sloja pristupa izvoru podataka koriste se, obicno, gotove biblioteke koje skrivajusvu logiku kreiranja upita nad bazom podataka. Takve biblioteke su NHibernate i Enti-tyFramework. Problem kod ovih biblioteka je sto je sva implementirana logika sakrivena odprogramera. Cesto se desava da su one optimizovane za odredeni softver za rad sa bazompodataka, dok za ostale imaju bagove, koje je, obicno, vrlo tesko otkriti i ispraviti, potrebnoje vreme za upoznavanje sa bibliotekom, a desava se da novije verzije biblioteka nisu kom-patibilne sa starijim verzijama. Cilj naseg rada je upoznati se sa arhitekturom pristupabazi podataka, tako da u projektu nije upotrebljana nijedna biblioteka. U nastavku tek-sta, opisacemo paterne koji ucestvuju u izgradnji sloja za pristup izvoru podataka (u ovomslucaju je to baza podataka), njihovu medusobnu interakciju i nacin na koji su implementi-rani u samom projektu.

6.1 Repozitorijum

Za ovaj patern moze se reci da predstavlja kicmu sloja za pristup podacima. Prethodno,ovaj patern opisali smo iz ugla DDD-a, sada cemo opisati sam patern i nacin na koji jeimplementiran u projektu.

Repozitorijum posreduje izmedu biznis sloja i sloja pristupa podacima, pruzajuci inter-fejs biznis sloju nalik interfejsu kolekcije objekata u memoriji. Kod za pristup izvoru poda-taka sakriven je u implementaciji repozitorijuma. Repozitorijum enkapsulira skup objekatasacuvanih na nekom medijumu i dozvoljene operacije nad njima, na objektno orijentisaninacin. Repozitorijum, takode, podrzava cisto razdvajanje biznis sloja, od sloja pristupapodacima i, takode, omogucava jednosmernu zavisnost sloja domena prema sloju pristupapodacima.

6.1.1 Interakcije repozitorijuma

Dijagram 10 prikazuje interakcije repozitorijuma sa klijentom i izvorom podataka.

Dijagram 10: Interakcija repozitorijuma sa klijentom i izvorom podataka

54

Page 56: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Repozitorijum predstavlja most izmedu podataka i operacija nad podacima koji se na-laze u razlicitim slojevima. Repozitorijum mapira podatke iz domena gde su podaci slabotipizirani (kao sto je baza podataka), u domen gde su podaci strogo tipizirani, (kao sto suagregati modela domena). Repozitorijum izvrsava odgovarajuce upite nad izvorom podatakai mapira rezultate u objekte. Repozitorijumi cesto koriste paterne Izlaz na tabelu podataka(eng. Table data gateway) i Fabriku (eng. Factory) kako bi mapirali podatke iz tabela bazepodataka u objektni model i kreirali agregate.

Klijentski kod koristi repozitorijum na isti nacin, kao sto koristi kolekciju objekata umemoriji. Kolekcija sadzi agregate koje je moguce filtrirati, ubaciti novi, azurirati ili obrisatipostojece. Cinjenica da objekti, tipicno, nisu smesteni direktno u repozitorijum nije izlozenakodu klijenta. Naravno, kod koji koristi repozitorijum treba da bude svestan da kolekcijamoze da se mapira na tabelu u bazi podataka sa stotinama hiljada redova, tako da pozivmetode All nad takvim repozitorijumom moze napraviti velike probleme.

Tipicni klijent repozitorijuma je aplikacioni servisni sloj. Repozitorijum definise metodeza pristup podacima koje su potrebne servisnom sloju za obavljanje biznis zadatka. Repozi-torijum obicno se implementira upotrebom neke ORM (Object relational mapper) bibliotekekoja obicno radi sav tezak i dosadan posao mapiranja podataka. Klijenti nikada ne treba damisle o SQL-u i mogu pisati kod cisto u terminima objekata.

Situacije sa vise mogucih izvora podataka, predstavljaju upotrebne slucajeve, gde vidimoda repozitorijum dolazi do izrazaja. Izvor podataka za repozitorijum ne mora biti samo re-laciona baza podataka. Iz ovog razloga repozitorijum moze biti veoma koristan u sistemimasa vecim brojem izvora podataka. Nekada smo zainteresovani da koristimo jednostavnu ko-lekciju podataka u memoriji (kada zelimo da testiramo zbog boljih performansi). Takode,razumljivo je cuvati neke objekte u memoriji u toku izvrsavanja aplikacije, (na primer, de-finicioni podaci kojima se cesto pristupa, a koji se retko menjaju). Jos jedan primer gderepozitorijum moze biti koristan je kada se kao izvor podataka koristi internet servis. Ser-vis podatke moze slati repozitorijumu u XML ili Json formatu, gde bi bio implementiranXML/Json repozitorijum koji bi pravio biznis objekte iz tog izvora.

Objekti u sloju domena mogu biti slicni ili isti modelu podataka, ali konceptualno onisu veoma razlititi. Model domena je apstrakcija biznis problema, bogat je biznis logikomi opisuje ponasanje kroz metode. Model podataka jednostavno je struktura skladistenja zasnimanje stanja objekta domena u zadato vreme. Zadatak repozitorijuma je drzati ova dvamodela odvojenim i ne dozvoliti im da se pomesaju u jedan.

Poenta repozitorijuma nije napraviti testiranje jednostavnijim ili jednostavnije promenititehnologiju skladistenja podataka. Poenta je, vec drzati biznis sloj razdvojen od tehnickeimplementacije sloja pristupa podacima, tako da biznis model moze nezavisno evoluirati bezda bude pogoden tehnologijom kojom je sloj pristupa podacima implementiran.

Repozitorijum pruza funkcionalnost koja je slicna funkcionalnosti koju pruza obicna ko-lekcija objekata, ali, takode, ukljucuje i funkcionalnost pravljenja upita nad kolekcijom.Postoje dva nacina na koji se mogu vrsiti upiti nad repozitorijumom:

1. Moze mu se proslediti objekat koji predstavlja upit (Specifikacija ili Objekat Upit).

2. Repozitorijum moze izloziti metode koje odreduju specificni biznis kriterijum; klijent

55

Page 57: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

poziva specijalizovani metod nad repozitorijumom, a repozitorijum formira upit u imeklijenta.

6.1.1.1 Repozitorijum za preuzimanje podataka

Repozitorijum moze funkcionisati u dva smera: preuzimanje i snimanje podataka.

Dijagram 11: Interakcije repozitorijuma prilikom preuzimanja podataka

Kada se koristi za preuzimanje objekata, repozitorijumu se prosleduje upit. Ovaj upitmoze biti specifican objekat ili parametrizovani metod. Repozitorijum je odgovoran zaizvrsavanje tih upita. Kada se takav metod pozove, Repozitorijum kontaktira TDG kakobi preuzeo sirove podatke iz baze podataka. Moguce je da repozitorijum pozove vise TDGobjekta za preuzimanje podataka kako bi bio u mogucnosti da izgradi finalni rezultat. TDGpruza sirove objektne podatke (DTO objekti ili mapa sa vrednostima). Repozitorijum ondavrsi neophodne transformacije tih podataka i poziva odgovarajuce metode fabrika kako bi sekonstruisao rezultat.

6.1.2 Repozitorijum za snimanje podataka

Drugi nacin na koji repozitorijum radi je pruzanje logike potrebne za snimanje objekata.Snimanje moze biti jednostavno, kao sto je serijalizacija objekta i njegovo slanje na TDGkako bi se izvrsila serijalizacija u fajl ili sofisticirano, kao sto je kreiranje niza informacija sasvim poljima i stanjem objekta.

Kada se koristi za snimanje podataka, klijentska klasa direktno komunicira sa fabrikom,izvrsi se kod logike obrade zahteva i onda se objekat salje repozitorijumu na snimanje. Re-pozitorijum snima objekte upotrebom TDG objekata i opciono kesira objekte u memoriju.

6.1.3 Povezivanje

Dijagram 13 predstavlja pogled visokog nivoa na nacin implementacije repozitorijuma imedusobnu saradnju klasa u sloju pristupa podacima.

56

Page 58: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 12: Interakcije repozitorijuma prilikom snimanja podataka

Dijagram 13: Zavisnosti repozitorijuma

U centru se nalazi repozitorijum. Na levoj strani, nalaze se TDG objekti koji se daljevezuju na izvor podataka. Na desnoj strani, nalazi se fabrika ciji je zadatak kreiranje objekatadomena. Repozitorijumi se cesto implementiraju tako da zavise od interfejsa TDG objekatai fabrika. Sa gornje strane, nalazi se klijentska klasa koja zahteva uslugu od repozitorijuma.

6.1.4 Implementacija Repozitorijuma

Postoje razlicite implementacije repozitorijuma i postoji dosta clanaka i debata o nacinuimplementiranja repozitorijuma. Zestoka rasprava vodi se o tome da li je dozvoljeno genera-lizovati repozitorijum. Na primer, ne podrzavaju sve klase repozitorijuma brisanje podataka.Ukoliko bi se generalizovala implementacija repozitorijuma, ta metoda ne bi mogla da se im-plementira za te specijalne slucajeve. Sa druge strane, generalizacijom se izbegava duplikacijakoda i kasnije se olaksava rad. Zbog toga, najcesce, koristi se hibridni nacin implementacije

57

Page 59: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

repozitorijuma, gde svi repozitorijumi imaju isti interfejs i dodaju svoje prilagodene metodeza rad sa agregatima.

Dijagram 14: Dijagram klasa za repozitorijum proizvod

Osnovni interfejs repozitorijuma je genericki, cime se omogucuje laka upotreba u ostalimdelovima aplikacije. Konkretni interfejsi repozitorijuma nasleduju bazni interfejs repozitori-juma i dopunjuju ga svojim metodama.

Klijenti znaju i rade sa konkretnim interfejsima repozitorijma. Obicno se i u imple-mentaciji kreira genericka bazna klasa koja implementira zajednicku funkcionalnost za svekonkretne repozitorijume. Na kraju, konkretna implementaciona klasa nasleduje baznu klasuimplementacije i implementira konkretni interfejs repozitorijuma.

6.1.5 Implementacija bazne klase repozitorijuma

Glavni zadatak bazne klase repozitorijma je implementirati zajednicku funkcionalnostza sve klase repozitorijma. Klasa implementira osnovni interfejs repozitorijma i interfejsjedinice rada. Bazna klasa je, takode, genericka i apstraktna (kao i sve ostale bazne klase).Kako podrzava mapiranje iz agregata u DTO objekat, zahteva genericke parametre za ovedve klase.

Listing 37: Definicija bazne klase repozitorijuma

public abstract class BaseRepository<TAggregate, TDto>: IBaseRepository<TAggregate>, IUnitOfWorkRep where TAggregate : AggregateRoot

Sledeca grupa metoda koju bazna klasa repozitorijuma sadrzi jesu metode za implemen-tiranje osnovnog interfejsa repozitorijuma.

58

Page 60: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 38: Implementacija CRUD metoda

protected abstract TAggregate BuildAggregate(TDto dtoObject);

public TAggregate FindBy(long id){

if (_unitOfWork.CheckIfExists(id, typeof (TAggregate)))return _unitOfWork.GetById<TAggregate>(id);

TDto dto = _tableGateway.GetById(id);return BuildAggregate(dto);

}

public void Update(TAggregate item){

_unitOfWork.RegisterChanged(item, this);}

public void Add(TAggregate item){

_unitOfWork.RegisterAdded(item, this);}

public void Remove(TAggregate item){

_unitOfWork.RegisterRemoved(item, this);}

public TAggregate this[long id]{

get { return FindBy(id); }set{

if (FindBy(id) == null){

Add(value);}else{

Update(value);}

}}

FindBy metoda pretrazuje agregat za dati jedinstveni identifikator (id). Proverava daobjekat nije vec kesiran u objektu jedinice rada, a ako jeste, onda se objekat preuzima izmemorije i vraca pozivaocu. Ukoliko objekat nije u memoriji, onda poziva se TDG objekatkako bi se preuzeo DTO objekat iz izvora podataka, a onda poziva se apstraktna metodaBuildAggregate ciji je zadatak da sagradi agregat od datog DTO objekta.

Metode Upate, Add i Remove jednostavno prijavljuju promene objektu jedinice rada,bez ikakvog upisa na izvor podataka. Takode, jezik c# podrzava sintaksu inteksera, kakobi se olaksao rad sa repozitorijumima implementirana je metoda this, cija se implementa-cija svodi na poziv prethodno opisanih metoda. Kako bi repozitorijum objekti mogli dakomuniciraju sa objektima jedinice rada potrebno je da klase repozitorijuma implementi-raju interfejs IUnitOfWorkRep koji definise metode za stvarno snimanje agregata na izvorpodataka. Implementacija interfejsa IUnitOfWorkRep sastoji se iz sledecih metoda:

59

Page 61: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 39: Implementacija interfejsa IUnitOfWorkRep

protected abstract TDto CreateDto(TAggregate item);

protected virtual void PersistNewAggregate(TAggregate item){

TDto itemDto = CreateDto(item);_tableGateway.InsertItem(itemDto);

}

protected virtual void PersistUpdatedAggregate(TAggregate item){

TDto itemDto = CreateDto(item);_tableGateway.UpdateItem(itemDto);

}

protected virtual void PersistDeletedAggregate(TAggregate item){

TDto itemDto = CreateDto(item);_tableGateway.DeleteItem(itemDto);

}

Sve metode su virtuelne i u njima je implementirano osnovno mapiranje upotrebom osnov-nog DTO objekta. CreateDto je apstraktna metoda koju svaka klasa implementira i kojanasleduje baznu klasu i njen zadatak je konstruisanje DTO objekta iz agregata. Sve metodeprimaju agregat kao parametar, pozivaju metodu za mapiranje agregata u DTO objekat, ina kraju, pozivaju TDG objekat za izvrsavanje stvarne akcije nad izvorom podataka.

6.1.6 Implementacija repozitorijuma ProizvodRepository

Za svaki agregat postoji posebna klasa repozitorijuma. U daljem tekstu opisacemo im-plementaciju klase repozitorijuma implementiranu za agregat proizvoda.

Listing 40: Klasa ProizvodRepository

public class ProizvodRepository : BaseRepository<Proizvod, ProizvodGateway.ProizvodDto>,IProizvodRepository

Klasa implementira interfejs IProizvodRepository, a ujedno nasleduje baznu klasu Ba-seRepository sa paremetrima Proizvod - klasa agregata i ProizvodDto - objekat transferapodataka. Sledi ostatak klase:

Listing 41: Metode

public Proizvod GetByNaziv(string naziv){

var resDto = _proizvodGateway.SelectByName(naziv);

if (resDto == null) return null;return BuildAggregate(resDto);

}

protected override Proizvod BuildAggregate(ProizvodGateway.ProizvodDto dtoObject)

60

Page 62: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

{return _proizvodFactory.BuildAggregate(dtoObject);

}

protected override ProizvodGateway.ProizvodDto CreateDto(Proizvod item){

ProizvodGateway.ProizvodDto res = new ProizvodGateway.ProizvodDto();res.ProizvodId = item.Id;res.Naziv = item.Naziv.Naziv;res.BarKod = item.BarKod.Kod;res.NetoKolicina = item.NetoKolicina;...

return res;}

private void InsertGrupeProizvoda(Proizvod item){

ProizvodGrupaGateway.ProizvodGrupaDto temp = new ProizvodGrupaGateway.ProizvodGrupaDto();temp.ProizvodId = item.Id;foreach (var grupa in item.ProizvodGrupaIds){

temp.GrupaId = grupa;_proizvodGrupaGateway.InsertItem(temp);

}}

private void DeleteAllProizvodGrupe(long proizvodId){

_proizvodGrupaGateway.DeleteByProizvodId(proizvodId);}

protected override void PersistUpdatedAggregate(Proizvod item){

base.PersistUpdatedAggregate(item);DeleteAllProizvodGrupe(item.Id);InsertGrupeProizvoda(item);

}

protected override void PersistDeletedAggregate(Proizvod entity){

DeleteAllProizvodGrupe(entity.Id);base.PersistDeletedAggregate(entity);

}

protected override void PersistNewAggregate(Proizvod item){

base.PersistNewAggregate(item);InsertGrupeProizvoda(item);

}

GetByNaziv metoda iz interfejsa IProizvodRepository, implementirana je tako sto se prvopoziva TDG objekat za proizvode, kako bi se selektovao proizvod po nazivu. Ukoliko jepronaden proizvod po nazivu, onda se kreira agregat pozivom fabrike.

CreateDto jednostavno mapira agregat u DTO objekat koji TDG objekat koristi za ma-piranje na izvor podataka. Takode, ovde su prepisane sve metode za snimanje i preuzimanjeagregata iz izvora podataka, jer je pored snimanja samog DTO objekta proizvoda potrebnosnimiti i grupe u koje je proizvod uclanjen.

InsertGrupeProizvoda metoda prima agregat, prolazi kroz sve grupe u koje je proizvod

61

Page 63: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

uclanjen i dodaje ih na izvor podataka. DeleteAllProizvodGrupe jednostavno poziva metoduna TDG objektu koji brise proizvode za dati objekat.

PersistUpdatedItem poziva baznu klasu koja vrsi azuriranje samog proizvoda, a zatimbrise sve grupe u kojima je proizvod bio uclanjen i uclanjuje opet proizvod u nove grupe.Na slicnom principu rade i metode PersistDeletedItem i PersistNewItem.

6.2 Jedinica Rada (eng. Unit Of Work – UOW)

Kada se radi sa podacima u memoriji koje je potrebno snimiti na izvor podataka, veomaje bitno imati evidenciju o izmenama nad objektima.

Jedan od najjednostavnijih nacina sinhronizacije objektnog modela sa izvorom podatakaje istovremena primena promena na objekat i izvor podataka. Ovo moze voditi premavelikom broju poziva prema bazi, sto na kraju utice na performanse aplikacije. Dalje, zahtevaotvorenu transakciju tokom citave obrade zahteva, sto je neprakticno. Situacija je, cak, igora kada je potrebno voditi evidenciju o objektima koji su ucitani (kako bi se izbeglanekonzistentna citanja).

Jedinica rada vodi evidenciju o svim promenama tokom biznis transakcije koje moguimati uticaj na izvor podataka. Kada se obrada korisnickog zahteva zavrsi zadatak jedinicerada je koordinisanje akcijom snimanja promena na izvor podataka.

Prilikom izmene objekata, koje je potrebno snimiti na izvor podataka, klijent kreirainstancu jedinice rada kako bi vodio evidenciju o promenjenim objektima. Svaki put kadase kreira, promeni ili izbrise neki domen objekat potrebno je obavestiti objekat jedinice radao promeni.

Kljucna stvar kod jedinice rada je ta da kada dode vreme za komitovanje, jedinica radaodlucuje sta je potrebno uraditi. Otvara transakciju, proverava da objekti u bazi nisu iz-menjeni od strane druge transakcije i snima podatke u bazu. Aplikacioni programeri nikadaeksplicitno ne pozivaju metode za azuriranje podataka nad bazom podataka. Na ovaj nacinoni nemaju potrebu da vode evidenciju o promenama ili da brinu o tome kako referencijalniintegritet u bazi podataka utice na redosled promena u aplikacionom sloju.

Naravno, kako bi sve ovo funkcionisalo, objekti jedinice rada moraju biti svesni objekatao kojima vode evidenciju. Postoji nekoliko nacina na koje je moguce izvestiti objekat jedinicerada o promenama:

• Sa registracijom klijenta korisnik objekta mora da zapamti da je potrebno registro-vati promenjeni objekat instanci jedinici rada. Svi objekti koji nisu registrovani necebiti snimljeni na izvor podataka prilikom komita. Ovo omogucuje da zaboravnost pro-uzrukuje probleme, ali, takode, daje fleksibilnost dopustajuci promene u memoriji beznjihovog snimanja na izvor podataka. Ipak, ovo svojstvo pre moze da izazove konfuzijuu odnosu na benefite koje pruza. U tu svrhu bolje je napraviti eksplicitnu kopiju.

• Sa registracijom objekta, odgovornost registrovanja promena smesta se na sameobjekte. Obicno se u ovom slucaju registracione metode smestaju u samim objektima.Kada se objekat ucita iz izvora podataka, on se registruje kao “cist”, a kada se promeni

62

Page 64: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

stanje objekta, objekat se registruje kao “zaprljan”. Kako bi ovo radilo, objekat jedinicerada mora da se prosledi svakom pojedinacnom objektu u domenu ili mora da se nalazina nekom poznatom mestu u projektu kome svi objekti domena mogu da pristupe.Prosledivanje objekta jedinice rada je zamorno ali obicno nije problem imati instancuobjekta u nekoj vrsti sesionog objekta.

• Pored ova dva pristupa postoji i treci pristup koji je uveden kasnije. Ovaj pristupprepusta odgovornost registrovanja promena objektima repozitorijuma. Objekat jedi-nice rada izvrsava sve neophodne pozive prema izvoru podataka u ime repozitorijuma.Dobit kod ovog pristupa je da registracije o promenama objekata nisu vidljive klijentui desavaju se u pozadini.

6.2.1 Implementacija jedinice rada

Takode, dosta se diskutuje i o nacinu implementiranja paterna jedinica rada. Na internetuje moguce naci razlicite implementacije ovog paterna. Problem je pronaci odgovarajucuimplementaciju tj. onu koja se najbolje uklapa u projekat.

Kako od nacina implementiranja jedinice rada uveliko zavisi izgled klijentskog koda, de-finisu se prvo zahtevi klijentskog koda. Svaka biznis transakcija moze zahtevati razlicitetipove objekata repozitorijuma. Takode, potrebno je omoguciti kreiranje objekata repozito-rijuma nezavisno od objekata jedinice rada. Osim toga, u toku biznis transakcije svi objektirepozitorijuma moraju vrsiti upis podataka pod istom transakcijom koju je objekat jedi-nice rada inicijalizovao. Registrovanje izmenjenih objekata prepusta se repozitorijumu, kakobi se izbegle nenamerne greske programera i sklonio sav tehnicki kod iz klijentskog koda.Klijentski kod jedinice rada treba da predstavlja varijaciju sledeceg koda:

Listing 42: Primer klijentskog koda jedinice rada

using(unitOfWork){// obrada klijentske biznis transakcijeunitOfWork.Commit();

}

Kako bi se zadovoljili ovi klijentski zahtevi objekat jedinice rada mora da implementirasamo jednu metodu bitnu za klijenta Commit. Kako objekti jedinice rada rade direktnosa izvorom podataka, i verovatno postoji potreba da se oslobode resursi nakon transakcije,unistavanje ovakvog objekta je eksplicitno. Sledeci interfejs definisan je u tu svrhu:

Listing 43: Osnovni interfejs jedinice rada

public interface IUnitOfWork : IDisposable{

void Commit();}

Sada kada imamo definisan interfejs, potrebno je pruziti implementaciju. Implementacija se

63

Page 65: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

nalazi u sloju pristupa podacima i u daljem tekstu opisujemo nacin na koji je implementiraninterfejs. Prvo se opisuje nacin na koji je implementirana evidencija izmenjenih objekata.

6.2.1.1 Evidencija izmenjenih objekata

U daljem tekstu se opisuje nacin implementiranja odgovornosti evidentiranja izmenjenihobjekata tokom biznis transakcije. U osnovi vrsi se evidencija tri vrste promena nad objek-tima: brisanje, dodavanje i izmena objekata. Prvo se definisu objekti i promenljive koji sluzeza predstavljanje svih tih promena.

Listing 44: Objekti za evidenciju izmenjenih objekata

private enum UowOperation { Add, Update, Delete }

private class PersistOperation{

public AggregateRoot Aggregate { get; set; }public IUnitOfWorkRep Repository { get; set; }public UowOperation Operation { get; set; }

}

private readonly Dictionary<Tuple<long, Type>, AggregateRoot> _cachedAggregates;private readonly LinkedList<PersistOperation> _uowPersistActions;

Kreirana je enumeracija koja sadrzi tri vrednosti za svaku od operacija. Takode, kreiran jeobjekat koji predstavlja akciju za snimanje i sastoji se od agregata koji je potrebno snimiti,repozitorijuma koji treba da sprovede operaciju snimanja i operacije, koja indikuje kojumetodu repozitorijuma je potrebno pozvati. Hes mapa cachedAggregates sluzi za kesiranjeagregata kako se ne bi vise puta preuzimao isti agregat iz izvora podataka u toku transakcije.Takode, ovim kesiranjem izbegava se nekonzistentno citanje. Lista ouwPersistActions drzireferencu na sve operacije koje je potrebno izvrsiti u redosledu u kome su ubacene. Sada jepotreban kod koji pruza interfejs klijentskim klasama za registrovanje promena:

Listing 45: Metode za registraciju izmenjenih objekata

public void RegisterAdded(AggregateRoot entity, IUnitOfWorkRep repository){

var addOperation = new PersistOperation { ... }_uowPersistActions.AddLast(addOperation);_cachedAggregates.Add(new Tuple<,>(entity.Id, entity.GetType()), entity);

}

public void RegisterChanged(AggregateRoot entity,IUnitOfWorkRep repository)

{var changedTuple = new Tuple<,>(entity.Id, entity.GetType());if (_cachedAggregates.ContainsKey(changedTuple)) return;

var updateOperation = new PersistOperation { ... }_uowPersistActions.AddLast(updateOperation);_cachedAggregates.Add(changedTuple, entity);

}

64

Page 66: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

public void RegisterRemoved(AggregateRoot entity,IUnitOfWorkRep repository)

{var delTuple = new Tuple<,>(entity.Id, entity.GetType());if (_cachedAggregates.ContainsKey(delTuple)) return;

var deleteOperation = new PersistOperation { ... }_uowPersistActions.AddLast(deleteOperation);

}

Kako se samo nove operacije dodaju u listu uowPersistActions, implementacija ovihmetoda veoma je jednostavna. Ovde, takode, mozemo da vidimo implementaciju kesiranjaobjekata koji ucestvuju u toku transakcije. Kesiranje ovde ima dvojaku ulogu, prva je samokesiranje podataka, a druga je sprecavanje duplog unosa iste operacije za jedan agregat utoku transakcije. Sledi kod metoda koje pruzaju interfejs repozitorijumima za preuzimanjekesiranog agregata.

Listing 46: Metode za preuzimanje kesiranih objekata

public bool CheckIfExists(long id, Type t){

return _cachedEntities.ContainsKey(new Tuple<long, Type>(id, t));}

public TEntity GetById<TEntity>(long id) where TEntity : AggregateRoot{

return (TEntity) _cachedEntities[new Tuple<long, Type>(id, typeof (TEntity))];}

Metoda CheckIfExists proverava da li je tip agregata sa datim ID-em kesiran. Dokgenericka metoda GetById vraca agregat datog tipa iz kesa.

6.2.1.2 Snimanje podataka

U ovom delu opisujemo nacin na koji se upravlja konekcijom, transakcijom i kako sesnimaju sve promene na izvor podataka. Komitovanje promena postize se sledecim kodom:

Listing 47: Komitovanje

public IDbConnection Connection { get; private set; }public IDbTransaction Transaction { get; private set; }

public void Commit(){

// Process Domain Events before transaction is startedProcessDomainEvents();

_connectionPool.LockConnection(Connection);Transaction = Connection.BeginTransaction();try{

PersistData();Transaction.Commit();

}

65

Page 67: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

catch (Exception e){

Clear();Transaction.Rollback();throw;

}finally{

Transaction.Dispose();_connectionPool.UnlockConnection(Connection);

}}public void Dispose(){

if (_connectionPool.IsLocked(Connection))_connectionPool.UnlockConnection(Connection);

}

˜UnitOfWork() { Dispose(); }

Iz prilozenog koda vidimo da UnitOfWork klasa drzi reference na aktivnu konekciju iaktivnu transakciju. Metoda Commit poziva se prilikom snimanja svih promena na izvorpodataka. Pre nego sto se krene sa snimanjem svih promena na izvor podataka procesirajuse prvo svi dogadaji domena. Nakon toga se eksplicitno zahteva upotreba tj. zakljucavanjeobjekta konekcije, startuje se transakcija i poziva se metoda za snimanje podataka. Nakraju transakcije, komituju se promene. Ukoliko dode do greske prilikom snimanja podataka,ponistavaju se sve promene.

Metoda Dispose poziva se prilikom unistavanja objekta i ovde se osigurava da objekatkonekcije nije zakljucan.

Svakako najvaznija i najzanimljivija metoda ove klase je metoda PersistData. Njen za-datak, zapravo, je da snimi podatke na izvor podataka. Implementacija ove metode jejednostavna, jer jednostavno prolazi kroz kolekciju akcija za snimanje i zavisno od akcijepoziva odgovarajuci metod repozitorijuma.

Listing 48: Metoda za snimanje podataka

private void PersistData(){

foreach (var action in _uowPersistActions){

switch (action.Operation){

case UowOperation.Delete:action.Repository.PersistDeletedItem(action.Aggregate);break;

case UowOperation.Update:action.Repository.PersistUpdatedItem(action.Aggregate);break;

case UowOperation.Add:action.Repository.PersistNewItem(action.Aggregate);break;

default:throw new Exception("Not supported Unit of work operation: " + action.Operation);

}}

}

66

Page 68: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Iz prilozenog vidimo da jedinica rada koristi repozitorijume za konacnu komunikaciju saizvorom podataka. Kako bi se omogucila ova funkcionalnost svaki repozitorijum mora daimplementira sledeci interfejs:

Listing 49: Interfejs IUnitOfWorkRep

public interface IUnitOfWorkRep{

void PersistNewItem(AggregateRoot item);void PersistUpdatedItem(AggregateRoot item);void PersistDeletedItem(AggregateRoot item);

}

6.3 Izlaz na tabelu podataka (eng. Table Data Gateway – TDG)

TDG objekat predstavlja objektno orijentisani ekvivalent tabele(ili view objekta) u re-lacionoj bazi podataka. Namera ovog paterna je sakriti interakciju sa tabelom. U vecinislucajeva, TDG objekat saraduje sa relacionim modelom, imajuci 1:1 vezu sa tabelom. U re-lacionoj implementaciju, TDG rukuje SQL upitima, izlazuci specifican interfejs koji odgovarapotrebama za rad sa podacima u tabeli.

TDG je kapija, interfejs na posebnu komponentu ili podsistem koji pruza pristup svojimpodacima apstraktovanjem cele tabele u bazi podataka (fajla ili bilo koje strukture) izainterfejsa jednog objekta. Sve standardne CRUD operacije izvrsavaju se upotrebom metodaTDG objekta. Za SQL bazu podataka, TDG metodi direktno odgovaraju SQL upitima:INSERT, SELECT, UPDATE, DELETE. Argumenti metoda se mapiraju direktno u SQLupite. Generalno, TDG pruza sve metode za pristup izvoru podataka, ukljucujuci i metodeza pretragu.

TDG pruza centralnu tacku pristupa izvoru podataka. Zbog toga, gotovo je nemoguceimplementirati ovaj patern sa objektima koji sadrze interno stanje. TDG objekti ne trebada sadrze stanje, ili da pamte podatke izmedu poziva. TDG objekti se obicno implemen-tiraju bez stanja. Uloga ovih objekata je jednostavno prebacivanje podataka u razlicitereprezentacije.

TDG patern je specijalno koristan kada je kod pristupa baziran na sablonu ili kada sekod automatski generise.

TDG ima jednostavan interfejs, obicno se sastoji od nekoliko metoda pretrage kako bipreuzeo podatke iz baze podataka i CRUD metoda. Svaki metod mapira ulazne parametreu SQL poziv i izvrsava SQL kroz konekciju prema bazi podataka.

Najvarljivija stvar kod TDG paterna je kako on vraca informacije iz upita. Moguce jevratiti jednostavne strukture podataka, kao sto je to mapa. Upotreba mapa za prosledivanjepodataka kroz aplikaciju je losa praksa zato sto se zaobilaze provere u vremenu kompajliranjai ne postoji eksplicitan interfejs. Bolja alternativa je koristiti DTO (Data Transfer Object)objekte.

67

Page 69: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Najveci deo vremena, TDG koristi se za mapiranje podataka na jednu tabelu u bazipodataka. U veoma jednostavnim aplikacijama, moguce je imati jedan TDG objekat kojiupravlja svim metodima za pristup svim tabelama u bazi. Moguce je, takode, imati jedanTDG objekat koji se mapira na jedan view objekat u bazi podataka. TDG objekati se u nekimsituacijama kreiraju za komplikovanije upite koji se ne cuvaju u bazi podataka. Ocigledno jeda TDG objekti koji su bazirani na view objekte u bazi cesto ne mogu da azuriraju podatkei tako oni ne sadrze logiku vezanu za brisanje i azuriranje podataka. Medutim, ukolikoje moguce azurirati podatke, onda skrivanje operacija azuriranja iza metode TDG objektapredstavlja veoma dobru tehniku.

6.3.1 Implementacija bazne TDG klase

Kako je zadatak svih TDG klasa mapiranje podataka izmedu razlicih reprezentacija,postoje zajednicke funkcionalsnosti koje sve TDG klase implementiraju. Najbolje mesto zasmestanje tih funkcionalnosti je bazna klasa. Bazna klasa je genericka i apstraktna.

Listing 50: CRUD metode

public abstract class BaseTableGateway<T> : IReadMapper<T>{

public abstract void PrepareSelById(long id, IDbCommand command);public abstract void PrepareInsert(T item, IDbCommand command);public abstract void PrepareUpdate(T item, IDbCommand command);public abstract void PrepareDelete(T item, IDbCommand command);public abstract T Retrieve(IDataReader reader);

Bazna klasa, takode, sadrzi apstrakne metode koje obavljaju osnovne CRUD operacijei koje moraju da implementiraju sve TDG klase. Kako je efikasnije komunicirati sa bazompodataka putem pripremljenih upita, zadatak metoda je da pripreme komandu za datuoperaciju. Takode, ova klasa nasleduje interfejs IReadMapper koji definise metodu Retrive.Ona treba da mapira i kreira DTO objekat iz Reader objekta i implementira je apstraktno.Bazna klasa, takode, nudi korisne metode koje se cesto upotrebljavaju prilikom preuzimanjapodataka iz baze podataka.

Listing 51: CRUD apstraktna implementacija

protected IDbCommand CreateCommand(){

IDbCommand command = UnitOfWork.Current.CreateCommand();return command;

}

public T GetById(long id){

IDbCommand command = CreateCommand();PrepareSelById(id, command);return FetchOneObject(command);

}

public void InsertItem(T item){

68

Page 70: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

IDbCommand command = CreateCommand();PrepareInsert(item, command);ExecuteCommand(command);

}

public void UpdateItem(T item){

IDbCommand command = CreateCommand();PrepareUpdate(item, command);ExecuteCommand(command);

}

public void DeleteItem(T item){

IDbCommand command = CreateCommand();PrepareDelete(item, command);ExecuteCommand(command);

}

Ove metode zapravo sprovode osnovne CRUD operacije. Metoda CreateCommand po-ziva se kada je potrebno kreirati novu komandu u okviru trenutne transakcije i ona samopreusmerava poziv jedinici rada koja zapravo kreira komandu. Algoritam je slican za sveostale metode. Prvo se kreira komanda, onda se pozove operacija koja kreira i pripremaSQL upit za izvrsavanje i na kraju se izvrsava komanda/upit. Izuzetak je GetById metoda,gde se na kraju vraca procitani DTO objekat.

Sledi jos nekoliko korisnih metoda koje se nalaze u baznoj klasi:

Listing 52: Usluzne metode

protected static int BoolToInt(bool boolValue){

return boolValue ? 1 : 0;}

protected static void AddParameter(IDbCommand command, string name, object value){

IDbDataParameter param = command.CreateParameter();param.ParameterName = "@" + name;param.Value = value;command.Parameters.Add(param);

}

public static void ExecuteCommand(IDbCommand command){

CommandExecutor.ExecuteCommand(command);}

public T FetchOneObject(IDbCommand command){

return CommandExecutor.FetchOneObject(command, this);}

public IList<T> FetchMulObjects(IDbCommand command){

return CommandExecutor.FetchMulValues(command, this);}

69

Page 71: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Ove metode korisne su za sve klase koje nasleduju baznu klasu, kao i za samu baznu klasu.BoolToInt metoda za logicku vrednost vraca brojcanu vrednost jer vecina baza podataka nepodrzavaju logicki tip podataka. AddParameter metoda se koristi prilikom pripreme upita,gde se parametar mapira na odredenu vrednost. Metode ExecuteCommand, FetchOneObjecti FetchMulObjects, su usluzne metode, ali kako se ta funkcionalnost razlikuje od ostatkafunkcionalnosti, njihova implementacija je izdvojena u posebnu klasu CommandExecutor.

6.3.1.1 Klasa Command Executor

CommandExecutor je staticka klasa koja implementira tri staticke metode opisane ranije.

Listing 53: Metoda za preuzimanje vise redova podataka iz baze podataka

public static IList<TResult> FetchMulValues<TResult>(IDbCommand command,IReadMapper<TResult> mapper)

{IList<TResult> result;IDataReader reader = null;try{

result = new List<TResult>();

Log.Log("Fetch Multiple Values:" + command.CommandText, Category.Debug, Priority.Low);

reader = command.ExecuteReader();

while (reader.Read()){

result.Add(mapper.Retrieve(reader));}if (!reader.IsClosed) reader.Close();

}catch (Exception e){

Log.Log("FetchMulValues exception:" + e, Category.Exception, Priority.High);if (reader != null && !reader.IsClosed) reader.Close();throw;

}

return result;}

FetchMulValues je genericka metoda, koja vraca listu objekata tipa TResult. Ima dvaparametra; prvi je komanda u koju se nalazi SQL upit, a drugi je referenca na objekat kojizna kako da iscita rezultate upita u objekat i koji implementira interfejs IReadMapper zaobjekat tipa TResult. Kako se ovde zapravo komunicira sa bazom podataka postoji velikaverovatnoca za izbijanje greske u ovom kodu. Zbog toga se sve loguje kako bi se kasnije lakseutvrdio uzrok greske. Izvrsava se upit nad bazom podataka i iscitavaju se objekti. Kadase zavrsi sa citanjem, zatvara se objekat. Ukoliko se desi neka greska, loguje se ta greska izatvara se objekat citanja (ukoliko je to moguce).

Metoda FetchOneObject je samo specijalni slucaj prethodno opisane metode i iz tograzloga nece biti opisana.

70

Page 72: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 54: Izvrsavanje komande

public static void ExecuteCommand(IDbCommand command){

try{

Log.Log("Execute command:" + command.CommandText, Category.Debug, Priority.Low);

int cmdCnt = command.ExecuteNonQuery();

Log.Log("Affected Count:" + cmdCnt, Category.Debug, Priority.Low);}catch (Exception e){

Log.Log("Command execution failed - " + e, Category.Exception, Priority.High);throw;

}}

Metoda ExecuteCommand izvrsava komandu u kontrolisanim uslovima. Loguje tekstkomande i izvrsava tu komandu. U slucaju greske, loguje se greska.

6.3.2 Implementacija ProizvodGateway klase

Kako bi kompletirali pricu o TDG klasama, potrebno je opisati implementaciju jedne kon-kretne klase. Kako je ranije opisana klasa repoztorijuma ProizvodRepository, sada opisujemoklasu ProizvodGateway.

Listing 55: Definicija ProizvodGateway klase

public class ProizvodGateway : BaseTableGateway<ProizvodGateway.ProizvodDto>{

Klasa ProizvodGateway nasleduje klasu BaseTableGateway, gde se kao genericki parame-tar zadaje DTO klasa ProizvodDto.

Listing 56: Staticka klasa FieldNames

public static class FieldNames{

public static readonly string Id = "proizvod_id";public static readonly string Naziv = "naziv";public static readonly string NetoKolicina = "neto_kolicina";...

}

Uvedena je praksa da svaka konkretna TDG klasa sadrzi statucku klasu u sebi podnazivom FieldNames koja definise polja u tabeli na koja se mapiraju podaci.

71

Page 73: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 57: Klasa ProizvodDto

public class ProizvodDto{

public long ProizvodId { get; set; }public string Naziv { get; set; }public string BarKod { get; set; }...

}

ProizvodDto je jednostavni objekat transfera podataka koji sadrzi proste tipove podatakai sluzi za iscitavanje i snimanje podataka u bazi podataka.

Sledi implementacija apstraktnih metoda iz bazne TDG klase.

Listing 58: Priprema upita za selektovanje po id-u

private static readonly string SelectByIdCommandText =string.Format(@"select {0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9} from proizvod where {0}=@{0}",FieldNames.Id, FieldNames.Naziv, FieldNames.NetoKolicina, FieldNames.DaniUpotrebe,FieldNames.ProizvodjacId, FieldNames.BarKod, FieldNames.MaxMarza,FieldNames.JedinicaMereId, FieldNames.PoreskaStopaId, FieldNames.NetoKolicinaJmId);

public override void PrepareSelById(long id, IDbCommand command){

command.CommandText = SelectByIdCommandText;

command.Prepare();

AddParameter(command, FieldNames.Id, id);}

Prva metoda je PrepareSelById. Ona postavlja tekst upita za selektovanje jednog reda poid-u, poziva metodu objekta IDbCommand, kako bi se pripremila operacija za selektovanjepodataka, i na kraju, dodaje se vrednost dinamickog parametra pozivom metode AddPara-meter iz bazne klase. Kako je tekst selektovanja podataka isti za svaku instancu objekta,tekst komande je smesten u staticku konstantu pored same metode.

Listing 59: Priprema upta za insert

// Insertprivate static readonly string InsertCommandText =

string.Format("insert into proizvod ({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9})VALUES (@{0}, @{1}, @{2}, @{3}, @{4}, @{5}, @{6}, @{7}, @{8}, @{9})",FieldNames.Naziv, FieldNames.BarKod, FieldNames.NetoKolicina,FieldNames.MaxMarza, FieldNames.JedinicaMereId, FieldNames.PoreskaStopaId,FieldNames.ProizvodjacId, FieldNames.DaniUpotrebe, FieldNames.Id,FieldNames.NetoKolicinaJmId);

public override void PrepareInsert(ProizvodDto item, IDbCommand command){

command.CommandText = InsertCommandText;

command.Prepare();

72

Page 74: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

AddParameter(command, FieldNames.Naziv, item.Naziv);AddParameter(command, FieldNames.BarKod, item.BarKod);AddParameter(command, FieldNames.NetoKolicina, item.NetoKolicina);...

}

Metoda PrepareInsert po svojoj implementaciji veoma je slicna prethodnoj metodi, s timsto je komplikovanija i razlikuje se u upitu koji izvrsava. Implementacija metoda Prepare-Update i PrepareDelete u mnogome je slicna implementaciji ovog metoda, algoritam je isti,jedino se razlikuje upit koji se izvrsava.

Listing 60: Mapiranje podataka

public override ProizvodDto Retrieve(IDataReader reader){

var p = new ProizvodDto();p.ProizvodId = MappHelper.GetDbInt(reader[FieldNames.Id]);p.Naziv = MappHelper.GetDbString(reader[FieldNames.Naziv]);p.BarKod = MappHelper.GetDbString(reader[FieldNames.BarKod]);...return p;

}

Na kraju, metoda Retrieve je zaduzena za mapiranje podataka u objekat iz baze podataka.Ovde se koriste konstante definisane u statickoj klasi FieldNames se univerzalna statickaklasa za mapiranje tipova podataka MappHelper.

6.4 Fabrika

Kako je ovaj patern veoma poznat i kako se koristi veoma cesto, u daljem tekstu ne opisujese sam patern, vec i nacin na koji je on implementiran u projektu za izgradnju agregata.

6.4.1 Implementacija bazne klasa fabrika

Repozitorijum koristi fabrike. Takode, za fabrike se implementira bazna klasa koja sadrzizajednicku funkcionalnost.

Listing 61: Bazna klasa fabrika

public abstract class BaseFactory<TAgg, TDto> where TAgg : AggregateRoot{

public TAgg BuildAggregate(TDto dto){

return CreateAggregate(dto);}

protected abstract TAgg CreateAggregate(TDto dto);}

73

Page 75: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Apstraktna klasa BaseFactory objedinjuje svu zajednicku funkcionalnost fabrika. Zasada je implementacija klase jednostavna i sadrzi samo metodu za kreiranje agregata iz datogDTO objekta. Ona dalje poziva apstraktnu metodu CreateAggregate, koja sadrzi logiku zakonstruisanje agregata. Klasa ima dva genericka parametra: TAgg predstavlja tip agregatakoji je potrebno konstruisati, dok TDto predstavlja tip DTO objekta u koji se smestajusirovi podaci preuzeti iz izvora podataka.

6.4.2 Implementacija fabrike ProizvodFactory

Kako bi objekat fabrike bio u mogucnosti da rekonstruise agregat, potrebna mu je re-ferenca na DTO objekat glavnog entiteta (koji se mora rekonstruisati). U velikom brojuslucajeva to nije dovoljno i tada klase fabrike u svom konstruktoru zahtevaju instance naostale TDG objekte koji su potrebni za konstrukciju agregata.

Listing 62: Klasa ProizvodFactory

public class ProizvodFactory : BaseFactory<Proizvod, ProizvodGateway.ProizvodDto>{

private readonly ProizvodGrupaGateway _proizvodGrupaGateway;

public ProizvodFactory(ProizvodGrupaGateway proizvodGrupaGateway){

_proizvodGrupaGateway = proizvodGrupaGateway;}

protected override Proizvod CreateAggregate(ProizvodGateway.ProizvodDto proizDto){

Proizvod selProizvod = null;

IList<ProizvodGrupaGateway.ProizvodGrupaDto> grupe =_proizvodGrupaGateway.SelectByProizvodId(proizDto.ProizvodId);

selProizvod = Proizvod.Reconstitute(proizDto.ProizvodId,proizDto.Naziv, proizDto.BarKod, proizDto.NetoKolicina,proizDto.NetoKolicinaJmId, proizDto.DaniUpotrebe,proizDto.MaxMarza, proizDto.JedinicaMereId, proizDto.PoreskaStopaId,proizDto.ProizvodjacId, grupe.Select(x => x.GrupaId));

return selProizvod;}

}

U prethodnom primeru vidimo da klasa ProizvodFactory nasleduje baznu klasu fabrikeBaseFactory upotrebom generickih parametara Proizvod (tip objekta agregata) i Proizvod-Gateway.ProizvodDto (tip DTO objekta). Agregat proizvoda sadrzi i grupe proizvoda. Zbogtoga je potrebno preuzeti i sve grupe proizvoda za dati proizvod. Kako bi se preuzele svegrupe proizvoda kao parametar se zahteva referenca na objekat ProizvodGrupaGateway.

Metoda CreateAggregate implementira funkcionalnost kreiranja agregata. Kako je kaoparametar dobijen DTO objekat proizvoda, potrebno je preuzeti sve grupe za dati proizvod.To se postize pozivanjem metode SelectByProizvodId objekta ProizvodGrupaGateway. Kadase preuzmu sve grupe, poziva se staticki metod agregata, koji je zaduzen za rekonstrukciju

74

Page 76: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

objekta. Na kraju, vraca referenca se na novokreirani agregat.Generalno, implementacija fabrika je jednostavna, ali ipak, korisno je drzati kod za kon-

struisanje agregata odvojenim, kako bi se izbeglo dupliranje koda.

75

Page 77: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

7 Sloj pristupa podacima - strana upita

Za razliku od komandne strane gde je potrebno voditi racuna o transakcijama i izmenje-nim objektima, ovde to nije potrebno. Samim tim, sloj pristupa podacima na strani upitaje jednostavniji. Takode, za razliku od komandne strane, gde je centralni objekat repozitori-jum, ovde je centralni objekat hendler upita. Nije dozvoljena izmena podataka, vec se samonjihovo preuzimanje. Kako je ovaj sloj koncentrisan na preuzimanje i pretragu podatakarazumno je da je logika za pretragu objekata komplikovanija u odnosu na komandnu stranu.Pocinjemo opis definisanjem bazne klase hendlera upita.

7.1 Implementacija bazne klase hendlera upita

Iako postoji veliki broj razlicitih upita koje nije moguce staviti pod isti kalup, ipak,postoji zajednicki algoritam za sve hendlere upita, koji je potrebno izvrsiti kako bi se dobilirezultati upita.

Velika razlika u odnosu na komandnu stranu je ta sto se na strani upita ne koriste objektijedinice rada, s obzirom na to da ne postoji logika za upis na izvor podataka. Objekti jedinicerada su zaduzeni da upravljaju konekcijom na komandnoj strani i potrebno je repliciratislicnu logiku i na strani upita. Ta logika je smestena u baznoj klasi hendlera. Kako je tojedino bitno zaduzenje smesteno u ovoj klasi i kako klasa nije velika, prikazuje se ceo sadrzajklase.

Listing 63: Bazna klasa hendlera upita

public abstract class QueryHandlerBase<TQuery, TResult> : IQueryHandler<TQuery, TResult>where TQuery : IQuery<TResult>

{private readonly IConnectionPool _connectionPool;

protected QueryHandlerBase(IConnectionPool connectionPool){

_connectionPool = connectionPool;}public TResult Handle(TQuery query){

IDbConnection connection = null;TResult result;try{

connection = _connectionPool.GetConnection();_connectionPool.LockConnection(connection);result = HandleQueryIntern(query, connection);

}finally{

if (connection != null) _connectionPool.UnlockConnection(connection);}return result;

}

protected abstract TResult HandleQueryIntern(TQuery query, IDbConnection connection);

76

Page 78: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Iz koda mozemo da vidimo da je bazna klasa hendlera genericka (gde su genericki pa-rametri tip upita koji se implementira i tip rezultata koji upit treba da vrati). Ovim ge-nerickim parametrima implementira se i interfejs IQueryHandler. Metoda Handle generickiimplementira algoritam obrade upita. U okviru metode rezervise se konekcija prema izvorupodataka, poziva se interna apstraktna metoda za obradu upita HandleQueryIntern i nakontoga, oslobada se konekcija koja je upotrebljena za pristup podacima.

7.1.1 Implementacija hendlera GetFakturaByIdQueryHandler

Baznu klasu nasleduju sve ostale klase za obradu upita. Implementacija konkretnih klasahendlera obicno je jednostavna osim u slucajevima gde je potrebno kreirati kompleksnemodele. Svaka klasa koja nasleduje baznu klasu sadrzi konstruktor koji zahteva parametreiste kao i bazna klasa (referenca na objekat tipa IConnectionPool) i dodatne parametrekoji predstavljaju reference na TDG objekte, koji sluze za preuzimanje podataka iz izvorapodataka. Sledi primer iz klase GetFakturaByIdQueryHandler.

Listing 64: Zaglavlje klase hendlera upita GetFakturaByIdQueryHandler

public GetFakturaByIdQueryHandler(IConnectionPool connectionPool,ISelByIdGateway<FakturaDto> fakturaDtoGateway,IMultiSelByIdGateway<FakturaStavka> fakturaStavkaGateway) : base(connectionPool)

{_fakturaDtoGateway = fakturaDtoGateway;_fakturaStavkaGateway = fakturaStavkaGateway;

}

U konstruktoru zahtevaju se reference na TDG objekte koji preuzimaju objekte tipaFakturaDto i FakturaStavka iz izvora podataka. Metoda koristi ove objekte kako bi se preuzelipodaci iz izvora podataka i kreirao rezultujuci objekat. U ovoj metodi vrsi se mapiranjesirovih podataka u formu, koja je pogodna za upotrebu od strane klijenta.

Listing 65: Kreiranje rezultujuceg modela za upit

protected override FakturaEdit HandleQueryIntern(GetFakturaByIdQuery query,IDbConnection connection)

{FakturaDto dto = _fakturaDtoGateway.GetById(connection, query.FakturaId);

FakturaEdit res = new FakturaEdit();

res.Id = dto.FakturaId;res.BrojFakture = dto.BrojFakture;res.DatumFakture = dto.DatumFakture;res.DatumValute = dto.DatumValute;res.Iznos = new LNovac(dto.Iznos, dto.ValutaId, dto.ValutaOznaka);{

Dobavljac d = new Dobavljac();d.Id = dto.DobavljacId;d.Naziv = dto.DobavljacNaziv;res.Dobavljac = d;

}{

77

Page 79: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Korisnik k = new Korisnik();k.Id = dto.KorisnikId;k.Ime = dto.KorisnikIme;k.Prezime = dto.KorisnikPrezime;res.Korisnik = k;

}res.FakturaRabat = dto.FakturaRabat;res.IsKalkulisana = dto.IsKalkulisana;

{res.FakturaStavkaList = _fakturaStavkaGateway.GetById(connection, res.Id);

}

return res;}

U prethodnoj metodi vidimo da se prvo preuzima referenca na privremeni objekat Fak-turaDto koji se preuzima iz izvora podataka preko jedinstvenog identifikatora. Nakon toga,kreira se objekat tipa FakturaEdit, ciji se podaci popunjuju iz privremenog DTO objekta.Stavke fakture preuzimaju se uz pomoc TDG objekta koji vraca sve stavke fakture za datiid fakture. Na kraju metoda vraca se novokreirani objekat FakturaEdit kao rezultat obrade.

7.1.2 Implementacija bazne TDG klase

Implementacija TDG objekata na strani upita slicna je implementaciji na komandnojstrani uz nekoliko razlika. Za razliku od komandne strane gde se jedan TDG objekat obicnomapira na tabelu, ovde se TDG objekti obicno mapiraju na view objekat iz baze podataka.Mapiranjem na view, smanjuje se broj poziva prema bazi podataka, cineci kreiranje objektamodela efikasnijim.

Takode, dok je upisni sloj koncentrisan na upis i azuriranje podataka i gde su zahteviza pretrazivanje podataka obicno jednostavni, strana upita zahteva kompleksniju logiku zaobradu razlicitih tipova upita nad izvorom podataka. TDG objekti ne sadrze metode zaizmenu podataka, ali zato sadrze razlicite metode za kreiranje upita nad izvorom podataka.Najcesci slucajevi kreiranja upita nad izvorom podataka su apstraktovani, a kreirane su igenericke metode za njihovu obradu.

Listing 66: Metode za preuzimanje podataka

public TResult GetById(IDbConnection connection, long id, ISelByIdQueryBuilder queryBuilder){

IDbCommand command = connection.CreateCommand();queryBuilder.PrepareSelById(id, command);return FetchOneObject(command, this);

}

public IList<TResult> GetMulById(IDbConnection connection, long id,IMulSelByIdQueryBuilder queryBuilder)

{IDbCommand command = connection.CreateCommand();queryBuilder.PrepareMulSelById(id, command);return FetchMulObjects(command, this);

}

78

Page 80: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

public IList<TResult> GetAll(IDbConnection connection, ISelAllQueryBuilder queryBuilder){

IDbCommand command = connection.CreateCommand();queryBuilder.PrepareSelAll(command);return FetchMulObjects(command, this);

}

Ove metode nude funkcionalnost, koja je cesto potrebna u klasama. One nasleduju baznuklasu. Zahteva se referenca na objekte koje implementiraju QueryBuilder interfejse. Onidefinisu metodu za kreiranje upita prema izvoru podataka.

7.1.3 Implementacija FakturaDtoGateway TDG klase

Implementacija TDG klase na strani citanja, slicna je implementaciji TDG klase ko-mandne strane. Zbog razlicitih nacina zadavanja upita, TDG objekti strane upita imple-mentiraju razlicite interfejse, kako bi ovi objekti mogli da se koriste u generickim klasama.

Listing 67: TDG klasa FakturaDtoGateway

public class FakturaDtoGateway : BaseTableGateway<FakturaDto>,ISelByIdGateway<FakturaDto>, ISelByIdQueryBuilder

{public override string TableName{...}

public FakturaDto GetById(IDbConnection connection, long id){

return base.GetById(connection, id, this);}

public void PrepareSelById(long id, IDbCommand command) {...}

public override FakturaDto Retrieve(IDataReader reader) {...}

public static class FieldNames {...}}

Kako vidimo iz prilozenog koda, svaka TDG klasa nasleduje baznu klasu BaseTableGa-teway i dodatne interfejse. U ovom slucaju, nasleduje se interfejs ISelByIdGateway, kojinaznacava da ova klasa omogucava preuzimanje jednog objekta FakturaDto iz izvora po-dataka. Takode, implementiran je interfejs ISelByIdQueryBuilder koji omogucava laksuimplementaciju metode GetById (ovaj interfejs zahteva implementaciju metode Retrieve).

79

Page 81: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8 Prezentacioni sloj – Model-View-ViewModel (MVVM)

patern

Kao sto smo na pocetku rada napomenuli, prezentacioni sloj je implementiran upotre-bom MVVM paterna. U daljem tekstu opisuje se sam patern, a zatim i nacin na koji jeimplementiran u projektu.

8.1 Uvod

MVVM patern: pomaze cistom razdvajanju biznis logike od prezentacione logike kori-snickog interfejsa, olaksava dizajniranje aplikacije, cini aplikaciju jednostavnijom za testi-ranje, odrzavanje i razvoj, promovise ponovnu upotrebu koda i omogucava programerima idizajnerima laksu saradnju tokom razvoja aplikacije.

Korisnicki interfejs aplikacije i lezuca prezentaciona i biznis logika razdvojeni su u trirazlicita sloja: view sadrzi korisnicki interfejs i logiku vezanu za nacin, na koji se podacipredstavljaju korisniku, view model sadrzi prezentacionu logiku i trenutno stanje korisnickoginterfejsa, i model sadrzi aplikacionu biznis logiku i podatke.

8.2 Istorija

2005-te godine, John Gossman, jedan od arhitekata WPF i Silverlight platformi u Maj-krosoftu predstavio je MVVM patern na svom blogu. MVVM je slican Presentation Model(PM) paternu u tome sto oba paterna karakterise apstrakcija prezentacije i ponasanja. Mar-tin Fowler je uveo PM kao nacin za kreiranje apstrakcije prezentacije koja je nezavisnaod platforme, dok je Gossman uveo MVVM kao standardizovani nacin upotrebe karakteri-stika WPF patforme, koje pojednostavljuju kreiranje korisnickog interfejsa. U tom smislu,MVVM smatra se specijalizacijom opstijeg PM paterna dizajniranog po meri WPF i Sil-verlight platforme. Ovaj patern se sada koristi i u drugim tehnologijama. U novije vremeovaj patern opisuje se kao MVB (Model-View-Binder) patern kada se implementira izvanMicrosoft zajednice.

8.3 Odgovornosti klasa i njihove karakteristike

MVVM patern je varijanta PM paterna, optimizovan da iskoristi mogucnosti WPF i Sil-verlight platformi. To su: mehanizmi vezivanja podataka Data binding, definisanje grafickihsablona za podatke (Templates), komande (Commands) i ponasanja koja mogu da se pri-dodaju na vizuelnu komponentu (Behaviours). View objekti komuniciraju sa view modelobjektima kroz mehanizme vezivanja podataka, upotrebom komandi i kroz obavestenja opromeni podataka (Change notification events). View model objekti zaduzeni su za preuzi-manje podataka, nadgledanje i koordinisanje izmena podataka nad objektima modela. Viewmodel objekti su, takode, zaduzeni za konvertovanje i validaciju podataka sa korisnickoginterfejsa.

80

Page 82: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 15 prikazuje MVVM klase i njihovu medusobnu saradnju:

Dijagram 15: Saradnja klasa u okviru MVVM paterna

Kljuc za efektivnu upotrebu MVVM paterna lezi u razumevanju nacina na koji ove klasemedusobno saraduju u razlicitim scenarijima i rasporedivanju koda u odgovarajucim kla-sama. U sledecem tekstu opisane su odgovornosti i karakteristike klasa MVVM paterna.

8.3.1 View

Odgovornost view objekata je definisanje strukture i izgleda svega sto korisnik vidina ekranu. Idealno, kod view objekta trebalo bi da sadrzi samo konstruktor. U nekimslucajevima, moze da sadrzi kod koji sadrzi logiku vezanu za nacin na koji se prikazuju po-daci i vizuelno ponasanje koje je tesko ili neefikasno izraziti na XAML jeziku (kompleksneanimacije ili direktan rad sa vizuelnim elementima koji su deo prikaza). Nije preporucljivostavljati bilo kakav kod koji sadrzi logiku (za koju je potrebno odraditi testiranje).

Podaci view objekata, vezuju se na podatke view model objekata na osnovu propertijakonteksta podataka (DataContext) koji je definisan za svaku view klasu. Za kontekst po-dataka view objekta postavlja se odgovarajuci view model objekat. View model objektipruzaju propertije i komande koji se povezuju sa komponentama view objekata. Takode,view model objekti obavestavaju view objekte o promenama stanja kroz dogadaje promenepodataka (Data change events). Tipicno, veza izmedu view i view modela objekata je jedanprema jedan, tj. jedan view objekat drzi referencu na samo jedan view model objekat.

Obicno, view klase nasleduju neku WPF kontrolu, ali postoje i slucajevi gde se korisnickiinterfejs generise preko kontrola, koje predstavljaju sablone podataka. Sablone podatakamozemo posmatrati kao prezentacije bez prezentacione logike. Oni su dizajnirani da sepovezu direktno na specifican view model. Sabloni podataka direktno se povezuju sa odgo-varajucim tipom podataka view model klase. WPF automatski kreira view objekte za sveobjekte zadatog tipa view model klasa kadgod se view model objekat prikaze na korisnickiinterfejs. Sablon podataka moze biti definisan direktno u kodu kontrole koja ga koristi,takode, moze biti definisan i u recniku resursa (resurce dictionary).

81

Page 83: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.3.2 View model

View model objekti sadrze prezentacionu logiku i podatke koje je potrebno prikazati.Ne sadrze direktnu referencu prema view objektima i nemaju znanje o tome kako je imple-mentiran korisnicki interfejs. View model objekti implementiraju propertije i komande nakoje se komponente view objekata vezuju i obavestavaju view objekte o promenama stanjagenerisanjem dogadaja. Definisanjem propertija i komandi precizira se funkcionalnost, alinacin prikaza funkcionalnosti odreduje se u view objektima.

Odgovornost view model objekata je koordinisanje izmedu view i model objekata. Tipicno,postoji veza jedan prema vise izmedu objekata view model i model klasa. View model objektimogu direktno da izloze objekte modela na prikaz. U tom slucaju klase modela moraju bitidizajnirane tako da podrze vezivanje podataka.

View model objekti konvertuju i manipulisu podacima modela tako da oni mogu bitilako upotrebljeni u view objektima. Ovi objekti, takode, mogu definisati dodatne propertijekako bi se dodatno olaksala implementacija view klasa. Ovi propertiji ne moraju biti deomodela. Na primer, moguce je kombinovati vrednosti dva polja kako bi se olaksao prikaz nakorisnickom interfejsu, ili se moze racunati broj preostalih karaktera za unos u polju. Ovdese, takode, implementira logika validacije podataka.

View model objekti, takode, mogu definisati logicka stanja prikaza kako bi se omogucilavizuelna promena korisnickog interfejsa. View objekti definisu raspored i stil komponentikoje odrazavaju trenutno stanje view model objekta. Na primer, view model objekat mozedefinisati status koji indicira da su podaci prosledeni na obradu i u tom slucaju view objekatmoze prikazati animaciju kako bi dao vizuelnu indikaciju korisniku.

Tipicno, view model objekti definisu komande ili akcije koje se prikazuju korisniku.Uobicajen primer je komanda za snimanje podataka koja omogucava korisniku da prosledipodatke izvoru podataka na snimanje. View objekat moze da odluci da komandu prikazepreko dugmeta, ili na neki drugi nacin. Komande pruzaju nacin za obradu korisnickih akcijai razdvajanje vizuelne prezentacije od prezentacione logike.

8.3.3 Model

Model sadrzi biznis logiku i podatke. Biznis logika je definisana kao aplikaciona logikakoja je zaduzena za preuzimanje i upravljanje aplikacionim podacima kako bi se osiguralabiznis pravila koja obezbedujuju konzistentnost i validnost podataka.

Tipicno, model predstavlja klijentski biznis model aplikacije. Sadrzi podatke, biznis ivalidacionu logiku. Model moze, takode, ukljuciti i kod za pristup podacima i kesiranje.Cesto se model i servisni sloj definisu kao strategija pristupa podacima.

Model, takode, moze podrzavati validaciju podataka i izvestavanje o greskama implemen-tiranjem odgovarajucih interfejsa. Ukoliko klase modela ne implementiraju odgovarajuceinterfejse, onda se klase modela obmotavaju view model klasom, koja onda pruza svu neop-hodnu funkcionalnost view klasi.

82

Page 84: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.4 Saradnja izmedu klasa

MVVM patern pruza cisto razdvajanje izmedu korisnickog interfejsa aplikacije, prezen-tacione logike, biznis logike i podataka razdvajanjem svakog elementa u posebnu klasu.

Saradnja izmedu view i view model klasa je najbitnija za razmatranje, ali i saradnjaizmedu model i view model klasa je, takode, vazna. Dalje opisujemo razlicite sablone ovihinterakcija i opisujemo kako implementirati MVVM patern u aplikaciji.

8.4.1 Povezivanje podataka (eng. Data binding)

Mogucnost vezivanja podataka igra veoma vaznu ulogu u MVVM paternu. WPF pruzabogate mogucnosti vezivanja podataka. View model klase treba da budu dizajnirane takoda mogu da podrze povezivanje podataka kako bi se iskoristile prednosti MVVM paterna.Obicno, ovo znaci da ove klase moraju da implementiraju odgovarajuce interfejse.

WPF podrzava nekoliko modela vezivanja podataka. Sa jednosmernim povezivanjem,korisnicke kontrole mogu biti vezane za view model objekat tako da samo prikazuju vrednostipodataka. Ukoliko se vrednost u view model objektu promeni, ta promena se reflektujena podatke koji se prikazuju na komponentu. Dvosmerno povezivanje, isto, automatskiazurira komponentu na ekranu prilikom promene podatka u view model objektu, ali i azuriravrednosti podataka u view model objektu kada se njihova vrednost promeni na komponenti.

Kako bi osigurali da je korisnicki interfejs uvek azuran (kada se podatak promeni uview model objektu), potrebno je implementirati interfejs koji ima funkciju obavestavanja opromeni podataka. Ako view model klasa definise propertije, koji se vezuju na view kompo-nente, onda view model klasa treba da implementira INotifyPropertyChanged interfejs. Akoview model klasa predstavlja kolekciju, onda treba da implementira INotifyCollectionChan-ged interfejs ili da nasledi klasu ObservableCollection koja implementira ovaj interfejs. Obainterfejsa definisu dogadaj koji je potrebno izazvati prilikom promene podataka. Podaci kojise nalaze na kontrolama automatski se azuriraju kada se dogadaj promene podataka izazove.

U velikom broju slucajeva, view model klase definisu propertije koji predstavljaju objekte.WPF podrzava povezivanje podataka za ugnjezdene propertije preko atributa Path. Zato jecest slucaj da view model objekat vrati referencu na drugi view model ili model objekat. Sveview model i model klase dostupne view klasama treba da implementiraju gore navedeneinterfejse (u zavisnosti od situacije) kako bi povezivanje podataka uspesno radilo. U daljemtekstu opisujemo kako implementirati INotifyPropertyChanged interfejs, dok je izostavljenopis implementacije intefejsa INotifyCollectionChanged kako nigde nije implementiran u pro-jektu, jer klasa ObservableCollection nudi svu potrebnu funkcionalnost.

8.4.2 Implementiranje interfejsa INotifyPropertyChanged

Implementiranjem ovog interfejsa u view model ili model klasi omogucuje se izazivanjedogadaja promene nad podacima koje kontrole osluskuju i azuriraju svoje vrednosti (ukolikoje dogadaj izazvan). Implementacija ovog interfejsa je jednostavna i prikazana je u sledecemprimeru:

83

Page 85: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 68: Sadrzaj klase BasePropertyChanged

public abstract class BasePropertyChanged : INotifyPropertyChanged{

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged(string propertyName){

VerifyPropertyName(propertyName);var handler = PropertyChanged;if (handler != null){

var e = new PropertyChangedEventArgs(propertyName);handler(this, e);

}}

[Conditional("Debug")]public void VerifyPropertyName(string propertyName){

if (TypeDescriptor.GetProperties(this)[propertyName] == null){

string msg = "Invalid property name: " + propertyName;Debug.Fail(msg);

}}

protected virtual bool SetProperty<T>(ref T member, T val, string propertyName){

if (Equals(member, val)) return false;member = val;OnPropertyChanged(propertyName);return true;

}}

Interfejs definise properti PropertyChanged tipa event (dogadaj) koji je potrebno imple-mentirati. Dogadaj se izaziva u metodu OnPropertyChanged gde prva linija proverava dali dati properti stvarno postoji u klasi i izvrsava se samo u debug modu. Ukoliko postojepretplaceni delegati na dogadaj, on se izaziva.

Implementiranje ovog interfejsa u svakoj view model klasi posao je koji se ponavlja i mozebiti sklon greskama. Zbog toga dobro je definisati baznu klasu koju nasleduju sve ostale viewmodel klase i koja implementira ovaj interfejs, kao sto je i prikazano u prethodnom primeru.

Klasa koja nasleduje baznu klasu moze podici dogadaj promene vrednosti propertija useteru pozivanjem metoda SetProperty. Ovaj metod proverava da li se zapravo postavljanova vrednost trenutnom polju. Ukoliko to jeste slucaj, vrednost polja se azurira i izazivase dogadaj PropertyChanged.

8.4.3 Komande

Pored toga sto view model klase pruzaju pristup podacima view klasama za prikaz iliizmenu, view model klase najverovatnije moraju imati definisanu jednu ili vise akcija ilioperacija koje mogu biti inicirane od strane korisnika. U WPF okruzenju akcije ili operacije,koje korisnik moze da izvrsi kroz korisnicki interfejs, tipicno su definisane kao komande.

84

Page 86: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Komande pruzaju zgodan nacin predstavljanja akcija ili operacija koje mogu biti lako vezaneza kontrole na prezentaciji. One sadrze stvarni kod koji implementira akciju ili operaciju ipomaze odrzavanju razdvajanja implementacije od vizuelne prezentacije.

Komande mogu biti vizuelno predstavljene i pozvane na razlicite nacine od strane ko-risnika. U najvecem broju slucajeva, one se pozivaju kao rezultat klika misem, ali one ,takode, mogu biti pozvane i kao rezultat neke precice na tastaturi ili necega drugog (npr:zavrsetak animacije). Kontrole na korisnickom interfejsu povezane su sa komandama u viewmodel objektima, tako da korisnik moze da ih pozove izazivanjem bilo kog ulaznog dogadajakoji kontrola definise. Interakcija izmedu prezentacionih kontrola i komande moze biti dvo-smerna. Komanda moze biti pozvana iz korisnickog interfejsa, a korisnicki interfejs mozebiti automatski azuriran kao posledica izvrsavanja komande.

View model klase mogu implementirati komande u obliku komandnog metoda ili komand-nog objekata (objekat koji implementira ICommand interfejs). U svakom slucaju, saradnjakontrole sa komandom moze biti definisana deklerativno bez kompleksnog koda za obradudogadaja u view klasama. Na primer, odredene kontrole same po sebi podrzavaju komandei pruzaju Command properti koji moze biti povezan sa ICommand objektom u view modelklasi.

Komandni objekat je objekat koji implementira interfejs ICommand. Ovaj interfejs de-finise Execute metod, u kome se izvrsava sama operacija. Takode, sadrzi i metodu CanExe-cute koja indikuje da li je moguce izvrsiti komandu. Obe metode imaju jedan argument kaoparametar. Enkapsulacija implementacione logike za operaciju u komandnom objektu znacida kod moze lakse da se odrzava i testira.

Implementirati ICommand interfejs je lako, medutim, vec postoje implementacije ovoginterfejsa koje je moguce koristiti u aplikaicji. Na primer, moguce je koristiti DelegateCom-mand klasu iz Prism biblioteke.

8.4.3.1 Klasa DelegateCommand

DelegateCommand klasa sadrzi dva delegata koji referenciraju metode implementiraneu view model klasi. Nasleduje DelegateCommandBase klasu, koja implementira ICommandinterfejs pozivanjem delegata Execute i CanExecute. Delegati se zadaju u konstruktoruprilikom kreiranja instance komandnog objekta, kao u sledecem primeru:

Listing 69: Kreiranje instance komande tipa DelegateCommand

var editStavkaCommand = new DelegateCommand(OnEditStavkaCommand, CanEditStavka);

Kada se pozove metod Execute u objektu DelegateCommand, objekat jednostavno prosledujepoziv ka metodi u view model klasi preko delegata koji je zadat u konstruktoru. Slicno, kadase CanExecute metod pozove, poziv se prosleduje odgovarajucoj metodi u view model klasi.Delegat CanExecute je opcionalan parametar u konstruktoru i ukoliko nije zadat onda jekomanda uvek dostupna za izvrsavanje.

DelegateCommand klasa je genericka klasa, argument tipa odreduje tip parametra ko-

85

Page 87: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

mande u Execute i CanExecute metodama. Postoji i negenericka verzija DelegateCommandklase u Prism biblioteci, koja moze se koristiti kada nije potreban parametar, kao sto je toprikazano u prethodnom primeru.

View model klasa moze naznaciti promenu statusa dostupnosti komande pozivom metodeRaiseCanExecuteChanged nad objektom DelegateCommand. Ovo podize dogadaj CanExe-cuteChanged i sve kontrole na prikazu, koje su povezane sa ovom komandom, automatskiazuriraju svoje stanje kako bi odrazile dostupnost povezane komande.

8.4.3.2 Povezivanje komandnih objekata sa vizuelnim komponentama

Postoji vise nacina na koje view kontrola moze da se poveze sa komandnim objektom.Odredene kontrole, posebno kontrole koje nasleduju klasu ButtonBase (Button, RadioBut-ton, Hyperlink), ili klase koje nasleduju klasu MenyItem, mogu jednostavno da se vezu zakomandni objekat kroz Command properti.

Listing 70: Povezivanje view kontrole na komandu

<Button Command="{Binding EditCommand}" Content="Izmeni Stavku" ... />

Komandni parametar je opcioni i moze se proslediti upotrebom CommandParameterpropertija. Kontrola automatski poziva ciljanu komandu u trenutku kada korisnik izvrsiakciju nad njom. Komandni parametar, ukoliko je zadat, prosleduje se metodi Execute.

8.5 Validacija podataka i izvestavanje o greskama

View model klase i klase biznis modela, cesto, zahtevaju validaciju podataka i signali-ziranje krajnjem korisniku o validacionim greskama, kako bi korisnik mogao da ih ispravi.WPF pruza podrsku za upravljanje validacionim greskama koje se desavaju kada se menjajuvrednosti projertija koji su vezani na view kontrole. Za propertije koji su vezani za kon-trolu, view model ili model klasa moze signalazirati validacionu gresku preko metode seteravrednosti propertija odbijanjem da postavi losu vrednost i izazivanjem izuzetka. Ukoliko jeValidatesOnExceptions properti za povezivanje podataka postavljen na True, WPF obradujeizuzetak i prikazuje vizuelni indikator korisniku da postoji greska u podacima.

Medutim, bacanje izuzetaka u seter propertija treba izbegavati gde god je to moguce.Alternativni pristup je da view ili view model klase implementiraju interfejse IDataErrorInfoili INotifyDataErrorInfo. Ovi interfejsi dopustaju view model klasama da izvrse validacijupodataka za jednu ili vise vrednosti propertija i vrate poruku greske view klasi tako dakorisnik moze biti obavesten o gresci.

8.5.1 Implementiranje interfejsa IDataErrorInfo

IDataErrorInfo interfejs pruza osnovnu podrsku za validaciju podataka i izvestavanje ogreskama. Definise dva propertija cija vrednost moze samo da se procita. Jedan je indeksni

86

Page 88: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

properti, koji ima naziv propertija koji se validira kao argument, a drugi properti je Errorkoji dozvoljava view model objektu da pruzi poruku greske za ceo objekat.

Indeksni properti dozvoljava view model/model klasama da vrate poruku o gresci zaodredeni properti. Prazan string ili null vrednost oznacava da je properti validan.

Indeksnom propertiju pristupa se kada se povezani properti prvi put prikaze, i kadgodse nakon toga promeni. Zato sto se indeksni properti poziva za sve vezane propertije kojise menjaju, treba biti pazljiv prilikom implementiranja validacije kako bi se osiguralo davalidacija bude brza i efikasna.

Kada povezujemo kontrole u prikazu na propertije koje je potrebno validirati kroz ovajinterfejs potrebno je postaviti vrednost propertija ValidatesOnDataErrors na True prilikompovezivanja kontrole sa podacima. Ovo osigurava da WPF zahteva informaciju o gresci zavezani properti, kao u sledecem primeru:

Listing 71: Povezivanje view kontrole sa view model propertijem

<DatePicker Name="DPic2"SelectedDate="{Binding Path=VmFaktura.DatumValute,

Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,ValidatesOnDataErrors=True}"

Style="{StaticResource DateStyle}" />

U projektu, ovaj interfejs je implementiran u apstraktnoj klasi BaseEditEnityViewModel,kako je potrebno odraditi jos nekoliko akcija prilikom validacije.

Listing 72: Apstraktna implementacija metode za validaciju u klasi BaseEditEntityVie-wModel

public string this[string columnName]{

get{

var res = ValidateField(columnName);var eventArgs = new ValidationArgs(columnName, res);OnValidatedObject(eventArgs);return res;

}}

Kao sto vidimo iz implementacije, indekser metoda kao ulazni parametar prima nazivkolone. Kako je BaseEditEnityViewModel apstraktna klasa, ona nije svesna svih polja kojeje potrebno validirati, zato poziva se apstraktna metoda ValidateField koju implementirajusve klase koje nasleduju ovu baznu klasu. Kada se validacija polja u metodi ValidateFieldzavrsi, poziva se metoda OnValidatedObject koja izaziva dogadaj ValidatedObject, kako bi seobavestili svi zainteresovani objekti o promeni statusa validnosti polja.

87

Page 89: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 73: Validacija polja u klasi VMFaktura

protected override string ValidateField(string fieldName){

if (string.Equals(fieldName, "BrojFakture") && !string.IsNullOrWhiteSpace(BrojFakture)){

return BrojFakture.GetValidationError(BrojFakture);}if (string.Equals(fieldName, "FakturaRabat") && !string.IsNullOrWhiteSpace(FakturaRabat)){

if (StrUtil.ToDouble(FakturaRabat) == null) return "Neispravan broj unet za rabat";}return null;

}

Sada je prikazan primer validacije polja iz klase VMFaktura. Potrebno je proveriti nazivpolja koje je poslato na validaciju (ovde treba biti pazljiv kako se ne bi pogresio naziv polja).Ukoliko se naziv poklapa sa trenutnim poljem, onda se vrsi validacija. Gresku je mogucedirektno vratiti iz koda ili pozvati metodu neke druge klase koja vraca opis greske. Kao stoje ranije napomenuto prazan string ili null vrednost oznacavaju da greska nije pronadena.

8.6 Konstrukcija i povezivanje objekata

MVVM patern pomaze kod cistog razdvajanja prezentacije od prezentacione i biznislogike. Smestanje koda u prave klase je prvi vazan korak kod efektivne upotrebe MVVMpaterna. Sledeci korak je razmotriti kako se view, view model i model klase implementirajui medusobno povezuju u vremenu izvrsenja.

Tipicno, veza izmedu view i view model klasa je jedan prema jedan. View i view modelklase povezuju se preko propertija DataContext koji se nalazi u view klasi; ovo omogucavavizuelnim elementima da se vezu na propertije, komande i metode view model objekta.Potrebno je odluciti kako se instanciraju view i view model objekti i kako se medusobnopovezuju preko DataContext propertija u toku izvrsenja.

Takode, potrebno je obratiti paznju da prilikom konstruisanja i povezivanja view i viewmodel objekti ostanu medusobno slabo zavisni. Kao sto je ranije napomenuto, view modelklase ne treba da zavise od view klasa. Slicno tome, view klase ne moraju da zavise odkonkretnih implementacija view model klasa.

Postoji vise nacina na koji view i view model objekti mogu biti konstruisani i povezani uvremenu izvrsenja aplikacije. Najpogodniji pristup za aplikaciju najvise zavisi od toga da lise prvo kreira view objekat ili view model objekat i da li se kreiranje objekata vrsi programskiili deklerativno.

8.6.1 Povezivanje objekata deklarativno

Mozda je najjednostavniji pristup da view deklerativno instancira svoj odgovarajuci viewmodel objekat kroz XAML kod. Kada se prikaz konstruise, instancira se odgovarajuci view

88

Page 90: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

model objekat. Ovaj pristup zahteva da view model klasa sadrzi prazan konstruktor bezparametara.

Deklarativna konstrukcija ima prednost da je jednostavna i da radi dobro u alatima zadizajniranje korisnickog interfejsa. Mana ovog pristupa je da view klase imaju znanje o kon-kretnom tipu view model klase i view model klasa mora da sadrzi prazan konstruktor jer nijemoguce proslediti parametre view model objektima iz XAML koda. Ovaj nacin instanciranjaobjekata upotrebljen je u projektu samo prilikom dizajniranja korisnickog interfejsa.

Listing 74: Postavljanje vrednosti DataContext propertija

<UserControl...d:DataContext="{d:DesignInstance fakturaEdit:VMFakturaNavEdt}"...>

8.6.2 Upotreba Sablona

View se moze definisati kao sablon podataka kome je dodeljen tip view model klase.Sabloni podataka mogu biti definisani kao resursi ili mogu biti definisani unutar kontrolekoja prikazuje view model objekat. WPF automatski instancira sablon podataka i podesavanjegov kontekst podataka na view model instancu. Ova tehnika je primer situacije gde seview model prvo instancira.

Sabloni podataka su fleksibilni i ne tako zahtevni za resurse. Dizajner moze da ih koristiza lako vizuelno predstavljanje view model objekata, bez implementacije kompleksnog koda.

Listing 75: Implicitni sablon podataka za prikaz informacija o korisniku

<ComboBox.ItemTemplate><DataTemplate>

<StackPanel Orientation="Vertical"><StackPanel Orientation="Horizontal">

<TextBlock FontStretch="Expanded"FontWeight="Bold"Text="Ime: " />

<TextBlock Text="{Binding Path=Ime}" /></StackPanel><StackPanel Orientation="Horizontal">

<TextBlock FontStretch="Expanded"FontWeight="Bold"Text="Prezime:" />

<TextBlock Text="{Binding Path=Prezime}" /></StackPanel>

</StackPanel></DataTemplate>

</ComboBox.ItemTemplate>

U prethodnom primeru vidimo implicitno definisan sablon podataka za kontrolu Com-boBox. Ovde je definisan sablon za objekat Korisnik gde se svaki element u listi prikazujetako sto se prvo prikazuje ime korisnika, a ispod imena se prikazuje prezime (sa labelama).

89

Page 91: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.6.3 Povezivanje objekata programski

Drugi pristup je programski. Postoji vise nacina na koji view model objekat moze da sepoveze sa view objektom.

• View model klasa moze da ima kao ulazni parametar referencu na view objekat. Kakonije dozvoljeno da view model objekat ima znanje o konkretnoj implementaciji viewobjekta, prosleduje se interfejs koji view objekat implementira. View model objekat usvom konstruktoru menja vrednost Data context propertija view objekta tako tako stomu postavi vrednost da pokazuje na sebe.

• View klasa zahteva ili instancira view model objekat u svom konstruktoru i postavljavrednost DataContext propertija na taj objekat. Ovaj metod koristi se u projektu ibice opisan detaljnije.

• Automatsko povezivanje upotrebom neke bibloteke. Postoje biblioteke koje automat-ski instanciraju view objekte upotrebom razlicitih pravila/konfiguracije. Na primer,postoje biblioteke koje instanciraju objekte bazirano na konvenciji imenovanja. Nazivisvih view model objekata zavrsavaju se sa “ViewModel”, dok se nazivi svih view obje-kata zavrsavaju sa “View”. Biblioteka automatski instancira view objekat i postavljavrednost DataContext properija view objekta da pokazuje na novokreirani view modelobjekat.

8.6.3.1 Povezivanje objekata programski u konstruktoru view klase

Programska konstrukcija i dodeljivanje view model objekta u kodu view klase ima pred-nost u jednostavnosti, a i dobro radi u alatima za dizajniranje korisnickog interfejsa. Nedo-statak ovog pristupa je da view klasa mora da ima znanje o tipu view model klase i to stozahteva pisanje programskog koda u view klasi. Sledi primer iz projekta:

Listing 76: Sadrzaj klase WFakturaEdt

public partial class WFakturaEdt : UserControl{

public WFakturaEdt(VMFakturaNavEdt viewModel){

InitializeComponent();DataContext = viewModel;

}}

Prethodni primer ilustruje implementaciju povezivanja view objekta WFakturaEdt i viewmodel objekta VMFakturaNavEdt. Kao sto se vidi u primeru, u konstruktoru klase WFaktu-raEdt zahteva se referenca na objekat VMFakturaNavEdt, inicijalizuje svoje komponente i,zatim, postavlja vrednost svog DataContext propertija na vrednost reference objekta VM-FakturaNavEdt. Interesantno je, takode, da je ovo jedini programski kod u klasi WFakturaEdtdok je ostatak klase ispisan u XAML kodu.

90

Page 92: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.7 Implementacija MVVM klasa

View model objekti puni su prezentacione logike, isto tako, view objekti sadrze dostakoda, koji se bavi interakcijom sa korisnikom, dok objekti modela uglavnom sadrze podatkei pruzaju podrsku view objektima za laksi rad sa podacima.

View objekti su obicni objekti koji nasleduju neku WPF kontrolu i implementiraju ko-risnicke kontrole pretezno u XAML kodu. Kako implementacija interakcije sa korisnikomnije primarni fokus ovog rada i kako view klase ne sadrze nikakvu specijalnu logiku koja jeznacajna za rad (jer sadrze uglavnom tehnicki kod), opis implementacije ovih klasa cemoizostaviti. Takode, veliki broj koncepata koji se implementiraju u view model klasama vec jeopisan tako da se u ovom poglavlju izostavljaju detalji klasa koji su opisani u okviru drugihpoglavlja ili je njihova implementacija veoma jednostavna. Dijagram 16 prikazuje dijagrambaznih model i view model klasa, koje se nalaze u prezentacionom sloju.

Dijagram 16: Dijagram klasa baznih MVVM klasa

Na osnovu dijagrama mozemo da vidimo strukturu nasledivanja model i view modelklasa. Struktura za model klase mnogo je jednostavnija, sto zakljucujemo na osnovu toga stosadrzi samo jednu klasu (BaseEditEntityModel) koja nasleduje krovnu baznu klasu promenepropertija. View model stuktura sadrzi nekoliko nivoa gde svaki nivo gde svaki nivo “ispod”dodaje svoju logiku. U daljem tekstu opisije se svaka bazna klasa, pojedinacno, kao i primerimplementacije baznih klasa (kroz opis konkretne klase koja nasleduje neku od ovih baznihklasa).

8.7.1 Klasa BasePropertyChanged

Ovo je veoma jednostavna klasa koja sadrzi samo implementaciju interfejsa INotifyPro-pertyChanged. Kako se notifikacije promena vrednosti koriste i u view model i u modelklasama, izdvojena je posebna klasa koja implementira logiku interfejsa. Sadrzaj klase jevec prikazan i opisan u okviru odeljka koji se bavi implementiranjem interfejsa INotifyPro-pertyChanged.

91

Page 93: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

8.7.2 Klasa BaseViewModel

Sadrzaj ove klase prazan je, jer trenutno ne dodaje nikakvu dodatnu funkcionalnost uodnosu na klasu BasePropertyChanged koju nasleduje, ali sa druge strane, dosta view modelklasa nasleduje ovu klasu. To su klase za koje bazne klase “ispod” ne zadovoljavaju njihovepotrebe, ali bazna klasa za te objekte nije kreirana zbog previse specificne logike, koja seupotrebljava samo u tim klasama.

8.7.3 Klasa BaseNavigableViewModel

Karakteristike ove klase su sledece: sadrzi baznu logiku (koja je potrebna da bi viewmodel mogao da ucestvuje u navigaciji), kao i reference na menadzer regiona, implemen-tira interfejs IConfirmNavigationRequest i pruza osnovnu implementaciju metoda interfejsa,sadrzi “Nazad” komandu za navigaciju nazad, (string koji predstavlja naslov view objekta)i cuva parametre koji su mu prosledeni tokom navigacije u propertiju CallerParameters.

Listing 77: Sadrzaj klase BaseNavigableViewModel

public abstract class BaseNavigableViewModel : BaseViewModel, IConfirmNavigationRequest{

private IRegionNavigationService _navigationService;

protected BaseNavigableViewModel(string title, IRegionManager regionManager,IServInjectCommand commandInjector) {...}

public DelegateCommand Back {...}public UriQuery CallerParameters {...}public IRegionManager Manager {...}public string Title {...}public IServInjectCommand CommandInjector {...}

public virtual void OnNavigatedTo(NavigationContext navigationContext) {...}public virtual bool IsNavigationTarget(NavigationContext navigationContext) {...}public virtual void OnNavigatedFrom(NavigationContext navigationContext) {...}public virtual void ConfirmNavigationRequest(NavigationContext navigationContext,

Action<bool> continuationCallback) {...}

protected virtual bool CanGoBack(){

if (_navigationService != null)return _navigationService.Journal.CanGoBack;

return false;}

private void GoBack(){

if (_navigationService.Journal.CanGoBack)_navigationService.Journal.GoBack();

}}

Prethodni listing prikazuje sadrzaj klase bez zadiranja u detalje svake pojedinacne metode(navigacione metode kasnije se opisuju u okviru posebnog poglavlja koje se bavi navigaci-jom). Detaljno su jedino prikazane metode koje implementiraju komandu Back, tako da iz

92

Page 94: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

njih mozemo da vidimo nacin implementiranja navigacije u nazad kroz istoriju. Implemen-tacija ovih metoda postignuta je upotrebom navigacionog servisa koji je opisan u posebnompoglavlju. Za sada, dovljno je reci da se ovaj servis koristi za navigaciju u aplikaciji.

8.7.4 Klasa BaseNavSearchViewModel

Kako je potrebno izlistati i pretraziti elemente, pre nego sto se odabere konkretan objekatsa kojim je potrebno raditi, i kako je ovo uobicajen slucaj u aplikaciji, implementirana jebazna klasa koja podrzava sve ove funkcionalnosti.

Klasa sadrzi implementaciju najcescih komandi, koje su potrebne u ovom scenariju, kaosto je to komanda za brisanje, izmenu, kopiranje u klipboard i filtriranje objekata. Sve ovekomande implementirane su uz upotrebu virtuelnih metoda, tako da je moguce prepravitikod svake komande, u svakoj klasi naslednici.

Za implementaciju nekih od ovih komandi potreban je interakcioni servis. Tako kon-struktor ove klase sadrzi ulazni parametar, koji predstavlja referencu na ovaj servis.

Klasa sadrzi sve rezultate upita, (npr. ukupan broj rezultata dobijen iz upita, referencuna trenutno selektovane elemente itd.) i sve te objekte smesta u objekat tipa Presentati-onCollection<T>. Kako bi mogli da se pribave rezultati upita, ova klasa sadrzi referencuna klasu CommandQueryService. Ona, pak, sadrzi metode za izvrsavanje komandi i upitaiz aplikacionog sloja. Naravno, kako sadrzi sve ove reference, klasa implementira algoritamucitavanja podataka u toku navigacije.

Listing 78: Sadrzaj klase BaseNavSearchViewModel

public abstract class BaseNavSearchViewModel<TEntity, TFilterQuery> : BaseNavigableViewModelwhere TFilterQuery : BaseFilterQuery<TEntity>where TEntity : IdentityCarier

{protected BaseNavSearchViewModel(IRegionManager regionManager,

IServInteraction interactServ, string formNavName,string title, TFilterQuery filter, IServInjectCommand commandInjector,CommandQueryService service): base(title, regionManager, commandInjector) {...}

public ICommand EditCommand {...}public ICommand DeleteCommand {...}public ICommand AddCommand {...}public ICommand CurrSellAsCSVToClipBoard {...}public abstract string EditFormPath { get; }public PresentationCollection<TEntity> ItemsCollection {...}public CommandQueryService Service {...}public ICommand FilterCommand {...}public uint TotalCount {...}public uint FilteredCount {...}public TFilterQuery Filter {...}public IServInteraction InteractService {...}

private async void RefreshData() {...}}

Prethodni listing prikazuje generalni sadrzaj klase bez ulaska u implementacione detalje

93

Page 95: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

metoda. Sledi opis implementacije komandi iz ove klase:

Listing 79: Sadrzaj metoda OnEditCommand i OnDeleteCommand

protected virtual void OnEditCommand(){

if (_itemsCollection.CurrentItem != null){

UriQuery query = new UriQuery();query.Add(NavigationConstants.Key,

_itemsCollection.CurrentItem.Id.ToString());Manager.RequestNavigate(ShellRegions.MainRegion,

new Uri(EditFormPath + query, UriKind.Relative));}else{

InteractService.ShowNotificationInfo("Izmena objekta", "Nema selektovane stavke");}

}

protected virtual void OnDeleteCommand(){

if (_itemsCollection.CurrentItem != null){

var conf = new ConfirmationMessage();

conf.Title = "Potvrda za brisanje";conf.Message = "Da li ste sigurni da zelite da obrisete objekat sa Kljucem:" +

_itemsCollection.CurrentItem.Id;

InteractService.ShowConfirmation(conf, async message =>{

if (!message.IsConfirmed) return;var res = await DeleteItem(_itemsCollection.CurrentItem);if (res) RefreshData();

});}else{

InteractService.ShowNotificationInfo("Brisanje Stavke", "Nema selektovane stavke");}

}

Prilikom izvrsavanja OnEditCommand metode prvo se proverava da li postoji selektovanelement koji je potrebno izmeniti. Ukoliko nije selektovan nijedan element, onda se kori-sniku prikazuje poruka o gresci. U slucaju da je korisnik selektovao stavku, onda se kreiranavigacioni upit (promenljiva query) u koji se dodaje ID trenutno selektovane stavke i vrsise navigacija na view za izmenu podataka.

Implementacija komande AddCommand je prosta, jer samo poziva menadzer regionaza navigaciju na view za izmenu podataka. To je i razlog sto nije prikazana. KomandaFilterCommand jednostavno poziva metodu RefreshData, koja je opisana u daljem tekstu.Komanda CurrSellAsCsvToClipBoard uzima podatke iz trenutno selektovanih objekata ikreira CSV koji onda kopira u clipboard.

Prilikom brisanja elementa, proverava se, opet da li postoji selektovan element. Ukolikose dobije potvrda da on postoji, kreira se zahtev za interakciju za korisnikom, kako bi on, jos

94

Page 96: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

jednom, potvrdio svoju nameru. Kada se to dogodi, poziva se apstraktna metoda za brisanjeelementa i osvezava se prikaz.

Listing 80: Metode za ucitavanje podataka na korisnicki interfejs

protected async Task<BaseFilterResult<TEntity>> GetFilterResult(TFilterQuery filter){

return await Service.ProcessQueryAsync<TFilterQuery, BaseFilterResult<TEntity>>(filter);}

private async void RefreshData(){

BaseFilterResult<TEntity> result = null;result = await GetFilterResult(_filter);if (result == null) return;ItemsCollection.Items = new ObservableCollection<TEntity>(result.Results);TotalCount = result.TotalCount;FilteredCount = (uint) result.Results.Count;

}

Takode, bitno je opisati nacin na koji se ucitavaju podaci. U tu svrhu koriste se prethodnoprikazane metode. Prvo je prikazan sadrzaj metode GetFilterResult koja jedino sluzi za laksipoziv servisa koji procesira upite. Metoda RefreshData upravlja ucitavanjem i prikazivanjempodataka na korisnicki interfejs. Najpre, poziva se servis za preuzimanje podataka kome seprosleduje upit/filter. On sadrzi uslove za filtriranje. Nakon uspesnog preuzimanja podatakapopunjava se lokalna kolekcija iz rezultata i lokalni propertiji koji prikazuju ukupan brojelemenata i broj vracenih elemenata.

8.7.5 Klasa BaseNavEditViewModel

Jedna od osnovnih funkcionalnosti aplikacije je mogucnost izmene podataka odredenihobjekata. Kako dosta view model klasa implementira ovu funkcionalnost, kreirana je baznaklasa koja implementira zajednicku funkcionalnost.

Klasa sadrzi komande koje se cesto koriste prilikom izmene objekta. To su komandeza snimanje i otkazivanje promena. Sadrzi referencu na interakcioni servis za interakcijusa korisnikom i sadrzi referencu na CommandQueryService servis za izvrsavanje komandi iupita iz aplikacionog sloja.

Klasa apstraktno implementira interfejs IDataErrorInfo, gde se koristi objekat tipa Er-rorTracker koji belezi sve poruke o validacionim greskama.

Definisano je polje koje oznacava trenutno stanje objekta, u smislu da li je objekat izme-njen. Njegova vrednost se automatski azurira nakon sto se objekat izmeni ili kada se objekatucita ili snimi upotrebom servisa. Sledi listing i opis najbitnijih detalja iz klase:

Listing 81: Najbitniji detalji klase BaseNavEditViewModel

public abstract class BaseNavEditViewModel<TDataModel> : BaseNavigableViewModel, IDataErrorInfowhere TDataModel : IdentityCarier

{private readonly BaseEditEnityModel<TDataModel> _editInstance;...

95

Page 97: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

protected BaseNavEditViewModel(IRegionManager regionManager, IServInteraction interactService,string title, IServInjectCommand commandInject, CommandQueryService cqSrvice,BaseEditEnityModel<TDataModel> editInstance): base(title, regionManager, commandInject)

{..._editInstance = editInstance;// track validation for edit objecteditInstance.ValidatedObject +=

(sender, args) => { ErrorTracker.UpdateErrorStatus(args.ErrorText, args.ColumnName); };editInstance.PropertyChanged += (sender, args) => { SaveChanges.RaiseCanExecuteChanged(); };VMEditState = EditState.Unchanged;

}}

protected async void ReloadEditObject(string id){

if (string.IsNullOrEmpty(key)){

_editInstance.PopulateForNewEntity();OnNewEntityFormPopulated(EditInstance);OnPropertyChanged(String.Empty);

}else{

var entityId = long.Parse(key);TDataModel data = null;

data = await LoadEntityData(entityId);

if (data != null){

_editInstance.PopulateForEditEntity(data);}_errorTracker.Clear();_saveChanges.RaiseCanExecuteChanged();OnEditEntityFormPopulated(EditInstance);OnPropertyChanged(string.Empty);

}VMEditState = EditState.Unchanged;

}protected override bool SetProperty<T>(ref T member, T val, string propertyName){

if (!Equals(member, val))VMEditState = EditState.Modified;

return base.SetProperty(ref member, val, propertyName);}

Iz prethodnog koda, vidi se da view model objekti izmene podataka sadrze referencuna objekte modela u prezentacionom sloju, koji, pak, obmotavaju model objekte, dobijenihkao rezultat upita iz aplikacionog sloja. Prilikom konstrukcije klase, dodaje se metoda kojaprati dogadaj promene podataka, kako bi se kontrolisala mogucnost izvrsavanja komande zasnimanje podataka i metoda koja osluskuje validacione greske iz modela, kako bi se korisnikuprikazale validacine poruke.

Metoda za ucitavanje objekta, cije detalje je potrebno izmeniti ReloadEditObject koristise svuda u okviru klase. Metoda ima parametar koji predstavlja ID objekta, koji je potrebnoucitati i koji je zadat kao string, jer se u navigaciji prosleduju samo stringovi. Parametar ID

96

Page 98: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

nije obavezan i, ukoliko nije zadat, u formi se popunjuju podrazumevane vrednosti za krei-ranje novog objekta. To se postize pozivom metode PopulateForNewEntity objekta modelai generisanjem dogadaja OnNewEntityFormPopulated. U slucaju da je zadata ID vrednost,poziva se apstraktna metoda za ucitavanje podataka sa trenutnim ID-em kao parametrom,popunjava se forma za izmenu i izaziva se dogadaj OnEditEntityFormPopulated. Na kraju,pozivaju se metode za resetovanje svog stanja (kako bi se resetovala forma).

U ovoj klasi je nadogradena implementacija bazne metode SetProperty, gde je dodatalogika pracenja trenutnog stanja izmene objekta.

8.7.6 Klasa BaseEditEntityModel

Ovo je bazna klasa za sve model klase, koje omogucuju izmenu podataka. Podaci, kojise preuzmu kao rezultat upita, su obicni DTO objekti i namenjeni su samo za citanje.Cak, i kada bi se omogucila izmena tih podataka, te klase ne bi implementirale interfejse izprezentacionog sloja (koji omogucavaju efikasnu upotrebu MVVM paterna i prednosti WPFtehnologije).

Moze se reci da je ova klasa veoma slicna prethodno opisanoj klasi BaseNavEditViewMo-del i da nema svrhe dodavati jos jednu klasu, koja omogucuje izmenu podataka. Medutim,klasa BaseNavEditViewModel se vise bavi koordinacijom procesa prilikom izmene podataka,dok se ova klasa bavi samim podacima.

Takode, u ovoj klasi, apstraktno se implementira interfejs IDataErrorInfo, sadrzi refe-rencu na ID objekta, nad kojim se vrse izmene i, cuva trenutno stanje izmene.

Klasa implementira mapiranje podataka iz model objekta (dobijenog iz aplikacionog slojakao rezultat upita) i sadrzi apstraktno implementirane metode PopulateForNewEntity (gdese inicijalizuje stanje za kreiranje nove instance objekta) i PopulateForEditEntity (sa para-metrom koji predstavlja objekat za izmenu). Prikazuju se samo najbitniji delovi klase:

Listing 82: Najbitniji delovi klase BaseEditEntityModel

public enum EditState{

New,Unchanged,Modified

}

public abstract class BaseEditEnityModel<T> : BasePropertyChanged,IDataErrorInfo where T : IdentityCarier

{

private string _entityId;

public void PopulateForNewEntity(){

PopulateNewEntityForm();EntityId = null;CurrEditState = EditState.New;

}

protected abstract void PopulateNewEntityForm();

97

Page 99: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

public void PopulateForEditEntity(T entity){

PopulateEditEntityForm(entity);EntityId = entity.Id.ToString();CurrEditState = EditState.Unchanged;

}

protected abstract void PopulateEditEntityForm(T entity);

public string this[string columnName]{

get{

var res = ValidateField(columnName);var eventArgs = new ValidationArgs(columnName, res);OnValidatedObject(eventArgs);return res;

}}

Postoji referenca na trenutni ID objekta, ciji detalji se menjaju i smestaju u propertiEntityId. Implementacija metoda PopulateForNewEntity i PopulateForEditEntity je veomajednostavna, buduci da one samo pozivaju svoje apstrakne implementacije, postavljaju vred-nost propertija EntityId i podesavaju trenutno stanje izmene. Validaciona procedura, takode,poziva svoju apstraktnu verziju i nakon toga izaziva dogadaj validacije podataka.

8.7.7 Klasa VMProizvodSel

Generalno, implementacija svih klasa u projektu, koje nasleduju klasu BaseNavSearc-hViewModel veoma je jednostavna. U konstruktoru klase zadaje se naziv forme i kreira seinstanca objekta upita, koji se koristi za filtriranje podataka, pored ostalih parametara, kojise prosleduju baznoj klasi. Zadaje se putanja do forme za izmenu i pruza se implementacijametode za brisanje jednog elementa.

Listing 83: Sadrzaj klase VMProizvodSel

public class VMProizvodSel : BaseNavSearchViewModel<ProizvodFilterResultItem, ProizvodFilterQuery>{

public VMProizvodSel(IRegionManager regionManager, IServInteraction confServ,IServInjectCommand cmdInj, CommandQueryService service): base(

regionManager, confServ, "Prikaz i Selekcija Proizvoda",new ProizvodFilterQuery(),cmdInj, service)

{}

public override string EditFormPath{

get { return ProizvodViews.WProizvodEdt; }}

protected override async Task<bool> DeleteItem(ProizvodFilterResultItem currentItem){

var command = new BrisanjeProizvodaCommand();

98

Page 100: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

command.ProizvodId = currentItem.Id;return await Service.ProcessCommandAsync(command);

}}

Prethodni listing prikazuje ceo sadrzaj klase VMProizvodSel. Jedina zanimljiva metodaje DeleteItem koja kreira komandu za brisanje proizvoda i poziva servis za izvrsavanje tekomande.

8.7.8 Klasa VMProizvodNavEdt

Ova klasa nasleduje apstraktnu klasu BaseNavEditViewModel i implementira svu logikupotrebnu za izmenu i kreiranje novog proizvoda. Klasa je puna prezentacione logike koju jetesko i neprakticno opisati. Opisuju se samo najbitnije metode.

Listing 84: Najbitniji detajli klase VMProizvodNavEdt

public VMProizvodNavEdt(IRegionManager regionManager, IServInteraction interactService, IServInjectCommand cmdInj,CommandQueryService cqService): base(regionManager, interactService, "Izmena Podataka O Proizvodu", cmdInj,

cqService, new VMProizvod()){

...LoadLookupValues();

}

private void LoadLookupValues(){

_allJedinicaMere.AddRange(CqSrvice.ProcessQuery<GetAllJedinicaMereQuery, IList<JedinicaMere>>(

new GetAllJedinicaMereQuery()));...

}protected override async Task<ProizvodEdit>LoadEntityData(long entityId){

var query = new GetProizvodByIdQuery(entityId);return await GetQueryResult(query);

}

protected override async Task<bool> ProcessCreateObject(){

var noviProizCmd = new NoviProizvodCommand();noviProizCmd.Naziv = VmProizvod.Naziv;noviProizCmd.BarKod = VmProizvod.BarKod;...var res = await CqSrvice.ProcessCommandAsync(noviProizCmd);if (res){

VmProizvod.EntityId = noviProizCmd.EntityId.ToString();return true;

}return false;

}

Prilikom kreiranja nove instance ucitavaju se sve lookup vrednosti pozivanjem servisa za

99

Page 101: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

procesiranje upita, (kao sto je to prikazano u metodi LoadLookupValues). Implementacijametode za ucitavanje objekta za izmenu veoma je jednostavna, kako se samo kreira upit,poziva servis za obradu upita i vraca rezultat pozivaocu. Metoda ProcessCreateObject imaslicnu logiku, s tim sto se u ovom slucaju kreira komanda koja se salje na procesiranje. Kakoje ovo komanda za kreiranje novog objekta, nakon procesiranja komande preuzima se IDnovokreirane stavke.

8.7.9 Klasa MProizvod

Klasa MProizvod nasleduje baznu klasu BaseEditEnityModel i predstavlja omotac klaseProizvodEdit koja se nalazi u servisnom sloju upita.

Listing 85: Najbitniji detalji klase MProizvod

public class MProizvod :BaseEditEnityModel<AppServ.Query.ProizvodQueries.ProizvodEditing.Model.ProizvodEdit>

{

protected override string ValidateField(string fieldName){

if (string.Equals(fieldName, PropNames.Naziv) && !string.IsNullOrWhiteSpace(Naziv))return new ProizvodNaziv(Naziv).GetErrorMessage(true, "Naziv Proizvoda");

...return null;

}

protected override void PopulateNewEntityForm(){

_barKod = null;_grupaList.Clear();_cena = 0;...

}protected override void PopulateEditEntityForm(

AppServ.Query.ProizvodQueries.ProizvodEditing.Model.ProizvodEdit entity){

_barKod = entity.BarKod;GrupaList.Clear();GrupaList.AddRange(entity.GrupaList);...

}public string BarKod{

get { return _barKod; }set { SetProperty(ref _barKod, value, PropNames.BarKod); }

}public double UkupnaVrednost{

get { return Cena*StanjeKolicina; }}...

}

U prethodnom listingu mozemo da vidimo implementaciju metode za validaciju koja jeimplementirana upotrebom vrednosnog objekta iz sloja domena. Takode, prikazan je deo im-plementacije metode PopulateNewEntityForm, gde se postavljaju podrazumevane vrednosti

100

Page 102: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

za propertije. Implementacija metode PopulateEditEntityForm sastoji se u kopiranju vred-nosti iz ulaznog objekta. Takode, ovde je prikazan primer implementacije propertija, gde seu seteru poziva metoda bazne klase SetProperty, kako bi se omogucila obavestenja o promenipodataka view objektu. Na kraju, prikazan je vestacki dodat properti UkupnaVrednost kojise ne snima i ucitava iz aplikacionog sloja, ali pruza korisnu informaciju korisniku.

8.7.10 Klasa CommandQueryService

Ova klasa sluzi kao centralna pristupna tacka aplikacionom sloju za izvrsavanje komandii upita. Sve ostale view model klase izvrsavaju svoje komande/upite pozivom metoda oveklase. Zadatak ove klase je da pozove dispecer za obradu upita/komande, vrati rezultat uslucaju uspesnog izvrsavanja i obradi izuzetke iz aplikacionog sloja u slucaju greske.

Listing 86: Najbitniji detalji klase CommandQueryService

public class CommandQueryService{

private readonly IUnityContainer _container;private readonly IServInteraction _interactServ;

public CommandQueryService(IUnityContainer container, IServInteraction interactServ){

_container = container;_interactServ = interactServ;

}

public TResult ProcessQuery<TQuery, TResult>(TQuery query)where TQuery : IQuery<TResult> {...}

public async Task<TResult> ProcessQueryAsync<TQuery, TResult>(TQuery query)where TQuery : IQuery<TResult> {...}

public void ProcessCommand<TCommand>(TCommand command)where TCommand : ICommand {...}

public async Task<bool> ProcessCommandAsync<TCommand>(TCommand command)where TCommand : ICommand

{try{

BusyStateManager.IsBusy = true;await TaskEx.Run(() =>{

var commandDispatcher = _container.Resolve<ICommandDispatcher>();commandDispatcher.Execute(command);

});BusyStateManager.IsBusy = false;return true;

}catch (CommandProcessingException e){

BusyStateManager.IsBusy = false;var message = new DialogMessage();message.Title = "Greska prilikom procesiranja komande: " + command.GetType().Name;message.Message = e.Message;_interactServ.ShowWarningDialog(message);return false;

}

101

Page 103: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

catch (Exception e){

BusyStateManager.IsBusy = false;var message = new DialogMessage();message.Title = "Greska prilikom procesiranja komande: " + command.GetType().Name;message.Message = e.Message;message.DetailMessage = e.ToString();_interactServ.ShowErrorDialog(message);return false;

}}

}

Ova klasa sadrzi reference na Unity kontejner i interakcioni servis koje se zahtevaju u kon-struktoru klase. Sadrzi cetiri razlicite metode za procesiranje upita/komandi. Jedna grupametoda je za procesiranje komandi, a druga grupa metoda je za procesiranje upita. Takode,svaka grupa metoda sadrzi sinhronu i asinhronu verziju. Kako je implementacija svih ovihmetoda veoma slicna, prikazuje se samo implementacija metode ProcessCommandAsync.

Metoda ProcessCommandAsync sluzi za asinhrono procesiranje jedne komande. To jegenericka metoda koja sadrzi genericki parametar TCommand. On oznacava tip komandekoji se izvrsava. Naravno ta klasa mora da nasleduje osnovni interfejs za komande ICom-mand. Komanda za izvrsavanje se prosleduje ovom metodu i onda se krece sa procesiranjem.Prvi korak je vizuelno predstaviti korisniku da je u toku izvrsavanje komande i to se postizepostavljanjem vrednosti ISBusy na true klase BusyStateManager, koja dalje izvestava viewobjekat da promeni prikaz i odgovarajucom animacijom ukaze na izvrsavanje komande. Ondase razresava referenca na dispecer komandi i poziva se izvrsavanje komande kroz dispecer.Ukoliko je izvrsavanje komande uspesno, obavestava se view da je zavrseno procesiranje ko-mande i zavrsava se procesiranje sa povratnom informacijom o uspesnom procesiranju. Uko-liko dode do greske prilikom procesiranja, onda se opet obavestava view objekat o zavrsetkuprocesiranja komande ali, isto se tako korisniku prikazuje poruka o gresci. Postoje dve va-rijante poruke koje se prikazuju: prva je ocekivana i prikazuje se samo poruka iz izuzetka,dok je druga neocekivana i prikazuju se svi detalji greske korisniku.

102

Page 104: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

9 Podela aplikacije u module

Niko vise ne pise sam celu aplikaciju. Izvan sveta integrisanih sistema, skoro svi seoslanjaju na biblioteke i radne okvire koje je napisao neko drugi. Njihovom upotrebom,moguce je koncentrisati se na stvarnu logiku aplikacije dok se infrastruktura, biblioteke iradni okviri ugraduju u aplikaciju i njih obicno je razvio neko drugi. Radeci na taj nacinsmanjuje se potrebno vreme za razvoj aplikacije.

Uspeh razvoja softvera otvorenog koda poslednjih decenija cini koncept ponovne upotrebebiblioteka mnogo ubedljivijim. Za veliki broj problema postoje resenja i ona su dostupna zaupotrebu bez ikakve nadoknade. Pisanje modernih aplikacija je, umnogome, proces sklapanjakoliko je proces kreiranja. Odabir dostupnih delova i njihovo spajanje predstavlja veliki deomodernog razvoja aplikacije. Umesto pisanja svega od pocetka, oni kojima je potrebanHTTP server koriste npr. Apache ili Tomcat server, a drugi kojima je potreban sistemza rad sa bazom podataka, mogu da odaberu MySQL, PostgreSQL ili neki treci softver zaupravljanje bazom podataka. U aplikaciji programer spaja razlicite delove i dodaje logikuaplikacije. Rezultat je potpuno funkcionalna aplikacija razvijena u neverovatno kratkomroku.

Razmotrimo kako Linux distribucije rade. Fedora, Mandriva, SUSE i Debian sadrze velikibroj istih aplikacija koje su napisane od istih ljudi. Distributor ih jednostavno upakuje i spojikako bi se instalirale zajedno. Izdavaci distribucije, cesto, pisu samo centralni upravljackisoftver, instalacioni softver i pruzaju garanciju kvaliteta kako bi osigurali da sve odabranekomponente rade zajedno.

Jos jedna stvar koju je potrebno shvatiti je da niko vise kompletno ne kontrolise orga-nizaciju celog proizvoda. Ne samo izvornog koda, vec i programera, s obzirom na to da suprogrameri rasprostranjeni po celom svetu i rade po svom rasporedu. Ova situacija uopstenije retka i opasna kao sto zvuci. Svako ko je pokusao da organizuje projekat sa timom vecimod pedeset ljudi zna da je ideja imati potpunu kontrolu procesa, u najboljem slucaju, utesnailuzija.

Mogucnost upotrebe eksternih biblioteka i njihovo uklapanje u aplikaciju daje mogucnostkreiranja kompleksnog softvera sa manjim utroskom vremena i manje posla. Naravno, po-trebno je upravljati tim bibliotekama i osigurati njihovu kompatibilnost. To nije jednostavanzadatak, ali ne postoji drugi praktican i isplatljiv nacin za izgradnju sistema danasnje kom-pleksnosti.

9.1 Modularne aplikacije

Modularna aplikacija je aplikacija koja je podeljena u skup funkcionalnih jedinica, kojenazivamo modulima. Modul sadrzi deo funkcionalnosti aplikacije i, tipicno, predstavlja skuppovezanih zadataka. Modul moze ukljuciti kolekciju povezanih komponenti, ukljucujuci ko-risnicki interfejs i biznis logiku ili delove aplikacione infrastrukture, kao sto su servisi apli-kacionog nivoa za logovanje ili za autentikaciju korisnika. Moduli su medusobno nezavisni,ali mogu da saraduju na slabo povezani nacin. Modularne aplikacije mogu olaksati razvoj,

103

Page 105: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

testiranje, pustanje u produkciju i nadogradivanje aplikacije.Na primer, razmotrimo bankovnu aplikaciju. Korisnik moze pristupiti razlicitim funkcio-

nalnostima, kao sto je transfer novca, placanje racuna, azuriranje licnih informacija i sve toiz jednog istog korisnickog interfejsa. Medutim, iza scene, svaka od tih funkcija se nalazi urazlicitom modulu. Moduli komuniciraju medusobno i sa razlicitim sistemima kao sto su tobaza podataka ili web servisi. Aplikacioni servisi integrisu razlicite komponente sa svakimod modula i upravljaju komunikacijom sa korisnikom. Korisnik vidi integrisani prikaz kojiizgleda kao jedna aplikacija.

Modularni pristup pomaze pri identifikaciji velikih funkcionalnih oblasti aplikacije i dopustarazvoj i testiranje funkcionalnosti, nezavisno. Ovo moze uciniti razvoj i testiranje jedno-stavnijim, a moze uciniti aplikaciju fleksibilnijom i laksom za nadogradivanje u buducnosti.Benefit modularnog pristupa je da moze uciniti celokupnu arhitekturu aplikacije fleksibilni-jom i jednostavnijom za odrzavanje, zbog toga sto omogucava podelu aplikacije na odrzivedelove. Svaki deo sadrzi specificnu funkcionalnost i svaki deo je integrisan kroz ciste, alislabo zavisne komunikacione kanale.

Prilikom razvoja aplikacije na modularan nacin, aplikacija se struktuira u odvojene mo-dule, koji mogu biti nezavisno razvijeni. Svaki modul sadrzi deo funkcionalnosti aplikacije.Jedna od prvih dizajnerskih odluka, koje je potrebno doneti jeste kako odluciti kako podelitiaplikaciju na module. Modul bi trebalo da sadrzi skup povezanih odgovornosti. Modul mozepredstavljati vertikalni deo aplikacije ili horizontalni servisni sloj. Velike aplikacije obicnoimaju oba tipa modula.

Dijagram 17: Podela aplikacije u module

104

Page 106: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Modul bi trebalo da ima minimalni skup zavisnosti na druge module. Kada modul imazavisnost na drugi modul, treba da bude povezan upotrebom interfejsa definisanih u deljivojbiblioteci ili upotrebom dogadaja.

Cilj modularnosti je podela aplikacije na takav nacin da ona bude fleksibilna, laka zaodrzavanje, stabilna cak i prilikom ucitavanja i brisanja modula iz memorije ili u tokuizvrsavanja aplikacije. Najbolji nacin za ostvarivanje ove funkcionalnosti je dizajnirati apli-kaciju tako da moduli budu slabo zavisni.

Postoji nekoliko nacina za kreiranje i pakovanje modula. Preporucljiv nacin je kreiranjejednog projekta po modulu. Kako jedan modul predstavlja jedan projekat, olaksava se nje-gova integracija i pustanje u produkciju. Medutim, ne postoji ogranicenje da jedan projekatmora da sadrzi samo jedan modul. U nekim slucajevima ovo moze biti pozeljno kako bi seminimizovao broj projekata u soluciji. Za velike aplikacije, nije neuobicajeno imati 10 do 50modula. Razdvajanje svakog modula u poseban projekat dodaje dosta kompleksnosti i mozeusporiti performanse okruzenja za razvoj aplikacije.

9.2 Razvoj modularne aplikacije upotrebom Prism biblioteke

Prism biblioteka daje podrsku za razvoj modularnih aplikacija i upravljanje modulimau toku samog izvrsenja aplikacije. Upotrebom funkcionalnosti Prism biblioteke smanjujese vreme razvoja modularnih aplikacija, buduci da nije potrebno implementirati i testiratisopstvene biblioteke koje bi obavljale tu funkcionalnost. Prism sadrzi sledece funkcionalnostiza razvoj modularnih aplikacija:

• Katalog modula za registraciju imenovanih modula.

• Podrska upotrebe meta podataka za definisanje modula, inicijalizaciju modula i konfi-gurisanje njihovih zavisnosti.

• Integracija sa kontejnerima za ubrizgavanje zavisnosti, kako bi se podrzala slaba zavi-snost izmedu modula.

• Prilikom ucitavanja modula Prism biblioteka podrzava:

– Upravljanje zavisnostima, ukljucujuci detekciju duplikata i ciklusa kako bi se osi-guralo da su moduli samo jednom ucitani i inicijalizovani u odgovarajucem redo-sledu.

– Dovlacenje modula na zahtev u pozadini kako bi se minimizovalo vreme pokretanjaaplikacije. Ostatak modula moze biti ucitan i inicijalizovan u pozadini ili kadamoduli budu potrebni na zahtev.

9.2.1 Osnovni gradivni elemenat modularnih aplikacija - IModule interfejs

Modul predstavlja logucku kolekciju funkcionalnosti i resursa, koji su upakovani u jednulogicku celinu na nacin koji dozvoljava nezavisan razvoj, testiranje i integraciju modula u

105

Page 107: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

aplikaciji. Svaki modul sadrzi centralnu klasu, koja je odgovorna za inicijalizaciju modula iintegraciju svojih funkcionalnosti u aplikaciju. Ta klasa mora da implementira IModule inter-fejs. Postojanje klase koja implementira interfejs IModule dovoljno je da se celina identifikujekao modul. Ovaj interfejs sadrzi samo jedan metod Initialize u okviru koga se implementiralogika za inicijalizaciju i integraciju funkcionalnosti modula u aplikaciju. Zavisno od svrhemodula, modul moze registrovati view objekte u kompozitni korisnicki interfejs, dodati ser-vise u aplikaciju ili nadograditi funkcionalnost aplikacije. Sledeci kod prikazuje minimalnuimplementaciju modula.

Listing 87: Minimalni kod za deklarisanje modula

public class MyModule : IModule{public void Initialize(){

// Inicializacioni kod}

}

9.2.2 Zivotni ciklus modula

Proces ucitavanja modula u Prism aplikaciji ukljucuje sledece korake:

1. Registracija/otkrivanje modula. Moduli koje aplikacija ucitava u toku izvrsavanja sedefinisu u katalog modula. Katalog sadrzi informacije o modulima koje je potrebnoucitati, njihovu lokaciju i redosled ucitavanje modula.

2. Ucitavanje modula. Fajlovi koji sadrze module ucitavaju se u memoriju. Ova fazamoze zahtevati dovlacenje modula sa neke udaljene lokacije.

3. Inicijalizacija modula. Moduli se onda inicijalizuju, sto znaci kreiranje instance klasekoja nasleduje IModule interfejs i poziv Initialize metoda kroz IModule interfejs.

9.2.3 Katalog modula

Katalog modula sadrzi informacije o modulima koji se mogu koristiti u aplikaciji. Katalogje jednostavno kolekcija ModuleInfo klasa. Svaki modul je opisan kroz ModuleInfo klasu kojacuva podatke kao sto su naziv, tip i lokacija modula. Postoji nekoliko tipicnih pristupa zapopunjavanje kataloga modula:

• Registrovanjem modula kroz kod.

• Registrovanjem modula kroz XAML fajl.

• Registrovanjem modula upotrebom konfiguracionog fajla.

• Automatskim otkrivanjem modula u direktorijumu na disku.

106

Page 108: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

9.2.4 Integracija modula u aplikaciju

Prism pruza sledece klase za inicijalizaciju aplikacije: UnityBootstrapper i MefBootstrap-per. Ove klase se mogu upotrebiti za kreiranje i konfiguraciju menadzera modula kako bi sepronasli i ucitali moduli. Moguce je promeniti metod konfiguracije izmenom samo nekolikolinija koda.

Kao sto smo ranije napomenuli, za integraciju modula sa ostatkom aplikacije, koristise metoda Initialize. Nacin implementacije ove metode se razlikuje u implementacijama izavisi od strukture aplikacije i sadrzaja modula. Sledece su uobicajene akcije koje je potrebnouraditi prilikom integracije modula u aplkaciji:

• Dodavanje view klasa u navigacionu strukturu aplikacije. Ovo je uobicajeno prilikomizgradnje kompozitnih korisnickih interfejsa.

• Pretplacivanje na dogadaje i servise na nivou aplikacije.

• Registracija deljenih servisa.

9.2.5 Komunikacija izmedu modula

Iako moduli trebaju da imaju slabu medusobnu zavisnost, uobicajeno je za module dakomuniciraju izmedu sebe. Postoje nekoliko paterna komuniciranja na slabo zavisan nacin,pri cemu, svaki ima svoje prednosti i mane. Tipicno, koristi se kombinacija ovih paterna zakreiranje resenja.

• Slabo zavisni dogadaji. Modul moze emitovati informaciju da je odredeni dogadaj iza-zvan. Ostali moduli, mogu se pretplatiti na te dogadaje kako bi mogli da reaguju kadase dogadaj izazove. Slabo zavisni dogadaji su lagani nacin podesavanja komunikacijeizmedu dva modula zato sto se veoma lako implementiraju. Medutim, dizajn koji sedosta oslanja na dogadaje, moze postati tezak za odrzavanje, posebno ukoliko postojipotreba za rukovanjem velikim brojem dogadaja kako bi se ispunio jedan zadatak. Utom slucaju, mozda je bolje razmotriti deljeni servis.

• Deljeni servisi. To je klasa kojoj se moze pristupiti kroz dobro poznati interfejs.Tipicno, deljeni servisi se nalaze u deljenom projektu i pruzaju sistemske servise, kaosto je autentikacija, logovanje i konfiguracija.

• Deljeni resursi. Ukoliko nije pozeljno za module da komuniciraju medusobno, moguceje, takode, omoguciti komunikaciju indirektno kroz deljive resurse kao sto je baza po-dataka ili web servis.

9.3 Ubrizgavanje zavisnosti (eng. Dependency Injection – DI)

Kontejneri, kao sto su to Unity i MEF, dopustaju jednostavnu upotrebu paterna inverzijekontrole IoC i ubrizgavanje zavisnosti DI (Dependency injection), koji pomazu prilikomsastavljanja komponenti na slabo zavisan nacin. Kontejner omogucuje komponentama da

107

Page 109: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

dobiju reference na druge komponente od kojih zavise bez hardkodiranja tih referenci. Zatokontejneri promovisu bolju ponovnu upotrebu koda i unapreduju fleksibilnost aplikacije.

Aplikacije bazirane na Prism biblioteci su modularne aplikacije, i, potencijalno, sastojese od velikog broja slabo zavisnih klasa i servisa. Njima je potrebno da medusobno saradujukako bi pruzili sadrzaj i primili obavestenja zasnovana na akcijama. Zato sto su objekti slabopovezani, potreban je nacin za medusobnu interakciju i komunikaciju kako bi se isporucilaneophodna biznis funkcionalnost.

Kako bi ove komponente bile zajedno povezane, aplikacije bazirane na Prism biblioteci seoslanjaju na kontejner ubrizgavanja zavisnosti (dependency injection container). Kontejnerismanjuju zavisnost izmedu objekata pruzajuci mogucnost instanciranja klasa i upravljanjemnjihovim zivotnim vekom. Tokom kreiranja objekta, kontejner ubrizgava sve zavisnosti kojeobjekat zahteva kao parametre. Ukoliko objekti zavisnosti jos nisu kreirani, kontejner ih kre-ira koristeci isti postupak. U nekim slucajevima, kontejner se i sam razresava kao zavisnost.

Postoje nekoliko prednosti koje se dobijaju upotrebom kontejnera:

• Sklanja potrebu komponenti da sama inicijalizuje svoje zavisnosti i upravlja njihovimzivotnim vekom.

• Dopusta promenu implementacije zavisnosti bez promene klijenske klase.

• Omogucuje testiranje tako sto omogucava da zavisnosti budu lazirane.

• Cini aplikaciju laksom za odrzavanje omogucavajuci da se nove komponente lako do-daju u sistem.

9.3.1 Upotreba kontejnera - osnovni scenariji

Kontejneri se koriste za dve primarne svrhe: registraciju i razresavanje zavisnosti.

9.3.1.1 Registracija

Pre nego sto je moguce ubrizgati zavisnost u objekat, potrebno je registrovati tipovezavisnosti sa kontejnerom. Registracija tipa, obicno, ukljucuje prosledivanje interfejsa ikonkretnog tipa koji implementira taj interfejs kontejneru. Postoje primarno dva nacinaregistrovanja tipa i objekata: kroz kod ili kroz konfiguraciju. Specificnosti registracije zaviseod tipa kontejnera.

Postoje dva nacina registracije tipova i njihovih implentacija u kontejner kroz kod:

• Moguce je registrovati tip ili mapiranje u kontejner. U odgovarajuce vreme kontejnervraca referencu na instancu tipa koji je zadat.

• Moguce je registrovati postojecu instancu objekta u kontejner kao Singleton. Kontejnervraca referencu na postojeci objekat.

108

Page 110: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

9.3.1.2 Razresavanje

Nakon sto se tip registruje, moguce je da se on razresi ili ubrizga kao zavisnost. Kada setip razresava, kontejner kreira novu instancu tipa i ujedno se ubrizgavaju sve zavisnosti teinstance.

Generalno, kada se tip razresava jedna od tri akcije se izvrsava:

• Ukoliko tip nije registrovan, baca se izuzetak.

• Ukoliko je tip regitrovan kao singleton, kontejner vraca singleton instancu. Ukoliko jeovo prvi put da se poziva kontejner za razresavanje, on po potrebi kreira novu instancu.

• Ukoliko tip nije registrovan kao singleton, kontejner vraca novu instancu.

9.4 Paterni za ubrizgavanje zavisnosti

Veliki broj ljudi mesa princip inverzije zavisnosti – DIP, inverziju kontrole – IoC i ubri-zgavanje zavisnosti – DI. U sledecem tekstu dajemo osnovna objasnjenja ovih pojmova.

DIP predstavlja softverski dizajn princip, IoC predstavlja dizajn patern, a DI predstavljaimplementaciju jedog tipa IoC. Pogledajmo sada koja je razlika izmedu principa i paterna:

• Dizajn princip nam pruza direktive. Princip samo kaze sta je dobro i sta nije dobro.Ne kaze nam nista o tome kako da resimo problem. Samo pruza direktive koje pomazudobrom dizajnu sofvera i kako izbeci los dizajn.

• Dizajn patern je generalno resenje za cest problem u okviru datog konteksta u dizajnusoftvera.

9.4.1 Princip inverzije zavisnosti (eng. Dependency inversion principle – DIP)

Princip glasi: Umesto da moduli nizeg sloja definisu interfejs na koji moduli viseg slojamogu da zavise, moduli viseg sloja treba da definisu interfejs koji moduli nizeg sloja imple-mentiraju. Formalna definicija principa glasi:

1. Moduli viseg sloja ne treba da zavise od modula nizeg sloja. Oba bi trebaloda zavise od apstrakcija.

2. Apstrakcije ne bi trebalo da zavise od detalja. Detalji treba da zavise odapstrakcija.

Na osnovu dijagrama 18 vidimo da u prvom slucaju moduli viseg sloja zavise od interfejsanizeg sloja. Kada se kreira nova klasa nizeg sloja, potrebno je izmeniti klasu viseg sloja kakobi se podrzala nova funkcionalnost, sto povecava kompleksnost odrzavanja.

U drugom slucaju vidimo sistem nakon inverzije zavisnosti. Ovde klase viseg sloja definisuinterfejs. Takode, klase viseg sloja ne zavise direktno od klasa nizeg sloja. Klase nizeg slojaimplementiraju interfejs definisan u klasi viseg sloja, tako da klasa viseg sloja ne treba dase promeni kada se pojavi nova implementacija.

Sledece su karakteristike sistema koji ne odrzava DIP:

109

Page 111: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 18: Zavisnosti klasa pre i posle upotrebe DIP principa

• Sistem je rigidan. Tesko je promeniti deo sistema bez uticaja na veliki broj drugihdelova sistema.

• Sistem je krhak. Kada napravimo promenu, moguce je da prestanu da rade funkcio-nalno nepovezani delovi sistema.

• Sistem je nepomican. Tesko ga je ponovo upotrebiti u drugoj aplikaciji, zato sto jetesko iscupati kod iz trenutne aplikacije.

Kako je DIP princip, on ne nudi resenje problema. Ukoliko zelimo da znamo kako resitiproblem, moramo da razmotrimo IoC.

9.4.2 Inverzija kontrole (eng. Inversion of Control – IoC)

IoC definise nacin na koji mozemo da odrzavamo DIP. IoC je moguce prakticno primenitiprilikom razvoja softvera. Postoji veliki broj definicija za IoC. Ovde se iznosi jednostavnadefinicija kako bismo lakse razumeli patern.

IoC je u osnovi patern za primenu DIP. Pojednostavljeno, IoC predstavlja inverziju kon-trole necega, zamenom kontrolora. Posebna klasa ili drugi modul u sistemu odgovorni su zakreiranje objekta od spolja. IoC znaci da menjamo kontrolu u odnosu na normalni nacinrada.

DIP kaze da moduli viseg sloja ne bi trebalo da zavise od modula nizeg sloja i da bioba trebalo da zavise od apstrakcija. IoC je nacin za pruzanje te apstrakcije, tj. nacin zapromenu kontrole. Ukoliko zelimo da modul viseg sloja bude nezavisan od modula nizeg slojapotrebno je invertovati kontrolu tako da modul nizeg sloja ne kontrolise interfejs i kreiranjeobjekata. Konacno IoC pruza nacin za inverziju kontrole.

IoC je moguce implementirati na sledece nacine:

• Inverzijom interfejsa. Umesto da pruzalac usluga definise interfejse, koji se, onda,koriste od strane klijenta, klijent definise interfejse koje zatim pruzalac usluga imple-mentira.

110

Page 112: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Inverzijom toka: Inverzija toka kontrole je fundamentalna ideja IoC koja je slicnaholivudskoj izreci “Don’t call us, we will call you”. Tokom obavljanja biznis zadatkane upravlja pruzalac usluga, vec se ta kontrola daje klijentu.

• Inverzijom kreiranja: Ovaj vid inverzije se najcesce koristi od strane programera.

9.4.3 Ubrizgavanje zavisnosti (eng. Dependency Injection – DI)

DI je tip IoC gde pomeramo kreiranje i sastavljanje zavisnosti izvan klase, koja zavisi odnjih. U normalnom objektu zavisnosti su kreirane unutar zavisne klase. Upotrebom DI tose postize izvan zavisne klase.

Sledeci su tipovi ubrizgavanje zavisnosti.

• Ubrizgavanje u konstruktor. Prosleduju se zavisni objekti u konstruktor objekta zavi-sne klase. Onaj koji ubrizgava zavisnosti, kreira ih i prosleduje kroz konstruktor.

• Ubrizgavanje kroz seter metoda. Ovo je jos jedan tip DI tehnike gde prosledujemozavisnosti kroz seter umesto kroz konstruktor.

• Ubrizgavanje upotrebom interfejsa. Ovaj metod najmanje se koristi u praksi i komplek-san je u odnosu na prethodna dva. Zavisna klasa implementira interfejs. Interfejs imametod za postavljanje zavisnosti. Onaj koji ubrizgava zavisnosti, koristi interfejs zapostavljanje zavisnosti. Tako mozemo da kazemo da zavisna klasa ima implementacijuinterfejsa i ima metod za postavljanje zavisnosti.

9.4.4 Uklapanje pojmova

Dijagram 19: Uklapanje pojmova DIP, IoC i DI

111

Page 113: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 19 prikazuje kako se sve uklapa zajedno. DI nije jedini nacin implementacijeinverzije kreiranjem, vec postoji vise takvih nacina. Na vrhu, nalazi se DIP koji predstavljanacin dizajniranja softvera koji ne kaze nista o tome kako napraviti nezavisan modul. IoCpruza nacin za primenu DIP i ne pruza specificnu implementaciju. On pruza metode zainverziju kontrole.

9.5 Implementiranje modula u aplikaciji

Potrebno je definisati module u aplikaciji, i dati primer prakticne primene svih gore nave-denih pojmova. Za definisanje modula potrebno je iskustvo u razvoju softverskih aplikacijai dobro poznavanje problema koji se resava. Ukoliko se definisu moduli koji definisu previsefunkcionalnosti, postoji problem da se iscupaju odredene funkcionalnosti iz modula. Ukolikose definisu moduli sa malo funkcionalnosti, onda, verovatno, modul zahteva dosta konfigu-racionih parametara. Projekat je podeljen, na projekte/module na vertikalni i horizontalninacin.

9.5.1 Horizontalni slojevi aplikacije

Aplikacija je horizontalno podeljena u projekte po ugledu na standardne DDD slojeveaplikacije. Uglavnom je cilj bio imati jedan projekat po sloju kako bi se aplikacija ne bi previseusitnjavala. Medutim, postoje situacije u kojima je bilo potrebno napraviti kompromisekreiranjem vise projekata za jedan sloj.

Dijagram 20: Horizontalna podela aplikacije na projekte

112

Page 114: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Dijagram 20 prikazuje vertikalni isecak aplikacije, tj. horizontalne slojeve aplikacije. Naslici mozemo da vidimo da aplikacija sadrzi dve strane, komandnu stranu i stranu upita.Takode, mozemo da primetimo da je arhitektura strane upita dosta jednostavnija. Sledikratak opis svih slojeva.

9.5.1.1 Sloj pristupa podacima

Sloj pristupa podacima sadrze i komandna strana i strana upita. Komandna strana sesastoji iz repozitorijuma, dok se strana upita sastoji iz implementacija hendlera upita. Izdijagrama mozemo, takode, da vidimo da su u oba slucaja izvrnute zavisnosti kako visi slojevine bi zavisili od konkretne implementacije sloja pristupa podacima.

9.5.1.2 Sloj domena

Sloj domena je podeljen u dva projekta: osnovni i projekat koji sadrzi implementacijuvrednosnih objekata. Razlog za razdvajanje na dva projekta je prezentacioni sloj. Problemje bio validacija u prezentacionom sloju. Naime, bilo je potrebno validirati razlicita polja,koja vec sadrze validacionu logiku u vrednosnim objektima. Obicno, validaciona logika sekopira za web aplikacije, medutim kako je ovo desktop aplikacija, postojala su dva izbora:duplirati logiku ili referencirati vrednosne objekte. Kako je reseno da se referenciraju vred-nosni objekti da bi se izbegla duplikacija koda, bilo ih je potrebno izbaciti u poseban projekatkako prezentacioni sloj ne treba da ima znanje o implementaciji domena.

9.5.1.3 Aplikacioni sloj

Aplikacioni sloj komandne strane sastoji se iz dva projekta. Prvi je *Command projekatkoji sadrzi samo komande koje su dostupne klijentu, bez ikakve implementacije, dok projekat*CommandImpl sadrzi implementaciju komandi i upravlja koordinacijom zadataka potrebnihda se izvrsi komanda.

Strana upita sadrzi samo jedan projekat koji sadrzi i definiciju upita i model koji se vracanakon izvrsenja upita.

9.5.1.4 Prezentacioni sloj

Prezentacioni sloj je sloj koji se nalazi na “vrhu” jer je on zaduzen za upravljanje interak-cijom sa korisnikom. Sastoji se uglavnom od MVVM objekata i dodatnog infrastrukturnogkoda koji uglavnom predstavlja implementaciju kontrola koje sluze za prikaz razlicitih po-dataka. Kao sto je receno, upravlja interakcijom sa korisnikom, navigacijom u aplikaciji ikreira komande i upite za aplikacioni sloj.

U slucaju desktop aplikacija, aplikacioni sloj moze da se prelije u prezentacioni sloj,jedan primer toga je upravljanje privilegijama gde se razlicitim korisnicima prikazuju razliciteforme.

113

Page 115: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

9.5.2 Definisanje horizontalnih modula aplikacije

Aplikacija je horizontalno podeljena na projekte. Potreban je nacin za inicijalizaciju ispajanje projekata kako bi svi projekti cinili celinu. U isto vreme, potrebna je mogucnostukljucivanja i zamena odgovarajucih delova bez izmene izvornog koda (u web aplikacijamaje cesto zahtev ucitati module u toku izvsavanja aplikacije, bez ponovnog pokretanja). Sadaje potrebno definisati module.

Moduli trebaju da budu nezavisni jedan od drugog, u smislu, da mogu nezavisno da sekompajliraju i pustaju u produkciju, jer je to jedan od najbitnijih benefita modula. Kako jeprakticno nemoguce da projekti potpuno budu nezavisni jedan od drugog moduli se povezujupreko deljivih projekata. Deljivi projekti ne treba da budu oni projekti koji sadrze logikumodula vec samo projekti koji izlazu interfejs. Na osnovu tog razmisljanja, aplikacija jehorizontalno podeljena u sledece module:

Dijagram 21: Horizontalni sloj podeljen u module

• PresentationModule: sadrzi prezentacionu logiku i koristi komande i upite. Ko-mande i upiti definisani su u projektima AppServ.Command i AppServ.Query. Medutimprezentacioni modul nije svestan njihove implementacije, buduci da ovi projekti samodefinisu izgled upita/komandi.

• CommandModule: sadrzi svu biznis logiku za procesiranje komandi. Implementirakomande definisane u projektu Command upotrebom agregata, entiteta, vrednosnihobjekata i repozitorijuma definisanih u projektu DomainModel. Medutim nema nika-kvo znanje o tome kako su repozitorijumi implementirani, s obzirom na to da referencirasamo interfejse koji su definisani u projektu DomainModel.

• QueryModule: sadrzi logiku za procesiranje upita. Implementira upite i kreiraobjekte modela koji su definisani u projektu AppServ.Query

114

Page 116: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• RepositoryModule: implementira interfejse repozitorijuma. Referencira projekatDomainModel i implementira interfejse repozitorijuma.

9.5.3 Vertikalni moduli aplikacije

U odnosu na horizontalni pogled aplikacije, gde donekle postoje standardni slojevi, ver-tikalni slojevi se vise definisu u odnosu na potrebe biznisa. Veoma je bitno odrediti koju ikoliko logike sadrzi vertikalni modul. Ukoliko sadrzi previse logike, teze je iscupati odredenulogiku kao nezavisnu celinu. Sa druge strane, ukoliko su moduli previse mali, potrebno jekonfigurisati sve te male module i dobri mehanizmi komunikacije izmedu njih. Definisani susledeci moduli:

Dijagram 22: Vertikalni moduli aplikacije

• ProizvodModule. Sadrzi funcionalnosti za rad sa proizvodima kao sto su CRUDoperacije za proizvod, promena cene, izmena trenutnog stanja u magacinu, uclanjivanjei isclanjivanje proizvoda iz razlicitih grupa proizvoda.

• KalkulacijaModule. Sadrzi funkcionalnosti za izradu faktura i kalkulacija, rad sadobavljacima, kalkulisanje faktura i procesiranje kalkulacija.

• POSModule. Sadrzi funkcionalnost kucanja racuna.

• ReportingModule. Sadrzi razlicite izvestaje. Za sada su to izvestaji koji grafickiprikazuju najvece dobavljace, najprodavanije proizvode i dnevni pazar za odredenivremenski period.

• Kernel. Kernel, zapravo, i nije modul, ali ovde se konceptualno posmatra kao moduljer se referencira u svim ostalim modulima. Kernel cine projekti koji sadrze objektekoji su zajednicki za sve ostale module. U kernelu je definisano vecina baznih intefejsai klasa koji se koriste u ostalim objektima. Kernel sadrzi iste projekte/slojeve kao iostali moduli jer su zajednicke funkcionalnosti grupisane na nuvou projekta.

115

Page 117: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Infrastrukturni Moduli. Pored prikazanih modula na slici 22, projekat sadrzi do-datne module koji nisu prikazani na njoj. Ovi moduli obicno implementiraju infra-strukturne servise koji su definisani u kernelu ili u ostalim modulima. Na primer,modul moze implementirati servis za slanje SMS-a koji se nalazi u kernelu i koji se ko-risti u svim ostalim modulima. Modul MySql.Database je definisan kako bi prikljucioMySql implementaciju repozitorijuma ostatku aplikacije.

Kao sto mozemo da vidimo na osnovu dijagrama 22 svi moduli referenciraju Kernel.Takode, moduli POS i Kalkulacija zavise od modula Proizvod kako je potrebno smanjitikolicinu proizvoda na stanju prilikom kucanja racuna i potrebno je promeniti cenu proizvodaprilikom kalkulisanja fakture. Neophodno je zapaziti da Prodavnica modul, glavni modulaplikacije, referencira samo Kernel.

Kako moduli ne smeju direktno da se referenciraju medusobno, potrebno je ukloniti vezuizmedu POS i Proizvod modula, kao i Kalkulacija i Proizvod modula. To je reseno tako stosu dodati novi deljeni projekti koji se koriste u oba modula. Kako POS i Kalkulacija modulizahtevaju usluge od modula Proizvod, kreiraju se deljeni projekti (koji definisu servise),koji se koriste u klijentskim modulima Kalkulacija i POS, a implementiraju se u moduluProizvod. Nakon dodavanja novih projekata, dijagram zavisnosti modula izgleda kao na slici23.

Dijagram 23: Vertikalni moduli aplikacije nakon sto su uvedeni deljeni projekti

9.6 Inicijalizacija aplikacije i povezivanje modula

Inicijalizacija aplikacije krece iz modula Prodavnica. Kao sto smo videli na osnovu di-jagrama, ovaj modul je nezavisan od ostalih modula (osim Kernela), a zaduzen je i zakonfiguraciju cele aplikacije i ucitavanje ostalih modula. Izvrsavanje Prism aplikacije kreceiz Bootstrapper klase, koja se obicno nasleduje i sluzi za konfiguraciju aplikacije prilikomucitavanja u memoriju (vise o inicijalizaciji Unity aplikacija mozete naci u dokumentacijiza Prism biblioteku [14]). Jedan od koraka prilikom inicijalizacije jeste ucitavanje katalogamodula. Kako se u aplikaciji katalog modula ucitava iz konfiguracionog fajla jedini kod kojise nalazi u metodi za konfiguraciju modula je sledeci:

116

Page 118: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 88: Metoda za konfigurisanje kataloga modula

protected override IModuleCatalog CreateModuleCatalog(){

return new ConfigurationModuleCatalog();}

Sva ostala konfiguracija nalazi se u XML konfiguracionom fajlu App.conf. U tom fajlu jeizmedu ostalog definisan string za konekciju prema bazi podataka, nacin logovanja informa-cija o izvrsenju aplikacije i definisani su moduli koje je potrebno ucitati prilikom izvrsenjaaplikacije. Kada se moduli ucitavaju na ovaj nacin, onda ih je lako dodati ili izbrisati izaplikacije bez promene izvornog koda. XML za inicijalizaciju modula je, uglavnom, isti zasve module i predstavlja varijaciju sledeceg primera:

Listing 89: Ucitavanje modula u aplikaciju

<module assemblyFile="Kernel.AppServ.QueryDispatcher.dll"moduleType="Kernel.AppServ.QueryDispatcher.Module.MainModule,Kernel.AppServ.QueryDispatcher, Version=1.0.0.0,Culture=Neutral, PublicKeyToken=null"moduleName="KernelQueryDispatcher" startupLoaded="true"/>

Prilikom inicijalizacije potrebno je podesiti zavisnost modula u smislu redosleda inicija-lizacije modula. U projektu uglavnom nema zavisnosti osim jednog izuzetka. Modul Doma-inEventsProcessor inicijalizuje objekat DomainEventsDispatcher i registruje ga u kontejnerkao Singleton. Buduci da je potrebna obrada dogadaja prilikom procesiranja komandi, svikomandni moduli moraju da sacekaju da se prvo inicijalizuje ovaj modul.

9.6.1 Inicijalizacija modula

Moduli se inicijalizuju tako sto se kreira instanca klasa koja nasleduje interfejs IModulei pozove se metoda Initialize. Svaki modul u projektu sadrzi po jednu klasu koja nasledujeinterfejs IModule i, uglavnom, se nalazi u direktorijumu Module sa nazivom MainModule.Sledi primer inicijalizacije modula DomainEventsProcessor :

Listing 90: Inicijalizacija medula DomainEventsProcessor

public MainModule(IUnityContainer container){

_container = container;}

public void Initialize(){

// init domain events dispatchervar dispatcher = new DomainEventsDispatcher(_container);_container.RegisterInstance(dispatcher, new ContainerControlledLifetimeManager());_container.RegisterType<IDomainEventsDispatcher, DomainEventsDispatcher>();_container.RegisterType<IDomainEventsRegistrator, DomainEventsDispatcher>();

}

117

Page 119: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Najcesce prilikom konstrukcije modula zahteva se referenca na kontejner kako bi mogaoda se iskoristi za registraciju i razresavanje tipova. U prethodnom primeru, dispecer dogadajadomena je registrovan kao Singleton. Referenca se dobija na njega kadgod se zatrazi referencana interfejs IDomainEventsRegistrator, koji se koristi za registrovanje hendlera dogadaja iliinterfejs IDomainEventsDispatcher, koji se koristi kada je potrebno dispecovati dogadaje.Kao sto smo videli iz ranijih poglavlja, kada je potrebno razresiti odredenu instancu in-terfejsa, obicno se ta zavisnost zahteva u konstruktoru, koje zatim kontejner automatskiubrizgava. Kontejner se uglavnom koristi prilikom inicijalizacije aplikacije za registrovanjetipova i na strateskim mestima u projektu za razresavanje referenci, kao sto je to UnityQu-eryDispatcher, UnityCommandDispatcher i metod inicijalizacije modula. Uglavnom je losapraksa koristiti referencu na kontejner u objektima, koji zahtevaju zavisnost (razloge za tomozemo videti u ovom izvoru [26]).

118

Page 120: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

10 Korisnicki interfejs

Korisnicki interfejs moze biti konstruisan pracenjem jedne od sledecih paradigmi:

• Sve kontrole forme se nalaze u jednom XAML fajlu, izgled forme je poznat u tokudizajniranja.

• Logicki bliski delovi su povezani u posebne elemente, obicno korisnicke kontrole. Tielementi se referenciraju u formama. Izgled forme je poznat u toku dizajniranja.

• Logicki bliski delovi forme su povezani u posebne elemente. Nije poznato koji ceelementi biti smesteni na formu jer se oni dinamicki dodaju u vremenu izvrsavanja.Aplikacije koje koriste ovu metodologiju nazivaju se kompozitne aplikacije.

Korisnicki interfejs kompozitnih aplikacija je sastavljen od vizuelnih komponenti kojesu medusobno nezavisne. Komponente su obicno view klase i obicno se nalaze u razlicitimmodulima aplikacije (ne obavezno). Ukoliko je aplikacija podeljena u module, potreban jenacin konstruisanja slabo zavisnog korisnickog interfejsa, tako da korisniku aplikacije i daljepredstavlja neprestano korisnicko iskustvo i pruza potpuno integrisanu aplikaciju.

Kako bi konstruisali UI, potrebna je arhitektura koja dopusta kreiranje view objekatasastavljenih od nezavisnih vizuelnih elemenata generisanih u vremenu izvrsavanja. Dodatno,arhitektura treba da pruzi strategiju za medusobnu komunikaciju elemenata bez uvodenjadodatnih zavisnosti izmedu njih.

10.1 Koncepti rasporedivanja UI elemenata

Polazni objekat u kompozitnoj aplikaciji, poznat je kao Shell. Shell se ponasa kao glavnastrana aplikacije. Shell moze da sadrzi jedan ili vise regiona. Regioni oznacavaju lokacije zaprikaz, gde je moguce ubaciti sadrzaj vremenu izvrsenja. Sadrzaj regiona moze biti ucitanautomatski ili na zahtev, zavisno od potreba aplikacije.

Tipicno, sadrzaj regiona je view. View klase sadrze deo korisnickog interfejsa koji pred-stavlja posebnu celinu, tj. odvojen je od ostalih delova aplikacije. View je moguce definisatikao korisnicku kontrolu, sablon podataka ili cak kao kontrolu.

Region upravlja prikazom i rasporedom svojih view objekata. Regionima moze da sepristupi po njihovim imenima. Oni podrzavaju dinamicko dodavanje i brisanje view obje-kata. Mozemo misliti o regionima kao o kontejnerima u kojima se view objekti automatskiucitavaju.

10.1.1 Shell

Shell predstavlja glavni view aplikacije koji sadrzi primarni prezentacioni sadrzaj. UWPF aplikaciji, Shell predstavlja instancu Window klase.

Shell igra ulogu glavne strane i pruza strukturni raspored elemenata u aplikaciji. Shellsadrzi jedan ili vise imenovanih regiona, gde se ubrizgavaju view objekti iz razlicitih modula

119

Page 121: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

koje je potrebno prikazati. Takode, moze definisati i odredene elemente, koji se nalaze uprvom planu, kao sto je pozadina, glavni meni i lista alata.

Shell definise celokupan izgled aplikacije. Moze definisati stilove i granicnike koji sudostupni samo za njega, a, takode, moze definisati stilove, sablone i teme koji se primenjujuna view objekte (koji se ubacuju u regione).

Tipicno, Shell je deo glavnog projekta i on moze (ali i ne mora) referencirati module kojisadrze view objekte, koje je potrebno ucitati u regione.

10.1.2 View

View objekti predstavljaju glavnu gradivnu jedinicu korisnickog interfejsa u kompozit-noj aplikaciji. View objekat je moguce definisati kao korisnicku kontrolu, stranicu, sablonpodataka ili kao prilagodenu kontrolu (custom control). View objekat sadrzi deo korisnickoginterfejsa koji treba da bude nezavisan od ostalih delova aplikacije sto je moguce vise.

10.1.3 Kompozitni view

View objekat koji podrzava specificnu funkcionalnost moze postati previse komplikovan.U tom slucaju, verovatno je potrebno podeliti taj objekat na nekoliko manjih izvedenihview objekta, gde glavni objekat rukuje svojom konstrukcijom i upotrebom izvedenih viewobjekata. Ovo moze da se postigne staticki u vremenu dizajna ili dinamicki u vremenuizvrsavanja upotrebom regiona. View objekat, koji nije u potpunosti definisan u jednojklasi, obicno se naziva kompozitni view (composite view). U velikom broju situacija, kompo-zitni view objekat je odgovoran za konstruisanje izvedenih view objekata i za koordinisanjenjihovim medusobnim interakcijama.

10.1.4 Konstruisanje view objekata

U kompozitnim aplikacijama, view objekti, koji se nalaze u razlicitim modulima, morajubiti prikazani na zadatim lokacijama korisnickog interfejsa u trenutku izvrsavanja aplikacije.Kako bi se ovo postiglo, potrebno je definisati lokacije gde se view objekti prikazuju i nacinnjihovog kreiranja. U Prism aplikacijama, ovo je moguce uraditi na dva nacina:

• View Discovery - potrebno je podesiti vezu izmedu imena regiona i tipa view objekta.Kada se region kreira, on pretrazuje sve definisane veze za taj region, instancira viewobjekte i smesta ih na odgovarajuce lokacije.

• View Injection - referenca na region se nalazi u kodu, view objekat programski seubacuje u region. Obicno, ovo se radi tokom inicijalizacije modula ili kao rezultatakcije korisnika. Kod aplikacije treba da napravi upit po nazivu menadzeru regionakako bi pribavio referencu na region. Nakon uspesnog pribavljanja reference na region,potrebno je ubaciti view objekat u region.

120

Page 122: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

10.2 Navigacija

Kada korisnik radi sa aplikacijom, korisnicki interfejs se konstanto azurira kako bi odraziotrenutni zadatak i podatke sa kojima korisnik radi. Proces kojim aplikacija koordinise ovepromene naziva se navigacija.

Cesto, navigacija znaci da odredene kontrole treba da budu uklonjene sa korisnickoginterfejsa, dok je druge kontrole potrebno prikazati. U drugim slucajevima, navigacija znacida je potrebno azurirati vizuelni status jedne ili vise kontola, na primer: ako razmotrimomaster-detail scenario, podaci prikazani na kontroli, koja prikazuje detalje, azuriraju se takosto se prikazuju detalji trenutno odabrane stavke u master kontroli. Svi ovi scenariji moguda se razmatraju kao navigacija, zato sto je potrebno azurirati korisnicki interfejs ne bi li seodrazio trenutni korisnicki zadatak i trenutno stanje aplikacije.

Navigacija unutar aplikacije moze biti rezultat interakcije korisnika sa korisnickim inter-fejsom ili iz same aplikacije kao rezultat nekog internog procesa. U jednim slucajevima na-vigacija zahteva veoma jednostavna azuriranja korisnickog interfejsa koja ne implementirajuposebnu logiku aplikacije. U drugim slucajevima, aplikacija moze implementirati komplek-snu logiku kako bi se osigurao odredeni biznis scenario. Na primer, aplikacija moze da nedozvoli korisniku da se udalji sa trenutnog ekrana, dok se ne osigura to da su podaci koje jekorisnik uneo tacni.

Navigaciju koja je postignuta preko promene statusa na postojecim kontrolama u vizu-elnom stablu, nazivamo statusno bazirana navigacija. Navigaciju koja je postignuta dodava-njem ili brisanjem elemenata iz vizuelnog stabla nazivamo prezentaciono bazira navigacija.

10.3 Prism navigacija

Implementiranje navigacije u WPF aplikacijama je lako zato sto WPF ima direktnupodrsku za navigaciju. Medutim, implementiranje navigacije u kompozitnim aplikacijamamoze biti izazovno, jer te aplikacije koriste slabo zavisne komponente. U tom slucaju, moguceje upotrebiti Prism bilibioteku za implementiranje navigacije.

10.3.1 Statusno bazirana navigacija

Kod statusno bazirane navigacije, view objekat koji predstavlja korisnicki interfejs seazurira kroz promene stanja u view model objektu ili kroz interakcije korisnika sa korisnickiminterfejsom. Kod ovog tipa navigacije, umesto zamene view objekta drugim view objektom,menja se samo stanje view model objekta koje se reflektuje na korisnicki interfejs. Zavisnood toga kako se menja stanje view model objekta, azuriranje korisnickog interfejsa korisnikumoze izgledati kao navigacija.

Ovakav stil navigacije pogodan je u sledecim siguacijama:

• Iste podatke/funkcionalnost potrebno je prikazati na drugaciji nacin - drugaciji stiloviili formatiranje.

• Potrebno je promeniti stil ili raspored komponenti na korisnickom interfejsu kako bi seodrazilo trenutno stanje view model objekta.

121

Page 123: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Potrebno je inicirati ogranicenu modalnu ili nemodalnu interekciju sa korisnikom.

Ovaj stil navigacije nije pogodan u situacijama u kojima korisnicki interfejs mora dapredstavi drugacije podatke korisniku ili u sitacijama gde korisnik treba da odradi drugizadatak. U tim slucajevima, bolje je implementirati posebne view/view model objekte zaobradu tog zadatka. Slicno, ovaj stil navigacije nije pogodan ako su razlicita stanja preteranokompleksna jer kod u klasama moze postati prevelik i tezak za odrzavanje. U ovom slucaju,bolje je implementirati view baziranu navigaciju upotrebom posebnih view objekata.

10.3.2 View bazirana navigacija

Iako statusno bazirana navigacija moze biti korisna u situacijama na koje smo ranije uradu ukazali, navigacija u okviru aplikacije se najcesce postize zamenom jednog view objektadrugim.

Zavisno od zahteva i kompleksnosti aplikacije, ovaj proces moze biti veoma kompleksan izahtevati pazljivu koordinaciju. Sledeci su cesti izazovi koje je potrebno razmotriti prilikomimplementiranja view bazirane navigacije:

• Meta navigacije - kontejner ili kontrola na koji se dodaju view objekti moze reago-vati drugacije na navigaciju prilikom dodavanja i sklanjanja view objekata. Takode,dodavanje i sklanjanje objekata iz kontrole moze se vizuelno predstaviti na razlicitenacine. U najvecem broju slucajeva metu navigacije predstavlja obicna kontejnerskakontrola koja moze da sadrzi i predstavi samo jedan view objekat. U tom slucaju, sce-nario dodavanja i sklanjanja komponenti je jednostavan. Medutim, postoje situacijeu kojima meta navigacije moze biti kontrola koja sadrzi listu objekata. Tada, mozese javiti potreba za aktiviranjem view objekta, koji je vec ubacen na kontrolu, iil zadodavanjem novog view objekta na specifican nacin.

• Potrebno je, takode, definisati nacin identifikacije view objekata na koje je potrebnoizvrsiti navigaciju. Na primer, u web aplikaciji stranica na koju je potrebno izvrsitinavigaciju cesto se identifikuje preko URI linka. U klijentskoj aplikaciji, view objekatmoze biti identifikovan po imenu tipa, lokaciji, ili na drugi nacin. Dalje, u kompozitnojaplikaciji, koja se sastoji od slabo povezanih modula, view objekti se cesto definisu urazlicitim modulima. Individualni view objekti moraju biti identifikovani tako da sene stvara direktna zavisnost izmedu modula.

• Nakon identifikovanja view objekta, proces instanciranja novog view objekta mora bitipazljivo koordinisan. Ovo moze biti narocito vazno kada se koristi MVVM patern.

• MVVM patern pruza cisto razdvajanje izmedu korisnickog interfejsa i njegove prezen-tacione i biznis logike. Medutim, navigaciono ponasanje aplikacije razapeto je i nakorisniski interfejs i prezentacionu logiku. Korisnik cesto inicira navigaciju iz kori-snickog interfejsa. Korisnicki interfejs se azurira kao rezultat navigacije, ali navigacijucesto inicijalizuju i koordinisu njome view model objekti. Mogucnost cistog razdvaja-nja navigacionog ponasanja kroz view i view model objekte vazan je aspekt, koji vredirazmatrati.

122

Page 124: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Prilikom navigacije cesto je potrebno proslediti parametre ili kontekst view objektuna koji se vrsi navigacija, kako bi mogao da se ispravno inicijalizuje. Na primer,ukoliko korisnik zeli da poseti stranicu za izmenu detalja odredenog klijenta, potrebnoje proslediti ID ili podatke klijenta kako bi view objekat mogao da prikaze potrebnepodatke.

• Veliki broj aplikacija ima pazljivo koordinisanu navigaciju kako bi se obezbedilo za-dovoljenje odredenih biznis pravila. Na primer, ukoliko postoje greske prilikom unosapodataka, korisnici mogu biti upitani pre nego sto izvrse navigaciju sa trenutnog viewobjekta, kako bi mogli da isprave invalidne podatke i uspesno kompletiraju operacijuizmene podataka. Takode, oni mogu biti upitani za potvrdu snimanja promena koje sunacinili nad objektom. Ovaj proces zahteva pazljivu koordinaciju izmedu prethodnogi sledeceg view objekta u navigaciji.

• Na kraju, najveci deo modernih aplikacija dozvoljava korisniku laku navigaciju nazadkroz istoriju. Slicno, mnoge aplikacije sadrze carobnjake gde korisnik prolazi krozrazlicite forme za unos podataka i gde korisnik moze da se krece napred/nazad kroznjih i u tom procesu dodaje i menja podatke pre kompletiranja zadatka. Ovakviscenariji zahtevaju neku vrstu istorizacionog mehanizma tako da redosled navigacijemoze biti biti sacuvan, ponovljen ili pre-definisan.

Svi ovi zadaci implementirani su uz pomoc mehanizma regiona Prism biblioteke. Unastavku rada opisujemo osnovnu funkcionalnost regiona i nacin na koji je navigacija imple-mentirana u aplikaciji.

10.3.3 Implementacija Regiona

Prism regioni su dizajnirani sa ciljem da podrze razvoj modularnih aplikacija dopustajucida korisnicki interfejs aplikacije bude konstruisan na slabo povezan nacin. Regioni dopustajuview objektima da budu definisani u modulima i budu prikazani na isti korisnicki interfejsaplikacije, bez zahteva da modul ima znanje o strukturi korisnickog interfejsa aplikacije.Oni dopustaju da promena rasporeda komponenti u aplikaciji bude postignuta na lak ibezbolan nacin omogucujuci dizajnerima da odaberu najbolji dizajn i raspored komponenatau aplikaciji, bez zahteva za promenama u samim modulima.

Prism regioni su u sustini imenovani drzaci prostora na kojima se prikazuju view objekti.Bilo koja kontejner kontrola u aplikaciji moze biti deklarisana kao region, to se postizejednostavno dodavanjem prikljucnog propertija RegionName, kao sto je prikazano u sledecemprimeru:

Listing 91: Zadavanje regiona

<controls:TransitioningContentControlregions:RegionManager.RegionName="MainRegion" ... />

123

Page 125: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Za svaku kontrolu zadatu kao region Prism kreira Region objekat koji predstavlja prostorkoji ona kontrolise i RegionAdapter objekat koji upravlja rasporedom i aktivacijom viewobjekata unutar zadate kontrole. Postoje gotove implementacije klase RegionAdapter zanajkoriscenije kontrole ali moguce je i kreirati sopstvene adaptere. Pristup Region objektimau kodu aplikacije vrsi se kroz klasu RegionManager.

U velikom broju slucajeva, region kontrola je jednostavna kontrola, kao sto je to Content-Control, koja moze da prikaze jedan view objekat u jedinici vremena. U drugim slucajevima,Region kontrola moze prikazati vise view objekata u isto vreme, kao sto je TabControl iliListBox kontola.

Region adapter upravlja listom view objekata dodeljenih regionu. Jedan ili vise ovih viewobjekata prikazuju se u region na kontrolu, u skladu sa definisanom strategijom rasporedivanjakontrola unutar regiona. View objektima moze biti dodeljeno ime koje se moze koristiti zakasnije preuzimanje istih. Ukoliko postoji vise view objekata u regionu, region adapter jezaduzen za odredivanje aktivnog.

Kao sto je vec ranije napomenuto, view objekti se dodaju u region na dva nacina. Prvi jeubrizgavanje view objekata (View Injection), dopusta da se view objekti programski dodajuu region. Ovaj pristup je koristan za dinamicki sadrzaj, gde se view objekti, koji se prikazujuna regionu, menjaju cesto prateci prezentacionu logiku.

Ubrizgavanje je podrzano kroz metod Add u klasi Region. Sledeci primer koda prikazujekako se moze preuzeti referenca na Region objekat preko klase RegionManager i programskidodati view u region.

Listing 92: Dodavanje view objekta u region

IRegionManager regionManager = ...;IRegion mainRegion = regionManager.Regions["MainRegion"];WProizvodi view = container.Resolve<WProizvodi>();mainRegion.Add(view);

Drugi metod, pod nazivom view discovery, dopusta modulu da registruje view tip zaime regiona. Kadgod se region sa zadatim imenom prikaze, instanca zadatog view tipa seautomatski kreira i prikazuje u regionu. Ovaj pristup je koristan za relativno staticki sadrzaj,gde view objekat treba da bude prikazan u regionu koji se ne menja. Ovaj metod je podrzankroz metodu RegisterViewWithRegion koja se nalazi u klasi RegionManager.

Listing 93: Registracija view objekta na region

var regionManager = ...;regionManager.RegisterViewWithRegion(GlobalRegions.CommandRegion, typeof (WCommands));

10.3.4 Osnovna navigacija u regionu

Oba prethodna metoda dodavanja view objekata u region mogu biti posmatrani kaoograniceni nacin navigacije. Regioni podrzavaju generalniji vid navigacije, baziran na URI

124

Page 126: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

identifikatore i prosiriv mehanizam navigacije.Navigacija u okviru regiona znaci da view objekat treba da bude prikazan u okviru re-

giona. View objekat (koji je potrebno prikazati) identifikuje se preko URI parametra, kojisadrzi naziv tog view objekta. Moguce je programski inicijalizovati navigaciju upotrebomRequestNavigate metoda definisanog u interfejsu INavigateAsync. Ovaj interfejs implemen-tira Region klasa, dozvoljavajuci inicijalizovanje navigacije u okviru tog regiona. Primer:

Listing 94: Navigacija na region

IRegionManager regionManager = ...;regionManager.RequestNavigate(GlobalRegions.MainRegion,

new Uri(CoreForms.WDobavljacSel, UriKind.Relative));

U navigacioni URI zadaje se naziv view objekta, koji treba biti kreiran i prikazan uregionu. Naziv se koristi za razresavanje tipa view objekta, kako bi se kreirala nova instancaupotrebom kontejnera.

Tokom navigacije, zadati view objekat instancira se upotrebom kontejnera zajedno saodgovarajucim view model objektom i ostalim komponentama. Nakon sto se view objekatinstancira, on se dodaje na zadati region.

10.3.5 Uloga view i view model objekta u navigaciji

Cesto view i view model objekti zele da ucestvuju u navigaciji. Prism biblioteka omogucujeovu funkcionalnost upotrebom INavigateAware interfejsa. Ovaj interfejs moguce je imple-mentirati na view ili (mnogo uobicajenije) na view model objekte. Implementiranjem ovoginterfejsa omogucava se da view/view model objekti ucestvuju u navigaciji.

Tokom navigacije, Prism proverava da li view klasa na koju se vrsi navigacija (ili njenDataContext za view model objekte) implementira interfejs INavigateAware. Ukoliko to jesteslucaj pozivaju se potrebne metode tokom navigacije. Interfejs INavigationAware definisesledece metode:

Listing 95: Interfejs INavigationAware

public interface INavigationAware{bool IsNavigationTarget(NavigationContext navigationContext);void OnNavigatedTo(NavigationContext navigationContext);void OnNavigatedFrom(NavigationContext navigationContext);

}

Metod IsNavigationTarget dopusta objektu da vrati status o tome da li moze da obraditrenutni zahtev za navigaciju. Ovo je korisno u situacijama gde se ista instanca view objektaponovno upotrebljava za razlicite podatke i scenarije. Na primer, view objekat moze da pri-kazuje informacije o korisnicima i moze se azurirati tako da prikazuje informacije za razlicitekorisnike. U projektu ova metoda uvek vraca pozitivan rezultat, kako se isti view objekatkoristi za prikazivanje razlicitih podataka.

125

Page 127: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 96: Implementacija metode IsNavigationTarget

public virtual bool IsNavigationTarget(NavigationContext navigationContext){

return true;}

Metod OnNavigatedTo poziva se kada je potrebno izvrsiti navigaciju na trenutni objekat.Ovaj metod dopusta objektu, na koji se vrsi navigacija, da se inicijalizuje. U primeru izaplikacije vidimo da se u ovom slucaju brisu komande iz komandnog panela.

Listing 97: Implementacija metode OnNavigatedTo

public override void OnNavigatedTo(NavigationContext navigationContext){

base.OnNavigatedTo(navigationContext);

CommandInjector.AddViewToRegion(_addCommandVm);CommandInjector.AddViewToRegion(_editCommandVm);CommandInjector.AddViewToRegion(_deleteCommandVm);CommandInjector.AddViewToRegion(_cvsCommandVm);

RefreshData();}

Metoda OnNavigatedFrom poziva se kada je potrebno izvrsiti navigaciju sa aktivnogobjekta na drugi. Ovaj metod omogucava trenutno aktivnom objektu da snimi svoje stanjeili da se pripremi za deaktiviranje ili brisanje iz korisnickog interfejsa. U aplikaciji ova metodaobicno se koristi za dodavanje komandi na komandni panel i za ucitavane podataka, kao stoje to prikazano u sledecem primeru iz klase BaseSelViewModel.

Listing 98: Implementacija metode OnNavigatedFrom

public override void OnNavigatedFrom(NavigationContext navigationContext){

base.OnNavigatedFrom(navigationContext);CommandInjector.RemoveViewFromRegion(_addCommandVm);CommandInjector.RemoveViewFromRegion(_editCommandVm);CommandInjector.RemoveViewFromRegion(_deleteCommandVm);CommandInjector.RemoveViewFromRegion(_cvsCommandVm);

}

10.3.6 Prosledivanje parametara tokom navigacije

Kako bi se implementiralo potrebno ponasanje aplikacije tokom navigacije, cesto je po-trebno proslediti dodatne podatke tokom navigacije. NavigationContext objekat pruza pri-stup navigacionom URI parametru. Parametru NavigationContext je moguce pristupiti usvim metodama iz interfejsa IsNavigationTarget.

Prism pruza UriQuery klasu kako bi olaksao zadavanje i preuzimanje navigacionih para-metera. Klasa se koristi za dodavanje parametara na URI u toku inicijalizacije navigacije i

126

Page 128: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

olaksava pristup tim parametrima u drugim metodama u toku navigacije. UriQuery klasaodrzava listu kljuc-vrednost parova za svaki parametar.

U sledecem primeru iz projekta, mozemo da vidimo kako se dodaju parametri u okvirumetode, koja vrsi navigaciju na view kalkulacija, kako bi se izvrsilo kalkulisanje fakture.

Listing 99: Navigacija upotrebom parametra

private void OnKalkulacijaCommand(){

...UriQuery query = new UriQuery();query.Add(GlobalConstants.NavCallerControl, CoreForms.WFakturaSel);query.Add(GlobalConstants.SelectedItem, ItemsCollection.CurrentItem.Id);Manager.RequestNavigate(GlobalRegions.MainRegion,

new Uri(CoreForms.WKalkulacijaEdt + query, UriKind.Relative));}

Parametri se preuzimaju iz propertija Parameters, koji se nalazi u objektu Navigation-Context. Ovaj properti vraca instancu UriQuery klase, koja pruza indeksni properti, kojidopusta lak pristup individualnim parametrima.

Sledeci primer demonstrira preuzimanje navigacionih parametara potencijalno prosledenihiz VMFakturaEdit objekta u view model klasi za kreiranje/izmenu detalja kalkulacije.

Listing 100: Preuzimanje parametra u navigaciji

public override async void OnNavigatedTo(NavigationContext navigationContext){

string fakturaIdStr = navigationContext.Parameters[GlobalConstants.SelectedItem];

if (fakturaIdStr != null){

long fakturaId = long.Parse(fakturaIdStr);var res = await LoadFaktura(fakturaId);if (!res)

InteractService.ShowShortError("Greska prilikom ucitavanja podataka","Desila se greska prilikom ucitavanja"+ " fakture za kalkulisanje: fakturaId:" + fakturaId);

base.OnNavigatedTo(navigationContext, false);}else{

base.OnNavigatedTo(navigationContext, true);}CommandInjector.AddViewToRegion(_procesiranjeKalkulacije);

}

Iz prethodnog primera mozemo da vidimo da se prvo pribavlja parametar SelectedItem(ukoliko je on zadat), a onda se vrsi inicijalizacija prikaza upotrebom ID-a fakture. Inace,poziva se bazna klasa za obradu navigacije (u tom slucaju, se iscitava ID kalkulacije i dobavljase objekat kalkulacije).

127

Page 129: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

10.4 Interakcija sa korisnikom

Cesto aplikacija treba da obavesti korisnika o nekom dogadaju ili pita za potvrdu prenego sto izvrsi odredene operacije. Ove interakcije cesto su kratka obracanja korisniku, di-zajnirane tako da jednostavno informisu korisnike o promeni u aplikaciji ili da se preuzmejednostavan odgovor od korisnika. Interakcije mogu biti modalne, u slucajevima kada prika-zujemo poruku korisniku gde ocekujemo odgovor od korisnika ili nemodalne, u slucaju kadase prikazuju informacije korisniku o promeni stanja u aplikaciji, ali se ne ocekuje obaveznoreakcija korisnika na obavestenje.

Postoji vise nacina na koji je moguce implementirati interakciju sa korisnikom, ali mozebiti izazovno implementirati je u MVVM aplikacijama, gde je potrebno ocuvati cisto odva-janje odgovornosti izmedu view i view model objekata. Na primer, u obicnoj aplikaciji kojane implementira MVVM patern, moguce je upotrebiti direktno poziv kontrole iz koda kojiimplementira prezentacionu logiku (code behind fajl u WPF aplikacijama) kako bi se dobiounos korisnika. U MVVM aplikaciji, ovo nije preprucljivo jer se krsi pravilo razdvajanjaodgovornosti izmedu view i view model objekata.

Ukoliko interakciju posmatramo iz ugla MVVM paterna, nalazimo da je view model obje-kat, odgovoran za iniciranje interakcije sa korisnikom, kako bi dobio odgovor potreban zadalje procesiranje, dok je view objekat zaduzen za prikaz i interakciju sa korisnikom upotre-bom odgovarajucih kontrola. Ocuvavanjem cistog razdvajanja odgovornosti izmedu prezen-tacione logike implementirane u view model klasi i korisnickog iskustva implementiranog uview klasi, pomaze unapredenju testabilnosti i fleksibilnosti aplikacije.

Postoje dva pristupa za implementiranje ove vrste interakcije sa korisnikom u MVVMpaternu. Jedan pristup je implementiranje servisa, koji se koristi u view model objektimakako bi inicirao komunikaciju sa korisnikom. Drugi pristup koristi dogadaje koji se inicirajuu view model objektima kako bi se izrazila zelja za komunikaciju sa korisnikom, zajedno sakomponentama u view objektima (koji su vezani za te dogadaje i koji upravlja vizuelnimdelom interakcije). U daljem tekstu, opisuje se samo prvi pristup, buduci da je samo onupotrebljen prilikom inicalizacije ove vrste komunikacije sa korisnikom.

10.4.1 Komunikacija sa korisnikom upotrebom interakcionog servisa

Kod ovog pristupa, view model objekat oslanja se na interakcioni servis kako bi iniciraointerakciju sa korisnikom. Ovaj pristup podrzava cisto razdvajanje odgovornisti i testiranjeview model objekata skrivanjem implementacije korisnickog interfejsa u servisu. Tipicno,view model objekat ima pokazivac na instancu interfejs servisa. Nakon sto view modelobjekat dobije referencu na interakcioni servis, moze programski da zahteva interakciju sakorisnikom kadgod je to potrebno. Sledi primer interakcionog servisa implementiranog uprojektu:

128

Page 130: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Listing 101: Interfejs servisa za interakciju

public interface IServInteraction{

void ShowNotificationInfo(string title, string message);void ShowNotificationWarning(string title, string message);void ShowNotificationError(string title, string message);void ShowConfirmation(ConfirmationMessage message, Action<ConfirmationMessage> callback);void ShowInfoDialog(DialogMessage message);void ShowWarningDialog(DialogMessage message);void ShowErrorDialog(DialogMessage message);void ShowDialog(ConfirmationDialog dialog);

}

Servis za interakciju podrzava vise tipova interakcije.

• Moguce je korisniku samo prikazati poruku na koju on treba da reaguje, tipa kada sedesi greska prilikom validacije ulaznih podataka ili prilikom procesiranja, i u tu svrhusluze metode Show*Dialog.

• Moguce je traziti potvrdu od korisnika za neku akciju, kao sto je cesto potrebna potvrdaod korisnika prilikom brisanja nekog elementa. To se postize pozivom metode Sho-wConfirmation koja ima dva parametra: prvi sadrzi podatke o poruci koju je potrebnoprikazati korisniku, dok drugi predstavlja akciju koju je potrebno pozvati/izvrsiti kadase dobije odgovor od korisnika.

• Pruza se mogucnost prikazivanja notifikacije korisniku na koju on ne mora da reaguje.To se postize pozivom neke od metoda ShowNotification*.

129

Page 131: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

• Moguce je prikazati proizvoljni dialog koji sadrzi dugmice OK/Cancel, pozivom metodeShowDialog sa parametrom ConfirmationDialog.

Ovaj servis se koristi u svim view model objektima kroz aplikaciju. Postoji dosta nacinaupotrebe ovog servisa kroz aplikaciju, a upotreba ovog servisa, uglavnom, je jednostavna.Sledi primer implementacije komande za brisanje stavke gde se servis poziva na dva mesta.

Listing 102: Implementacija komande za brisanje

protected virtual void OnDeleteCommand(){

if (_itemsCollection.CurrentItem != null){

var conf = new ConfirmationMessage();

conf.Title = "Potvrda za brisanje";conf.Message = "Da li ste sigurni da zelite da obrisete objekat sa Kljucem:" +

_itemsCollection.CurrentItem.Id;

InteractService.ShowConfirmation(conf, async message =>{

if (!message.IsConfirmed) return;var res = await DeleteItem(_itemsCollection.CurrentItem);if (res) RefreshData();

});}else{

InteractService.ShowNotificationInfo("Brisanje Stavke", "Nema selektovane stavke");}

}

Ukoliko nema selektovane stavke, onda se korisniku to i prikazuje u vidu notifikacije. Uko-liko postoji selektovana stavka, onda se pita korisnik za potvrdu brisanja. Ukoliko korisnikpotvrdi svoju akciju, nastavlja se sa brisanjem. Na kraju se osvezavaju podaci.

Navigacioni servis implementira Shell klasa, koja se nalazi u glavnom projektu. SadrziContentControl komponente u koje se zatim dinamicki dodaje sadrzaj pozivom servisa.

10.5 Implementacija korisnickog interfejsa u aplikaciji Prodavnica

Vecina stvari o implementaciji korisnickog interfejsa vec je recena u prethodnim pogla-vljima, uz primere iz aplikacije. Medutim, nedostaje opis nacina na koji je sve povezano ikako komponente saraduju zajedno.

10.5.1 Regioni

Kao sto je ranije napisano, Shell predstavlja pocetni i glavni view WPF aplikacije. Nje-gov zadatak je da drzi sav sadrzaj koji aplikacija prezentuje i da omoguci upravljanje timsadrzajem. U tu svrhu definisani su regioni.

130

Page 132: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Slika 24: Definisani regioni na Shell-u

Kao sto mozemo da vidimo na osnovu slike 24, definisana su dva regiona: jedan Ma-inRegion za smestanje glavnog sadrzaja aplikacije, i drugi CommandRegion za smestanjekomandi.

10.5.2 Navigacija

Navigacija aplikacije inicijalizuje se prilikom inicijalizacije aplikacije. U glavni regionaplikacije dodaje se instanca klase WNavigation, koja sadrzi kao view model implementacijuinterfejsa IServInjectNavItem, gde svi ostali moduli (kojima je potrebno pristupiti iz glavnenavigacije) registruju svoje view objekte. Registracija se vrsi u metodu Initialize klasemodula i obicno izgleda ovako:

Listing 103: Registrovanje view objekta na region

IServInjectNavItem navigation = _container.Resolve<IServInjectNavItem>();UriQuery query = new UriQuery();var navitemSelDobavlaca = new NavItemDef{

ItemName = "Pretraga Dobavljaca",Command =

new DelegateCommand(() =>

_regionManager.RequestNavigate(ShellRegions.MainRegion,KalkulacijaViews.WDobavljacSel + query)),

BackgroundColor = Colors.DodgerBlue,ImageSource = ImageSourcePath.Dobavljaci

};navigation.RegisterViewForRegion(navitemSelDobavlaca);

Iz prethodnog koda mozemo da vidimo, da je prvi korak preuzimanje reference na obje-kat koji implementira interfejs IServInjectNavItem upotrebom kontejnera. Onda se kreirainstanca klase NavItemDef, gde se pored ostalih parametra nalazi i komanda, cija se akcija

131

Page 133: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

poziva kada korisnik zeli da izvrsi navigaciju. Implementacija komande sastoji se od jed-nostavnog poziva menazera regiona kome se upucuje zahtev za navigaciju na zeljeni view.Nakon inicijalizacije svih view objekata glavni navigacioni prozor izgleda kao na slici 25.

Slika 25: Glavni navigacioni view aplikacije

Kako je svaki modul inicijalizovao svoje view objekte, prikazane su sve stavke na glavnommeniju navigacije. Svaki modul registruje svoje view objekte koji se ukljucuju u glavnunavigaciju, ovo je izuzetno fleksibilno, jer omogucuje dodavanje novih elemenata navigacijeiz novog modula bez ikakve modifikacije koda za glavnu navigaciju. Takode, ukoliko semodul ne inicijalizuje, tj. zakomentarise se inicijalizacija nekog *.Presentation modula, necese desiti greska vec samo stavke tog modula nece biti prikazane.

Nakon sto se inicijalizuje navigacija, view model objekti upravljaju navigacijom u apli-kaciji na nacin kao sto je objasnjeno u prethodnim poglavljima.

132

Page 134: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

11 Zakljucak

Prilikom pisanja rada i izrade projekta, opisali smo vise tema, i sve su se bavile nacinomrazvoja odredenog dela aplikacije. Iako nisu svi koncepti u potpunosti opisani, to je bilodovoljno za razvoj jedne potpuno modularne aplikacije, sto nam je i bio krajnji cilj. S obziromna to da smo u radu izlozili osnovne koncepte razvoja modularnih aplikacija, smatramo dasmo ovim radom postavili dobru osnovu za dalja, tj. buduca istrazivanja.

DDD filozofija izrade aplikacije je, verovatno, najbolji nacin za implementaciju kom-pleksnih biznis problema, buduci da ona omogucava cisto razdvajanje koncepata u razliciteslojeve aplikacije i pruza nacin za izdvajanje biznis logike aplikacije u posebnu celinu. Cak,i kada neki projekat ne prati DDD, upotreba paterna i koncepata, koji definisu DDD, moguunaprediti aplikaciju.

Razdvajanje aplikacije na komandnu stranu i stranu upita olaksalo nam je, u velikoj meri,implementiranje, a uz to, ucinio je kod citljivijim. Kao sto smo videli iz rada, CQRS je malipatern koji ima veliki uticaj na arhitekturu i nacin izrade aplikacije. U startu potrebno je viserada prilikom izrade aplikacije, ali kako se vremenom uslozavaju biznis zahtevi, odrzavanjeaplikacije postaje lakse (u odnosu na aplikaciju koja nije razdvojena na komande i upite).

MVVM se pokazao kao dobar patern za implementiranje korisnickog interfejsa u kombi-naciji sa WPF tehnologijom, buduci da omogucava izradu korisnickog interfejsa uz pomocXAML koda (koji umnogome olaksava kreiranje korisnickog interfejsa) i omogucava cistorazdvajanje prezentacije od prezentacione logike (time omogucava da dizajneri i programerirade paralelno na istom projektu).

Najbitnija odlika aplikacije je to sto je modularna. Uprkos pocetnim poteskocama uimplementiranju modularne aplikacije, mozemo da zakljucimo da modularnost pruza mnogokoristi. Prism biblioteka veoma je koristan alat, koji pomaze prilikom izrade modularnihaplikacija. Nalazimo da je njena pogodnost u tome sto sadrzi implementaciju najbitnihpaterna potrebnih za izradu modularnih aplikacija.

Na kraju, nijedna arhitektura aplikacije nije savrsena arhitektura. Ona zavisi od visefaktora: problema koji se resava, zahteva koji su izlozeni, ljudi koji rade na projektu itd. Nijeprakticno implementirati sve slojeve za prostu aplikaciju, koja obavlja jednostavan zadatak,buduci da to zahteva dosta vremena, a dobit je mala. Sa druge strane, kod velikih aplikacija,sa kompleksnom biznis logikom i vecim brojem ljudi koji rade na razvoju aplikacije, pozeljnoje imati definisane slojeve i module kako bi timovi mogli da se specijalizuju za odredeni deoaplikacije i kako bi se kompleksna aplikacija razbila na manje delove. Sveukupno gledano,time bismo omogucili efikasan razvoj aplikacije.

133

Page 135: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Literatura

[1] Eric Evans, Domain-driven Design - Tackling Complexity in the Heart of Software,Addison Wesley, 2003.

[2] Martin Fowler, David Rice, Matthew Foemmel, Edward Hieatt, Robert Mee, RandyStafford, Patterns of Enterprise Application Architecture, Addison Wesley, 2002.

[3] Scott Millet, Nick Tune, Patterns, Principles, and Practices of Domain-Driven Design,John Wiley & Sons, Inc., Indianapolis, 2015.

[4] Dino Esposito, Andrea Saltarello, Microsoft .NET: Architecting Applications for theEnterprise, Second Edition, Microsoft Press Redmond, 2015.

[5] Vaughn Vernon, Implementing Domain-Driven Design, Addison-Wesley, 2013.

[6] Tim McCarthy, .NET Domain-Driven Design with C# Problem V Design V Solution,Wiley Publishing, Inc., Indianapolis, 2008.

[7] Jimmy Nilsson, Applying Domain-Driven Design and Patterns: With Examples in C#and .NET, Addison Wesley Professional, 2006.

[8] Adam Nathan, Daniel Lehenbauer, Windows Presentation Foundation UNLEASHED,Sams Publishing, Indianapolis, 2007.

[9] Mark Seemann, Dependency Injection in .NET, Manning Publications Co., ShelterIsland NY, 2012.

[10] Abel Avram, Domain-Driven Design Quickly, C4Media Inc., USA, 2006.

[11] Greg Young, Julian Dominguez, Grigori Melnik, Fernando Simonazzi, Mani Subra-manian, Dominic Betts, Exploring CQRS and Event Sourcing - A journey into highscalability, availability, and maintainability with Windows Azure, Microsoft, 2012.

[12] Greg Young, CQRS Documents, https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf, 2010.

[13] Martin Fowler, CQRS, http://martinfowler.com/bliki/CQRS.html, 2011.

[14] Microsoft patterns & practices, Developer’s Guide to Microsoft Prism - Building MVVMand Modular Applications with WPF and Silverlight, Microsoft Press, Redmond, 2011.

[15] Kirk Knoernschild, Java Application Arhitecture - Modularity Patterns with ExamplesUsing OSGi, Pearson Education, Inc, US, 2012.

[16] Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides, Design Patterns - Ele-ments of Reusable Object-Oriented Software, Addison-Wesley, US, 1995.

134

Page 136: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

[17] Paul Rayner, Aggregates & Entities in Domain-Driven Design, http://thepaulrayner.com/blog/aggregates-and-entities-in-domain-driven-design/, 2015.

[18] Jeffrey Palermo, The fallacy of the always-valid entity, http://jeffreypalermo.com/blog/the-fallacy-of-the-always-valid-entity/, 2009.

[19] Jimmy Bogart, Validation in a DDD world, https://lostechies.com/jimmybogard/2009/02/15/validation-in-a-ddd-world/, 2009.

[20] Halil Ibrahim Kalkan, Dependency Injection and Unit Of Work using Cas-tle Windsor and NHibernate, http://www.codeproject.com/Articles/543810/Dependency-Injection-and-Unit-Of-Work-using-Castle#HowToOpenConn, 2014.

[21] Leszek Koc, Generic Repository Framework (Generic Unit ofWork), http://www.codeproject.com/Articles/766609/Generic-Repository-Framework-Generic-Unit-of-Work, 2014.

[22] Shiju Varghese, Commands, Command Handlers and Com-mand Dispatcher, https://weblogs.asp.net/shijuvarghese/cqrs-commands-command-handlers-and-command-dispatcher, 2011.

[23] Mateusz Stasch, CQRS - Simple architecture, https://www.future-processing.pl/blog/cqrs-simple-architecture/, 2015.

[24] Steven van Deursen, Meanwhile... on the command side of my architecture, https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=91, 2011.

[25] Steven van Deursen, Meanwhile... on the query side of my architecture, https://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92, 2011.

[26] Richard Miller, When Dependency Injection goes Wrong, http://richardmiller.co.uk/2011/05/19/when-dependency-injection-goes-wrong/, 2011.

[27] Jimmy Bogard, A better domain events pattern, https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/, 2014.

[28] Udi Dahan, , Domain Events Salvation, http://udidahan.com/2009/06/14/domain-events-salvation/, 2009.

[29] Jonas Gauffin, Repository pattern, done right, http://www.codeproject.com/Articles/526874/Repository-pattern-done-right, 2015.

[30] Sun Microsystems, The Benefits of Modular Programming, https://netbeans.org/project_downloads/usersguide/rcp-book-ch2.pdf, 2007.

135

Page 137: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Biografija

Marko Milenkovic je roden 6. maja 1988. godine u Leskovcu. Osnovnu skolu “SinisaJanic” u Vlasotincu zavrsio je 2003. godine. Tehnicku skolu “Milentije Popovic” u CrnojTravi (smer ekonomski tehnicar) zavrsio je 2007. godine, gde je pokazao veliko interesovanjeza informaticke nauke.

Skolske 2007/2008. godine upisao je osnovne studije informatike na Departmanu zainformatiku, Prirodno-matematickog fakulteta u Nisu, koje je zavrsio skolske 2010/2011.godine sa prosecnom ocenom 8,32. Skolske 2011/2012. godine upisao je master akademskestudije na Departmanu za informatiku, Prirodno-matematickog fakulteta u Nisu, studijskogprograma informatike. Master akademske studije zavrsio je skolske 2015/2016. godine saprosecnom ocenom 9,63.

136

Page 138: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Прилог 5/1

ПРИРОДНO - MАТЕМАТИЧКИ ФАКУЛТЕТ

НИШ

КЉУЧНА ДОКУМЕНТАЦИЈСКА ИНФОРМАЦИЈА

Редни број, РБР:

Идентификациони број, ИБР:

Тип документације, ТД: монографска

Тип записа, ТЗ: текстуални / графички

Врста рада, ВР: мастер рад

Аутор, АУ: Марко Миленковић

Ментор, МН: Марко Петковић

Наслов рада, НР: RAZVOJ МОДУЛАРНИХ АПЛИКАЦИЈА У .NET

ОКРУЖЕЊУ

Језик публикације, ЈП: српски

Језик извода, ЈИ: енглески

Земља публиковања, ЗП: Р. Србија

Уже географско подручје, УГП: Р. Србија

Година, ГО: 2016.

Издавач, ИЗ: ауторски репринт

Место и адреса, МА: Ниш, Вишеградска 33.

Физички опис рада, ФО: (поглавља/страна/ цитата/табела/слика/графика/прилога)

136 стр. ; граф. прикази

Научна област, НО: Рачунарске науке

Научна дисциплина, НД: Софтверско инжињерство

Предметна одредница/Кључне речи, ПО: DDD, CQRS, MVVM, Prism, Unity, модуларне апликације

УДК 004.728 004.738.5

Чува се, ЧУ: библиотека

Важна напомена, ВН:

Page 139: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Q4.16.01 - Izdawe 1

Извод, ИЗ: У раду Razvoj модуларних апликација у .NET окружењу

описана је архитектура комплексних апликација кроз

израду практичног пројекта. Након увођења у проблем и

представљања захтева које апликација мора да задовољи,

представљени су основни концепти дизајна доменом

диригованих (DDD) апликација. У овом одељку описана је

архитектура DDD апликација представљањем слојева

апликација и њихове међусобне сарадње. Даље, описани су

патерни за моделирање домена, као и патерни за

управљање животним веком објеката у домену.

У раду је описан начин имплементације апликације

праћењем DDD-а и употребом CQRS патерна.

Представљене су базне класе свих основних објеката, и

пример њихове имплементације.

У наставку рада представљен је слој приступа извору

података. Описани су патерни који чине слој приступа

подацима апликације, њихова међусобна сарадња, као и

њихова имплементација. Како је апликација подељена на

два дела (командну страну и страну упита), описан је и

начин имплементације стране упита.

Након тога, описан је презентациони слој, и начин на који

је он имплементиран, у којем главну улогу игра MVVM

патерн.

Након дефинисања основних слојева, пажња је усмерена на

модуларност апликације, уз истицања предности

модуларних у односу на монолитне апликације. Том

приликом, описана је Prism библиотека и начин њене

употребе за израду модуларних апликација, кроз примере

из пројекта.

До краја овог рада описани су начини на које је апликација

подељена у модуле, могућности да се ти модули одрже

независним, затим кориснички интерфејс (уз објашњења

имплементације навигације и интеракције са корисником).

Датум прихватања теме, ДП:

06.10.2015.

Датум одбране, ДО:

Чланови комисије, КО: Председник:

Члан:

Члан, ментор:

Образац Q4.09.13 - Издање 1

Page 140: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Прилог 5/2

ПРИРОДНО - МАТЕМАТИЧКИ ФАКУЛТЕТ

НИШ

KEY WORDS DOCUMENTATION

Accession number, ANO:

Identification number, INO:

Document type, DT: monograph

Type of record, TR: textual / graphic

Contents code, CC: university degree thesis (master thesis)

Author, AU: Marko Milenković

Mentor, MN: Marko Petković

Title, TI: DEVELOPMENT OF MODULAR APPLICATIONS IN .NET ENVIRONMENT

Language of text, LT: Serbian

Language of abstract, LA: English

Country of publication, CP: Republic of Serbia

Locality of publication, LP: Serbia

Publication year, PY: 2016

Publisher, PB: author’s reprint

Publication place, PP: Niš, Višegradska 33.

Physical description, PD: (chapters/pages/ref./tables/pictures/graphs/appendixes)

136 p. ; graphic representations

Scientific field, SF: Computer science

Scientific discipline, SD: Software engineering

Subject/Key words, S/KW: DDD, CQRS, MVVM, Prism, Unity, modular applications

UC 004.728

004.738.5

Holding data, HD: library

Note, N:

Page 141: Razvoj modularnih aplikacija u .NET okru zenjuunarske... · Univerzitet u Ni su Prirodno matematicki fakultet Departman za ra cunarske nauke Master rad Razvoj modularnih aplikacija

Q4.16.01 - Izdawe 1

Abstract, AB: In the Master Thesis “Development of modular applications in .NET environment'' the architecture of complex applications has been described by working on a practical project. After the introduction of the problem and the presentation of the requests that need to be met by the application, the basic concepts of the domain driven design (DDD) application have been presented. In this section the architecture of the DDD applications has been described by showing the layers of the applications and their mutual cooperation. Moreover, design patterns for domain modelling have been described, as well as the patterns for the governing the lifespan of the objects in the domain.

In this thesis the way of implementing the application by following DDD and the usage of CQRS patterns has been depicted. Also, presented are the base classes of all basic objects, including the example of their implementation.

In the continuation of the thesis the layer of data access has been shown. The patterns which make up the data access layer of the application have been described, including their mutual cooperation and implementation. Considering the fact that the application has been divided into two sides (the command side and the query side), the way of implementing the query side has also been described.

In addition, the presentation layer has been shown as well as the way it was implemented, where the main role belongs to the MVVM pattern.

After defining the basic layers, attention has been directed towards the modularity of the application, with the emphasis on the advantages of the modular as compared to monolith applications. Here, Prism library was described, as well as the way in which it can be used to produce modular applications by using the examples from the project.

Furthermore, in this thesis there will be the descriptions of the ways in which the application has been divided into modules, the possibilities of these modules being maintained independent, the user interface (along with the explanations of the implementation of the navigation and interaction with the user).

Accepted by the Scientific Board on, ASB: 06.10.2015.

Defended on, DE:

Defended Board, DB: President:

Member:

Member, Mentor:

Образац Q4.09.13 - Издање 1