Dane są nieważne bo liczy się przede wszystkim mechanizm działania

Tym prze­wrot­nym tytu­łem chcę dziś zwró­cić uwa­gę na dwa waż­ne i bar­dzo pomoc­ne wzor­ce ana­li­tycz­ne (sło­wo ana­li­tycz­ne ode mnie, w książ­kach na temat wzor­ców bar­dzo rzad­ko uży­wa­na jest nazwa wzor­ce ana­li­tycz­ne”) opi­sa­ne w książ­ce M.Fowlera. Wzorcami ana­li­tycz­ny­mi nazy­wam te wzor­ce pro­jek­to­we, któ­re poma­ga­ją w ana­li­zie i pro­jek­to­wa­niu mode­lu dzie­dzi­ny sys­te­mu (biz­ne­so­wy model obiektowy).

MFowlerWzorceProjektowePolecam oczy­wi­ście lek­tu­rę całej książ­ki Martina Fowlera Architektura Systemów Zarządzania Przedsiębiorstwem Wzorce Projektowe.[1] Opisuje on w niej wła­snie wzor­ce pro­jek­to­we bar­dzo przy­dat­ne już na eta­pie pro­jek­to­wa­nia. Fowler nie uży­wa poję­cia wzor­ca ana­li­tycz­ne­go” ale książ­ka jest podzie­lo­na na obsza­ry zasto­so­wa­nia wzor­ców, wśród nich znaj­dzie­cie więc wzor­ce logi­ki dzie­dzi­ny, wzor­ce pre­zen­ta­cji inter­ne­to­wych czy omó­wio­ne tu wzor­ce dys­try­bu­cji a tak­że bar­dzo przy­dat­ne wzor­ce okre­ślo­ne jako pod­sta­wo­we”.

Poza wzor­ca­mi typo­wy­mi dla sys­te­mów obiek­to­wych w ogó­le, war­to zwró­cić uwa­gę na dwa. Opiszę krót­ko dwa bar­dzo przy­dat­ne na eta­pie ana­li­zy wzor­ce pro­jek­to­we (odno­śni­ki do stro­ny M.Fowlera): Transfer Object oraz Value Object.

Przykład użycia Value Object i Transfer Object

Na eta­pie ana­li­zy pro­ble­mu (ana­li­zy i mode­lo­wa­niu dzie­dzi­ny sys­te­mu) powin­no nas inte­re­so­wać przede wszyst­kim co i po co się dzie­je. Wielu ana­li­ty­ków o rodo­wo­dzie pro­gra­mi­stycz­nym zaczy­na pra­cę od pytań w rodza­ju ile nazwi­sko ma zna­ków, czy mogą to być tyl­ko lite­ry czy coś jesz­cze”, itd. Jest to moim zda­niem kom­plet­ne nie­po­ro­zu­mie­nie. Zabieranie się za opra­co­wa­nie mode­lu od tej stro­ny (mode­lo­wa­nie danych) cał­ko­wi­cie przy­ćmi roz­wią­zy­wa­ny pro­blem. Np. sta­ra­jąc się zro­zu­mieć sys­tem sprze­da­ży bile­tów na samo­lot istot­ne jest na począt­ku nie to, ile zna­ków może mieć nazwi­sko a to, że bar­dzo istot­na jest iden­ty­fi­ka­cja pasa­że­rów i miejsc jakie mają zająć w samo­lo­cie. To nie jest to samo, bo iden­ty­fi­ka­cja pasa­że­rów ma za cel kon­tro­lę, kogo wpusz­cza­my na pokład samo­lo­tu, a nazwi­sko to jed­na z cech pasa­że­ra i na eta­pie ana­li­zy nie nale­ży zakła­dać, że to jedy­ny i naj­lep­szy spo­sób na to roz­róż­nie­nie. Brzmi jak here­zja? Zapewne, bo więk­szość” ana­li­ty­ków zaczy­na wła­śnie od bada­nia np. nazwiska.

A kie­dy zając się nazwi­skiem? Dopiero wte­dy gdy zro­zu­mie­my pro­blem i opra­cu­je­my sys­te­mo­we jego roz­wią­za­nie. Po pierw­sze dla­te­go, że na począt­ku nie mamy wie­dzy (za wcze­śnie na taką decy­zję) by usta­lić na jakich kon­kret­nie danych będzie opie­ra­ła się iden­ty­fi­ka­cja pasa­że­rów (a nuż poja­wi się [[bio­me­tria]]), po dru­gie nie­po­trzeb­nie skom­pli­ku­je­my doku­men­ta­cję zamu­la­jąc ją od same­go począt­ku dużą ilo­ścią zbęd­nych atry­bu­tów”.

Druga istot­na rzecz, to komu­ni­ka­cja. Wiemy, że fir­ma sprze­da­ją­ca bile­ty lot­ni­cze ope­ru­je róż­ny­mi dany­mi (lokal­ny model biz­ne­so­wy tej fir­my) na temat rezer­wo­wa­nych bile­tów. Wiemy, że te dane – o sprze­da­nych bile­tach – muszą być prze­ka­za­ne linii lot­ni­czej, ta zaś wcze­śniej musi jakoś prze­ka­zać infor­ma­cje o tym, na jakie loty i jakie bile­ty oferuje.

Co cie­ka­we, dosko­na­le to pasu­je to opi­sów wyko­ny­wa­nych przez zama­wia­ją­ce­go (albo jak kto woli user sto­ry). Normalny czło­wiek raczej powie nam, że zapi­su­je dane pasa­że­ra”, ale raczej nie powie nam, że reje­stru­je 25 zna­ków nazwi­ska, 20 zna­ków imie­nia i cza­sem 20 zna­ków dru­gie­go imie­nia.….”. Ten sam czło­wiek powie następ­nie, że prze­ka­zu­je dane o sprze­da­nych bile­tach do odpo­wied­nich linii lot­ni­czych a nie, że doko­nu­je trans­fe­ru kolek­cji danych zawierających.….”.

Analiza i projektowanie

Na bazie wywia­dów, doku­men­tów itp. sta­ra­my się zro­zu­mieć co jest gra­ne”, jak ten sys­tem funk­cjo­nu­je. Powstaje np. taki dia­gram. Tu zakła­dam już, że mode­lu­je­my sys­tem sprze­da­ży bile­tów, ale sys­tem ozna­cza wszyst­ko to, co bie­rze w tym udział” a nie kon­kret­ne oprogramowanie”:

Transfer biletów do linii lotniczej

Mamy model cze­goś co ma się wyda­rzyć. Na tym eta­pie kom­plet­nie nie ma sen­su zaj­mo­wa­nie się tym ile zna­ków ma nazwi­sko. To może się zmie­nić w toku ana­li­zy a nawet imple­men­ta­cji, ale nie powin­na ulec zmia­nie logi­ka tej operacji.

Jak już opa­nu­je­my logi­kę (zro­zu­mie­my co i jak ma dzia­łać i zapro­jek­tu­je­my jak to zre­ali­zo­wać) zabie­ra­my się za szcze­gó­ły. Model dzie­dzi­ny (frag­ment):

Model dziedziny

Jak widać, np. danePasażera jako zawar­tość to daneOsoby a nie pola i typy danych”. Czym są DaneOsoby znaj­dzie­my tu:

ValueObject

Zamiast pro­stych typów danych (np. zna­ko­we) sto­su­je­my obiekt jako typ danych. To zna­ko­mi­cie uła­twia póź­niej­sze roz­sze­rze­nia i zmia­ny (nie musi­my nic zmie­niać w mode­lu dzie­dzi­ny by np. dodać dru­gie imię do danych oso­by, mody­fi­ku­je­my w jed­nym miej­scu jedy­nie dekla­ra­cję kla­sy DaneOsoby).

Wywołanie ope­ra­cji podajDaneBiletu zwra­ca obiekt DaneBiletuLitniczego (lub agre­gat zawie­ra­ją­cy wszyst­kie bilety):

Transfer ObjectNie jest to ten sam obiekt co wcze­śniej, ValueObject to typ danych, Transfer Object to seria­li­za­cja, któ­rej celem jest jedy­nie prze­nie­sie­nie w moż­li­wie naj­prost­szy do odczy­ta­nia spo­sób okre­ślo­nych infor­ma­cji (oba te obiek­ty nie mają jed­nak toż­sa­mo­ści). Nie nale­ży tych wzor­ców mylić ani utoż­sa­miać, gdyż Value Object to typ danych” zaś Transfer Object to jedy­nie para­metr wywo­łań, Value Object powi­nien mieć ope­ra­cje sparw­dza­ja­ce jego popraw­ność (wali­da­cja), Transfer Object słu­ży wyłącz­nie do prze­ka­zy­wa­nia infor­ma­cji jako para­metr wywo­łań i odpo­wie­dzi (w pew­nym sen­sie defi­niu­je pro­to­kół wymia­ny danych).

Korzyści ze sto­so­wa­nia tych wzor­ców to mię­dzy innymi:

  1. szcze­gó­ły danych odkła­da­my na koniec pro­jek­tu co jest bez­piecz­ne (nie musi­my mody­fi­ko­wać pro­jek­tu w mia­rę postę­pu pozy­ski­wa­nia wie­dzy o szczegółach),
  2. zmia­na tych szcze­gó­łów nie spo­wo­du­je potrze­by zmia­ny szkie­le­tu mode­lu dziedziny,
  3. może­my pro­wa­dzić spo­koj­ną upo­rząd­ko­wa­ną ana­li­zę top-down” (od ogó­łu do szczegółu),
  4. może­my się umó­wić z deve­lo­pe­rem, że jako ana­li­ty­cy nie będzie­my wni­ka­li w szcze­gó­ły danych, nadal może­my ope­ro­wać kla­sa­mi ValueObject i TransferObject (kla­sy te będą w począt­ko­wej fazie pro­jek­tu bez atrybutów),
  5. mimo to może­my umie­ścić w kla­sach ValueObject warun­ki wali­da­cji (ope­ra­cje klas, któ­rych tu nie poka­zy­wa­łem) i do tego one mię­dzy inny­mi słu­żą (to się nazy­wa okre­śla­nie wyma­gań poprzez testy czy­li TDD),
  6. już na samym począt­ku może­my uzgod­nić postać danych wymie­nia­nych na inter­fej­sach (np. zdal­na komu­ni­ka­cja) i korzy­stać z tej umowy.

Tak zapro­jek­to­wa­ny sys­tem speł­nia tak­że jed­ną z klu­czo­wych zasad pro­jek­to­wa­nia obiek­to­we­go: sys­tem jest zamknię­ty na zmia­ny i otwar­ty na rozszerzenia”.

Na zakończenie

Często sły­szę, że to trud­ne i pra­co­chłon­ne (dodat­ko­we kla­sy w mode­lu), nie­ste­ty zbyt pro­sty pro­jekt potra­fi być kosz­tow­niej­szy w roz­bu­do­wie w porów­na­niu z pier­wot­nym wytwo­rze­niem, dla­te­go jak klient w ramach wyma­gań wpi­su­je (a wpi­su­je coraz czę­ściej): sys­tem ma umoż­li­wiać łatwe roz­sze­rze­nia funk­cjo­nal­no­ści, to nale­ży go tak pro­jek­to­wać, w prze­ciw­nym wypad­ku wyma­ga­nie to nie jest spełnione…

Druga uwa­ga: czę­sto sami klien­ci zabi­ja­ją swo­je pro­jek­ty żąda­jąc na samym począt­ku udo­ku­men­to­wa­nia wszyst­kich szcze­gó­łów jakie im do gło­wy przyj­dą nie potra­fiąc jed­no­cze­śnie opi­sać mecha­ni­zmu dzia­ła­nia ich orga­ni­za­cji (lub nowe­go pomy­słu). To nie­ste­ty czę­sto spo­ty­ka­ne zja­wi­sko, z któ­rym moim zda­niem nale­ży wal­czyć. Paradoksalnie zło­żo­ność sys­te­mów biz­ne­so­wych tkwi w mecha­ni­zmie ich funk­cjo­no­wa­nia a nie w danych, któ­re zbie­ra­ją (któ­rych nie raz jest po pro­stu za dużo…).

Dane to fak­ty jakie chce­my znać, te fak­ty są kon­se­kwen­cją dzia­ła­nia a nie odwrotnie. 

Na ryn­ku w Polsce są jesz­cze mię­dzy inny­mi książ­ki o wzorcach:

Moim zda­niem jed­nak nie przy­dat­ne ana­li­ty­kom. Pierwsza to wzor­ce tech­nicz­ne”, sto­so­wa­ne głów­nie z kom­po­nen­tach Controller i View (archi­tek­tu­ra MVC). Druga to w zasa­dzie doku­men­ta­cja [[J2EE]]. Tak więc obie raczej dla pro­gra­mi­stów i archi­tek­tów. Książkę Fowlera pole­cam ana­li­ty­kom i archi­tek­tom tak­że. Wszystkim pole­cam tak­że UML i Wzorce pro­jek­to­we Larmana.

(UWAGA! Pokazano pro­jekt poglą­do­wy, wyssa­ny z pal­ca, nie ujaw­ni­łem tre­ści żad­ne­go z moich real­nych projektów).

Footnotes
[1]M. Fowler, Architektura sys­te­mów zarzą­dza­nia przed­się­bior­stwem. Wzorce pro­jek­to­we [na:] ?helion​.pl?, http://​helion​.pl/​k​s​i​a​z​k​i​/​a​r​c​h​i​t​e​k​t​u​r​a​-​s​y​s​t​e​m​o​w​-​z​a​r​z​a​d​z​a​n​i​a​-​p​r​z​e​d​s​i​e​b​i​o​r​s​t​w​e​m​-​w​z​o​r​c​e​-​p​r​o​j​e​k​t​o​w​e​-​m​a​r​t​i​n​-​f​o​w​l​e​r​,​s​z​a​b​k​o​.​htm, udo­stęp­nio­no 16 lipiec 2017.
[2]H. SA, J2EE. Wzorce pro­jek­to­we. Wydanie 2 [na:] ?helion​.pl?, http://​helion​.pl/​k​s​i​a​z​k​i​/​j​2​e​e​-​w​z​o​r​c​e​-​p​r​o​j​e​k​t​o​w​e​-​w​y​d​a​n​i​e​-​2​-​d​e​e​p​a​k​-​a​l​u​r​-​j​o​h​n​-​c​r​u​p​i​-​d​a​n​-​m​a​l​k​s​,​j​2​e​e​w​2​.​htm, udo­stęp­nio­no 16 lipiec 2017.

Domain-Driven Design – nie metoda a styl.…

Na temat pro­jek­to­wa­nia, mode­lo­wa­nia, ana­li­zy itp. napi­sa­no wie­le. Wiele razy też czy­ta­łem i obser­wo­wa­łem jak psu­je się pro­jek­ty tak zwa­ną opty­ma­li­za­cją, któ­ra jest jed­nak nie raz niczym innym jak upra­sza­niem pro­wa­dzą­cym czę­sto do kata­stro­fy pod­czas przy­szłej rozbudowy.

Często spo­ty­kam się z teza­mi, że mode­le two­rzo­ne w sty­lu DDD ([[Domain Driven Design]], ang. Projektowanie Zorientowane na Dziedzinę sys­te­mu) są mało opty­mal­ne. Najczęściej przy­ta­cza­nym przy­kła­dem jest kry­ty­ka agre­ga­tów ([[wzo­rzec pro­jek­to­wy Agregat]], obiekt biz­ne­so­wy mode­lo­wa­ny jako obiekt głów­ny i jego kom­po­nen­ty) jako mało wydaj­nych kon­struk­cji w przy­pad­ku zapy­tań o fil­tro­wa­ne i agre­go­wa­ne rapor­ty. Jest to praw­da ale wzo­rzec agre­ga­tu słu­ży do zarzą­dza­nia tymi obiek­ta­mi a nie do two­rze­nia zaawan­so­wa­nych wyli­czeń z uży­ciem ich wybra­nych atry­bu­tów. Zgodnie z DDD raczej nale­ży wte­dy zapro­jek­to­wać osob­ną kla­sę (agre­gat) będą­cą utwo­rzo­nym na boku” sto­sem danych do ana­liz. Czy tak się robi? Oczywiście, w dużych sys­te­mach sta­wia się dodat­ko­wą hur­tow­nie danych, któ­rych archi­tek­tu­ra jest przy­sto­so­wa­na do takich wła­snie zapy­tań, w mniej­szych pro­jek­tu­je się spe­cjal­na tabli­cę danych do takich raportów.

Niestety spo­ty­kam się czę­sto z uprasz­cza­niem dobrych mode­li bo będzie się lepiej rapor­to­wa­ło”. Czy lepiej to się oka­że w prak­ty­ce zaś model zepsu­ty” (naj­czę­ściej opty­ma­li­za­cja pole­ga na uprasz­cza­niu kon­struk­cji agre­ga­tu) sta­je się póź­niej nie raz nie­moż­li­wy do rozbudowy.

Właśnie mam w ręku książ­kę [[Joshua Blocha Java Efektywne Programowanie]] (kupi­łem by poczy­tać co robią pro­gra­mi­ści a nie pro­gra­mo­wać ;)))) i tam jest roz­dział o opty­ma­li­za­cji, a w nim super sen­ten­cja: sta­raj się pisać pro­gra­my popraw­ne a nie szyb­kie, po szcze­gó­ły tej myśli odsy­łam do książ­ki… Jakakolwiek opty­ma­li­za­cja na eta­pie pro­jek­to­wa­nia jest przed­wcze­sna, dopie­ro po skom­pi­lo­wa­niu i testach nale­ży oce­nić czy pro­gram jest za wol­ny, bo może nie jest …

Czemu lubię i pole­cam styl DDD? Bo nawet jak nie zna­my przy­szłych zmian (nowych wyma­gań) to na pew­no pro­jekt będzie się dało roz­bu­do­wać zamiast zmie­niać. Dlaczego? Bo jeśli pro­jekt dobrze mode­lu­je” rze­czy­wi­stość to zna­czy, że jeśli tyl­ko coś zmie­ni się w tej rze­czy­wi­sto­ści będzie moż­li­we to, do takie­go same­go odwzo­ro­wa­nia w pro­jek­cie. (tu pole­cam tak­że arty­kuł ze stron devlab​.pl o tak zwa­nych ValueObject): Ważne jest tak­że prze­strze­ga­nie her­me­ty­za­cji, któ­ra tak­że mode­lu­je rze­czy­wi­stość. Innymi sło­wy jest tyl­ko jeden spo­sób by poznać lub zmie­nić zawar­tość mojej tecz­ki: popro­sić mnie”. Optymalizacja pole­ga­ją­ca na udo­stęp­nia­niu danych obiek­tów wprost, jeśli nie (o zgro­zo) bez­po­śred­nio z innych obiek­tów, to z pomo­cą ope­ra­cji get/set (bez­po­śred­nie pyta­nia o kon­kret­ne atry­bu­ty i ope­ra­cje na nich) mamy na tacy to co powin­no być chro­nio­ne. Skutki są takie, że być może nie­co upro­ści­li­śmy kod ale jego przy­szła roz­bu­do­wa i kon­ser­wa­cja raczej będzie kosz­ma­rem. Zapewne wie­lu z Was spo­tka­ło się z przy­pad­kiem, popra­wie­nia (uzu­peł­nie­nia) jakiejś funk­cjo­nal­no­ści powo­du­ją­cą lawi­nę błę­dów w innych. To jeden z typo­wych efek­tów łama­nia zasa­dy hermetyzacji.

Polecam arty­kuł z MSDN (cytat poni­żej), dodam że DDD to raczej styl ana­li­zy i pro­jek­to­wa­nia a nie meto­dy­ka bo nie ist­nie­je recep­ta na ana­li­zę i mode­lo­wa­nie. Trzeba zro­zu­mieć to co się mode­lu­je i odwzo­ro­wać w posta­ci mode­lu w imple­men­to­wa­nym opro­gra­mo­wa­niu. Porównanie z jaski­nią Platona jest doskonałe :):

Możecie przy­rów­nać [[teo­rie o Jaskini Platona]] do for­mu­ły mode­lo­wa­nia DDD. Wiele z tych zale­ceń poma­ga nam zbli­żyć się ide­al­ne­go mode­lu w ogó­le. Droga pro­wa­dzą­ca do two­rzo­ne­go kodu zale­ży od głów wie­lu eks­per­tów dzie­dzi­no­wych i pro­jek­tan­tów, spon­so­rów pro­jek­tu, wyma­gań bran­ży w któ­rej pra­cu­je­my. Dlatego ma ogrom­ny sens patrze­nie (razem) na pro­jek­to­wa­ny sys­tem (opro­gra­mo­wa­nie) jak na cie­nie odwzo­ro­wu­ją­ce rze­czy­wi­stość na ścia­nie jaski­ni przed jej miesz­kań­ca­mi. (źr. An Introduction To Domain-Driven Design.)

Na koniec mała uwa­ga dla zwo­len­ni­ków metod zorien­to­wa­nych na przy­pad­ki uży­cia. Projektowanie na bazie [[sesji JAD]] i kolek­cjo­no­wa­niu przy­pad­ków uży­cia bar­dzo czę­sto pro­wa­dzi do bar­dzo kosz­tow­nych ana­liz i opa­słych doku­men­tów, gdzie odkry­wa­ne potem w pro­to­ty­pach nowe wyma­ga­nia nisz­czą budżet i har­mo­no­gram pro­jek­tu, jeśli nie wyra­ża się na nie zgo­dy nisz­czą przy­dat­ność pro­duk­tu. Piękną meta­fo­rę przy­to­czył [[Martin Fowler]]:

AllTopics

Wyobraźmy sobie kogoś, kto chce napi­sać pro­gram symu­lu­ją­cy part­ne­ra do gry w sno­oke­ra. Problem ten może zostać opi­sa­ny przy­pad­ka­mi uży­cia opi­su­ją­cy­mi cechy tej gry sce­na­riu­szem: Gracz ude­rza bia­łą kulę, któ­ra prze­miesz­cza się z kon­kret­ną pręd­ko­ścią, ta po okre­ślo­nym cza­sie ude­rza czer­wo­ną kulę pod okre­ślo­nym kątem, ude­rzo­na czer­wo­na kula prze­miesz­cza się na pew­ną odle­głość w pew­nym kie­run­ku.” Możesz sfil­mo­wać set­ki tysię­cy takich ude­rzeń, zare­je­stro­wać (sce­na­riusz) para­me­try każ­de­go ude­rze­nia i jego skut­ki. Jednak tą meto­dą i tak nie stwo­rzysz nawet dość dobrej symu­la­cji. Aby napi­sać na praw­dę dobrą grę, powi­nie­neś raczej zro­zu­mieć pra­wa rzą­dzą­ce ruchem kul, ich zależ­ność od siły i kie­run­ku ude­rze­nia, kie­run­ku itp. Zrozumienie tych praw pozwo­li Ci znacz­nie łatwiej napi­sać dobre opro­gra­mo­wa­nie.” (źr. [[Analysis Patterns. Reusable Object Models, Martin Fowler, Addison-Wesley, 1997]], wtrą­ce­nia moje).

Powyższy cytat, moim zda­niem powi­nien sobie wziąć do ser­ca każ­dy dobry ana­li­tyk i pro­jek­tant. Powiem też, że dla mnie sam ten cytat wart był ceny książ­ki. To jest wła­śnie spo­sób myśle­nia, któ­ry gorą­co pole­cam i sam sto­su­ję. Z innej książ­ki: przed­mio­tem ana­li­zy jest to co ogól­ne, rze­mio­sło w szcze­gó­łach jako narzę­dzie pro­jek­to­wa­nia się nie sprawdza.

Na koniec pole­cam tak­że inny cie­ka­wy arty­kuł koń­czą­cy się słowami:

… gdy sytu­acja robi się zło­żo­na, dobrze jest zde­cy­do­wać się na podej­ście w sty­lu DDD, ponie­waż jest dużo bar­dziej czy­tel­niej­sze i lepiej odpo­wia­da ska­li skom­pli­ko­wa­nych i dużych apli­ka­cji. (źr. La Gal?re / Propaganda Kapitana Pazura ? Blog Archive ? O wali­da­cji słów kilka).

Programistom i nie tyl­ko, pole­cam cie­ka­wy arty­kuł Sławka Sobótki z SDJ na temat DDD.