Architektura… najpierw a nie na końcu

Szperałem w sie­ci szu­ka­jąc infor­ma­cji o archi­tek­tu­rze i micro­se­rvi­sach, a wpa­dłem na to:

… com­mu­ni­ca­tion betwe­en two dif­fe­rent softwa­re modu­les is pro­por­tio­nal to the com­mu­ni­ca­tion betwe­en the desi­gners and deve­lo­pers of the­se modu­les. Conway?s law expo­ses the vul­ne­ra­bi­li­ty in mono­li­ths as they grow in size. Micro-servi­ces may not be the best, but bet­ter than mono­li­ths for the con­tem­po­ra­ry web based appli­ca­tions. (Źródło: Micro-Services: A com­pa­ri­son with Monoliths | My Blog)

To pra­wo” wyja­śnia dla­cze­go powsta­ją złe inter­fej­sy a nawet zła archi­tek­tu­ra: z regu­ły dla­te­go, że to – archi­tek­tu­ra – jest lep­szym lub gor­szym kom­pro­mi­sem pomię­dzy zespo­ła­mi pro­gra­mi­stów, ich obcią­że­nia, krót­kich ter­mi­nów itp. Obserwuję to dość czę­sto w orga­ni­za­cji pra­cy developerów.

Jednak, żeby powsta­ła taka dobra archi­tek­tu­ra”, ktoś ponad zespo­łem deve­lo­pe­rów” powi­nien zapro­jek­to­wać archi­tek­tu­rę: kom­po­nen­ty z ich odpo­wie­dzial­no­ścia­mi i inter­fej­sy mię­dzy nimi.

Typowym pro­ble­mem jaki napo­ty­kam, nie tyl­ko w dużych pro­jek­tach dla budże­tów­ki”, jest archi­tek­tu­ra będą­ca efek­tem prze­py­cha­nek, poli­ty­ki, wal­ki o men­dej­sy”, czy­li o to któ­ry dostaw­ca (albo wewnętrz­ny zespół) dosta­nie więk­szy budżet. To naj­gor­sze i nie­ste­ty nie raz spo­ty­ka­ne meto­dy budo­wy architektury.

Jak to robić dobrze? Idąc za Fowlerem, pod­sta­wą budo­wy (usta­la­nia) gra­ni­cy odpo­wie­dzial­no­ści kom­po­nen­tu jest gra­ni­ca kon­tek­stu”. Można to przed­sta­wić tak:

Powyżej poka­za­no wynik ana­li­zy, mamy tu model poję­cio­wy. Widać, że pew­ne poję­cia (doce­lo­wo kla­sy, ich ope­ra­cje i atry­bu­ty) są dość gęsto powią­za­ne, inne luź­no. Granice pomię­dzy kom­po­nen­ta­mi powin­no budo­wać się tak, by sil­nie powią­za­ne ele­men­ty były w jed­nym kom­po­nen­cie (nigdy nie roz­dzie­laj rodzi­ny) a inter­fej­sy mię­dzy nimi były jak naj­prost­sze. Stosowanie DTO ozna­cza, że kom­po­nent powi­nien jed­no­ra­zo­wo zażą­dać całe­go agre­ga­tu danych (doku­men­tu) i same­mu sko­rzy­stać, nawet tyl­ko z czę­ści danych któ­re dostał, zamiast deta­licz­nie pro­sić o poszcze­gól­ne pola. Innymi sło­wy nie­za­leż­nie od tego czy w danym momen­cie potrze­bu­je­my tyl­ko war­to­ści kon­kret­nej fak­tu­ry, danych nabyw­cy czy fak­tycz­nie całej jej zawar­to­ści, zawsze żąda­my całej fak­tu­ry. Dzięki temu inter­fejs (API) jest prost­sze, licz­ba wywo­łań API jest zawsze mała. Powyższy przy­pa­dek zaowo­cu­je poniż­szym podzia­łem na komponenty:

ModelDziedzinyGraniceKontekstuKomponentu

Metoda ta i podej­ście (jako dobra prak­ty­ka), i u Fowlera (cyto­wa­ny DTO) i u Evansa (autor wzor­ca Domain Driven Design) nazy­wa­na jest boun­ded con­text” (gra­ni­ca kon­tek­stu, rodzi­na zawsze razem i tyl­ko jed­na rodzi­na w jed­nym miesz­ka­niu). Dzięki temu uzy­sku­je­my bar­dzo dobry, przy oka­zji zgod­ny z zasa­dą „[[Open Close Pryncypia]]”, model archi­tek­tu­ry, któ­ry pozwa­la zle­cić kom­po­nen­ty do wyko­na­nia, defi­niu­jąc wyłącz­nie opra­co­wa­ne już inter­fej­sy, kolej­ność ich (kom­po­nen­tów) wyko­na­nia nie ma zna­cze­nia dla całości.

Przy oka­zji ujaw­nia się zło” jakie wyrzą­dza budo­wa­nie zło­żo­ne­go opro­gra­mo­wa­nia na bazie inte­gra­cji poprzez współ­dzie­le­nie danych:

ModelDziedzinyIntegracjaPrzezWspoldzielnieDanych

Komponenty są sil­nie od sie­bie uza­leż­nio­ne (współ­dzie­lo­na baza danych w mode­lu rela­cyj­nym), nie da się two­rzyć (kodo­wać) tych kom­po­nen­tów nie­za­leż­nie od sie­bie, bo trze­ba uzgad­niać a potem uznać, wspól­ny model danych. Ingerencja w jaki­kol­wiek kom­po­nent z regu­ły ozna­cza inge­ren­cję w całą aplikację.

Dzieląc sys­tem na nie­za­leż­ne kom­po­nen­ty (każ­dy kom­po­nent sam zarzą­dza swo­imi dany­mi) , usta­la­my inte­gra­cję z uży­ciem inter­fej­sów a nie współ­dzie­ląc dane, z cze­go cał­ko­wi­cie rezygnujemy:

ModelDziedzinyWspolpracaKomponentowDziedzinowych


To jest powód dla któ­re­go, w dobrych fir­mach deve­lo­per­skich klu­czo­wą rolę peł­ni archi­tekt. Na pozio­mie logi­ki biz­ne­so­wej, w zasa­dzie jest nim – takim archi­tek­tem – ana­li­tyk biz­ne­so­wy-pro­jek­tant, któ­ry po zakoń­cze­niu eta­pu ana­li­zy i pro­jek­to­wa­nia, peł­ni rolę „[[pro­duct owne­ra]]”, pro­wa­dzą­ce­go nad­zór autor­ski i zarzą­dza­nie zmia­ną wymagań.

Tak więc archi­tek­tu­ra powin­na być efek­tem głę­bo­kiej ana­li­zy i pro­jek­to­wa­nia z testa­mi, dopie­ro potem powin­na się stać – być może każ­dy kom­po­nent osob­no – mate­ria­łem do zle­ce­nia dla deve­lo­pe­rów. Najgorszy wariant to podział na kom­po­nen­ty z pomo­cą nego­cja­cji, poli­ty­ki i wal­ki o budżet. 

Czas nie jest aktorem dla Systemu c.d. czyli projekt

i uda­ło się. Ciąg dal­szy tek­stu Czas nie jest akto­rem dla Systemu, zapra­szam do lektury.

Prosty przy­kład (na opi­sa­nie bom­by w sie­ci inter­net się nie odwa­ży­łem ;)). Produkt jaki ma powstać to syre­na sygna­ło­wa. Wymaganie funk­cjo­nal­ne: sys­tem powi­nien pozwa­lać na zapro­gra­mo­wa­nie godzi­ny i cza­su trwa­nia sygna­łu. Wymaganie poza-funk­cjo­nal­ne: dokład­ność zega­ra pro­gra­ma­to­ra ma być nie gor­sza niż błąd rzę­du sekun­dy w ska­li roku (:)).

Wersja pierwsza

Diagram pro­sty (wprost mode­lo­wa­ne wyma­ga­nia zama­wia­ją­ce­go) za to imple­men­ta­cja już nie. Diagram przy­pad­ków uży­cia, jego celem jest opi­sa­nie wyma­gań funk­cjo­nal­nych, czy­li tego jakie sys­tem ma ofe­ro­wać usłu­gi albo jak kto woli serwisy:

Diagram enig­ma­tycz­ny ale do tego słu­ży ten typ dia­gra­mu: akto­rzy i usłu­gi dla nich (tak zwa­ny korzeń pro­jek­tu). W tej posta­ci (pierw­sza ite­ra­cja ana­li­zy i pro­jek­to­wa­nia) otrzy­ma­my np. taki pro­jekt archi­tek­tu­ry (logi­ki) systemu:

Jak widać sys­tem skła­da się z dwóch pod­sta­wo­wych kom­po­nen­tów: Sterownika i Generatora Dźwięku czy­li syre­ny wła­ści­wej ;). Sterownik jest imple­men­to­wa­ny (znak Realizacji) z pomo­cą gene­ra­to­ra sygna­łu cza­su i pro­gra­ma­to­ra. Programator ma inter­fejs użyt­kow­ni­ka (GUI), zapro­gra­mo­wa­ny wysy­ła do Syreny sygna­ły Start i Stop zgod­nie z usta­wio­nym (zapro­gra­mo­wa­nym) cza­sem (har­mo­no­gra­mem).

Generator sygna­łu cza­su ma wyśru­bo­wa­ne wyma­ga­nia, więc jego wyko­na­nie będzie raczej kosztowne.

Wersja dru­ga

Jak widać pro­gra­mo­wa­nia dużo, koszt cało­ści poten­cjal­nie wyso­ki, więc kom­bi­nu­je­my dalej. Czy aby na pew­no wszyst­ko trze­ba robić same­mu? Pierwsza rzecz po wstęp­nym pro­jek­cie to upew­nie­nie się, czy nie ma cze­goś goto­we­go na ryn­ku. Jak nie trud­no (chy­ba) się domy­śleć, zegar­ków na ryn­ku dosta­tek, więc chy­ba nie trze­ba go na nowo odkry­wać. Mamy coś takie­go jak COTS ([[Commercial off the shelf]]) czy­li ogól­nie dostęp­ne na ryn­ku kom­po­nen­ty lub usłu­gi. No to zmia­na projektu:

Zegarek mam goto­wy, kupu­je­my Zegar lub, tu, uży­je­my wzor­co­we­go sygna­łu cza­su, np. takie­go jakie­go uży­wa typo­wa domo­wa pogo­dyn­ka z radio­wo ste­ro­wa­nym zega­rem (to dar­mo­wa usłu­ga). Teraz nasz dia­gram UC wyglą­da tak:

Tu akto­rem abso­lut­nie nie jest Czas, akto­rem jest inny sys­tem, tu źró­dło sygna­łu cza­su. Diagram UC jest zgod­ny ze stan­dar­dem a my zro­zu­mie­li­śmy isto­tę rze­czy”. Stosowanie w ana­li­zie stan­dar­dów pro­wa­dzi do rozu­mie­nia i ma taką wła­śnie zale­tę: ana­li­za i mode­lo­wa­nie pro­wa­dzi do zro­zu­mie­nia pro­ble­mu, łama­nie zasad nota­cji ukry­wa nie­zro­zu­mie­nie pro­ble­mu (o co cho­dzi z tym ocze­ki­wa­nym przez klien­ta czasem).

Wprowadziłem dwa ste­reo­ty­py: czło­wieksys­tem, cel chy­ba jest oczy­wi­sty (czło­wiek ma ekra­ny GUI, sys­tem jakiś inter­fejs” wymia­ny danych).

Rolą ana­li­ty­ka biz­ne­so­we­go jest zro­zu­mie­nie celu zama­wia­ją­ce­go ale też opra­co­wa­nie naj­ko­rzyst­niej­sze­go roz­wią­za­nia. Nie jest to wyrę­cza­nie pro­gra­mi­stów, a ana­li­za i pro­jek­to­wa­nie. Programiści (dostaw­ca opro­gra­mo­wa­nia) imple­men­tu­ją, roz­wią­zu­ją pro­ble­my techniczne.

Tak więc jedy­nym tak na praw­dę ele­men­tem (kom­po­nen­tem) wyma­ga­ją­cym szcze­gó­ło­we­go opra­co­wa­nia i imple­men­ta­cji jest Programator, war­to było pochy­lić się nad ana­li­zą i pro­jek­to­wa­niem, testy pomy­słu wyko­nać na mode­lach… bo taniej.

Pokazałem przy oka­zji tak zwa­ne zstę­pu­ją­ce podej­ście do ana­li­zy i pro­jek­to­wa­nia: od ogó­łu do szcze­gó­łu. Zanim zacznie­my pro­jek­to­wać model dzie­dzi­ny sys­te­mu war­to ogra­ni­czyć zakres pro­jek­tu do nie­zbęd­ne­go mini­mum (naro­bić to się każ­dy potra­fi .)). W sty­lu opar­tym na [[Domain Driven Design]] (DDD) nazy­wa się „[[core doma­in]]” i „[[boun­ded con­text]]” (o DDD innym razem).

Zapytany zosta­łem tak­że o testy akcen­ta­cyj­ne. Proste: nasta­wiam jakąś godzi­nę i czas wycia” i cze­kam :), żeby dłu­go nie cze­kać nasta­wiam taką by cze­kać kil­ka minut a nie godzin … upły­wu cza­su się nie testu­je (ten pły­nie i bez nas) , testu­je­my pro­gra­ma­tor… Owszem moż­na się umó­wić, że testu­je­my sygna­ły Start Stop inter­fej­su a nie syre­nę (będzie ciszej).

Podsumowanie

Jak widać trosz­kę się, mam nadzie­ję, upo­rząd­ko­wa­ło i mamy nastę­pu­ją­ce wnioski:

  1. czas nie jest żad­nym akto­rem, akto­rem jest bene­fi­cjent sys­te­mu, ktoś kto z nie­go korzy­sta lub z nim współ­pra­cu­je, para­me­try takie jak godzi­na alar­mu i czas jego trwa­nia, to nie aktor czas, a treść danych wpro­wa­dza­nych do sys­te­mu (np. for­mu­larz kon­fi­gu­ra­cji alarmu),
  2. opra­co­wa­nie samej spe­cy­fi­ka­cji wyma­gań funk­cjo­nal­nych abso­lut­nie nie deter­mi­nu­je pro­jek­tu, czy­li tego co nam pro­gra­mi­ści dostar­czą (a może to być kosz­tow­ne lub nie, ale to wyni­ka z pro­jek­tu a nie dia­gra­mu UC),
  3. jedy­nym spo­so­bem dokład­ne­go posta­wie­nia zada­nia pro­gra­mi­stom (deve­lo­pe­ro­wi) jest opra­co­wa­nie pro­jek­tu tego co ma powstać (jego logi­ki), jest to już nie spe­cy­fi­ka­cja wyma­gań funk­cjo­nal­nych a spe­cy­fi­ka­cja sys­te­mu (albo jak kto woli, wyma­ga­nie jest projektem),
  4. nie licz­cie na to, że dostaw­ca zawsze zro­bi wszyst­ko by to kosz­to­wa­ło jak naj­mniej… (patrz dru­ga wer­sja pro­jek­tu, jest tań­szy w wyko­na­niu – ryzyko),
  5. jeże­li ktoś umiesz­cza na dia­gra­mie UC akto­ra Czas, to naj­praw­do­po­dob­niej nie rozu­mie imple­men­ta­cji albo ukry­wa ją, lub odkła­da imple­men­ta­cję, to zna­czy, że odsu­wa jed­ną z naj­waż­niej­szych decy­zji pro­jek­to­wych (kosz­ty!) na później,
  6. zale­tą takie­go pro­jek­to­wa­nia (obiek­to­we ukie­run­ko­wa­ne na kom­po­nen­ty) jest moż­li­wość szyb­kie­go osią­ga­nia celu rela­tyw­nie niskim kosz­tem, w tym pro­jek­cie tak na praw­dę do napi­sa­nia jest sam pro­gra­ma­tor, resz­tę jako COTS kupu­je­my: goto­wą syre­nę, sygnał cza­su wyko­rzy­stu­je­my cudzy” (w tym przy­pad­ku to przy oka­zji kla­sycz­ny clo­ud computing).

Tak więc war­to na eta­pie ana­li­zy biz­ne­so­wej wyko­nać nie ste­no­gram z życzeń zama­wia­ją­ce­go (potra­fi sobie nawet sam zro­bić krzyw­dę) i nagi­nać zasa­dy (czas to nie aktor!), ale wyko­nać pro­jekt logi­ki sys­te­mu by zro­zu­mieć pro­blem do roz­wią­za­nia i spraw­dzić jak go roz­wią­zać naj­efek­tyw­niej. Wybór wyko­naw­cy powi­nien być ostat­nim krokiem.

Poniżej ana­li­za (dia­gram poka­zu­ją­cy powyż­sze niu­an­se ana­li­zy i mode­lo­wa­nia czy­li zro­zu­mie­nia co tak na praw­dę jest isto­ta problemu):

Wyobraźmy się, że wie­lu poten­cjal­nych deve­lo­pe­rów dosta­ło wyma­ga­nie: dostar­czyć pro­gra­mo­wa­na syre­nę (lub pierw­szy dia­gram UC) jako zapy­ta­nie ofer­to­we, jaki będzie roz­rzut ofert?

Do zama­wia­ją­cych opro­gra­mo­wa­nie: oddzie­laj­cie ana­li­zę i pro­jek­to­wa­nie od wyko­na­nia :), to ma same zale­ty. Zastanów się teraz Czytelniku, jakie roz­wią­za­nie zapro­po­nu­je więk­szość firm programistycznych…

Na koniec pyta­nie z gatun­ku iro­nii: jak na tym tle wyglą­da­ją zwin­ne meto­dy­ki wytwa­rza­nia oprogramowania?

P.S.

Polecam dia­gram przy­pad­ków uży­cia z innej stro­ny: Udziałowcy pro­jek­tu czy­li któ­ry dia­gram UML …,

oraz ten sam pro­blem, opi­sa­ny z innej per­spek­ty­wy na stro­nach IBM.