FunnyBean / IntraWeb

0 stars 0 forks source link

Zjednotiť základné CRUD operácie #76

Open Burgyn opened 8 years ago

Burgyn commented 8 years ago

Problém: V súčasnosti máme RoomsController / EquipmentsController a UserControllers, ktoré majú zväčša rovnaké metódy a ich telá.

Chceme to zjednotiť do spoločnej generickej triedy, ktorá by tieto základné operácie vedela zastrešiť.

Navrhoval som, aby sa to neriešilo generickým predkom controllera, ale aby to bolo v samostatnej business triede. Jednotlivé controllery by len požiadavky preposielali na spomínanú triedu. (dôvodom je to, že controller je v podstate len vstupný bod pre request a nemala by sa v ňom riešiť priamo business logika)

Konkrétne riešenie tu môžeme rozdiskutovať, keď sa to začne riešiť.

soy-grep commented 8 years ago

Nie som si istý, či mojími myšlienkovými pochodmi vyriešim problém opakujúceho sa kódu v kontroleroch... ale myšlienka Kontrolera ako iba vstupný bod mi je blízka.

Takže v princípe by sme zaviedli medzičlánok - niečo medzi Controller a DataAccess. Niečo, čo by zhromažďovalo business logiku "modulov" ako Rooms, Equipments, Rezervácie... Tá sa z pohľadu editácie záznamov často opakuje.

Predpokladám že v budúcnosti budeme chcieť riešiť rôzne komplexnejšie operácie napr. nad Doménovými objektami Miestností - proces samotnej rezervácie môže zahŕňať žiadosť o uzamknutie samotnej miestnosti na čas rezervovania, overenie či na takúto rezerváciu má užívateľ vôbec právo, budeme riešiť uzamykanie záznamov (Pessimistic Concurrency). A nezdá sa mi v pohode, aby kontroler Rezervácií liezol priamo do Repository užívateľov a miestností. Také isté úkony nad užívateľmi a miestnosťami možno bude chcieť vykonávať aj iný kontroler, nebodaj nejaký Background Worker servra... preto sa tu pýta ten medzičlánok.

Z pohľadu Responsibility sa na Controller pozerám podobne ako to naznačil @Burgyn - vstupná brána do aplikácie. Controller by mal informácie z Requestu podsunúť správnym Business Objektom, tie ich spracujú a vrátia výstup. Tento výstup Controler sformátuje do Response. Na druhú stranu Repository je zodpovedné IBA za čítanie a zapisovanie dát. Tam určite nepatrí logika samotnej rezervácie abo nejaké prepočty.

Z pohľadu závislostí by teda fungoval vzťah: Controller -> Business Logika -> Data Access

Práve dvojica [Business Logika -> Data Access] z môjho pohľadu predstavuje niečo ako Modul... (Ten Data Access bude do Modulu injektovaný, takže je stále implementácia DAL nezávislá). Tento prvok by mal byť ideálne natoľko nezávislý, že by mu malo byť jedno, kto využíva jeho metódy - či WebApi servis s kontrolermi, alebo nejaký extra proces importujúci dáta z iného zdroja. To znamená, že v krabičke modulu by sme sa nemali hrať s pojmami Request/Response...

V princípe by sa tento prístup dal aplikovať aj do desktopovej MVVM aplikácie, kde by bol vzťah View -> ViewModel -> Business Logika -> Data Access... Ale tam môže Modul obsahovať aj prvky prezentačnej vrstvy z danej domény... Ale to len tak pomimo.

Toľko moje myšlienky, Neviem či to nie je "Overkill" pre našu pomerne jednoduchú aplikáciu... :stuck_out_tongue_closed_eyes:.

Burgyn commented 8 years ago

Veľmi pekne zhrnuté. Súhlasím v podstate so všetkým čo si napísal. Čo sa týka "Overkill" v rámci nášho projektu tak tu mám názor taký, že išli sme do tohto projektu aj preto aby sme sa čo najviac naučili. Príde mi to ako veľmi dobrá príležitosť naučiť sa, ako sa to dá spravit správne. Taktiež už teraz vidíme duplicitu kódu, takže riešiť to je potrebné.

soy-grep commented 8 years ago

Ako budeme súhrnne nazývať tie triedy, ktoré zastrešujú business logiku? Teda ten medzičlánok... Business Objekt, Modul, Servis?

Burgyn commented 8 years ago

Tak presne táto otázka mi vŕta v hlave veľmi dlho. Najčastejšie sa v poslednej dobe stretávam s tym, že to volajú service.

Milan Martiniak Dňa 7. 4. 2016 12:21 používateľ "Stanislav Gulis" notifications@github.com napísal:

Ako budeme súhrnne nazývať tie triedy, ktoré zastrešujú business logiku? Teda ten medzičlánok... Business Objekt, Modul, Servis?

— You are receiving this because you were mentioned. Reply to this email directly or view it on GitHub https://github.com/FunnyBean/IntraWeb/issues/76#issuecomment-206799801

soy-grep commented 8 years ago

Mhm... môže byť servis :smile:

Ak si teda v vytvoríme Servisy riešiace Miestnosti, Rezervácie, Užívateľov, tieto Servisy budú to, na čo sa budú odvolávať Controllery, s čím budú priamo pracovať...

A nad týmto sa vždy zamýšľam - ak bude Servis skrývať pred okolím svoje Repository (čo je podľa mňa správne), treba dopodrobna vyriešiť komunikáciu medzi Servismi. Logika v jednom servise často potrebuje informácie od iného servisu. Napr. Rezervácie môžu chcieť informáciu o konkrétnej Miestnosti, ktorú si vypýtajú od Servisu Miestností - nie z Repository miestností. Tzn. že Servis môže vo výsledku interfejsom vzdialene pripomínať Repository, ktoré ale má navyše nejaké špecifické metódy - napr. RezervujMiestnosť, ZrušRezervácie, OverDostupnosť... Ak si teda zavedieme IRezervácieServis a IMiestnostiServis (slovenské názvy sú len pre jednoduchosť), tieto interfejsy budú/môžu obsahovať CRUD operácie, ak to iné servisy/kontrollery takto potrebujú.

Na rozdiel od Repository by už ale navonok nemali vynášať Entity z DAL, ale by mali s Controllermi aj medzi sebou komunikovať pomocou nejakých Data Transfer Objektov (DTO). Prakticky aj ViewModel objekt ktorý používajú Controllery je DTO...

Takto sa mi to zdá správne, čo sa týka oddelenia jednotlivých vrstiev a testovateľnosti. Hm, ale ako toto všetko implementovať s minimom komplikovanej dedičnosti a generických predkov? :smile:

Btw. na túto tému sú parádne kurzy na Pluralsighte - kľúčové slovo Domain-Driven Design. Niektoré som kukal, ale mám v hlave stále veľa otáznikov...

Burgyn commented 8 years ago

Súhlasím s tým, že servisy by mali skrývať svoje repository a navonok komunikovať len cez DTO. Vo väčšine prípadov si myslím, že nám postačia ViewModel-y, ktoré už máme vytvorené. Viem si ale predstaviť, že pre používateľov nám vzniknú DTO objekty napríklad bez obrázkov, mena a hesla, .. bez vecí, ktoré nás nezaujímajú v prípade rezervácie miestností.

Čo sa týka komunikácie medzi servismi, tak tá by mala byť čisto na tom, že ak niektorý potrebuje niečo od iného tak ho dostane injectnutý cez konštruktor v podobe interfejsu. Samozrejme, že pre jeden servis napríklad správy miestnosti môže existovať viacero interfejsov. Pretože v našom obľúbenom príklade rezervácií miestností nás nezaujíma pridávanie, mazanie miestnosti, ... Taktiež pre jeden "modul" môže existovať viacero servisov.

Generický predok sa do tých základných servisov na jednoduché CRUD operácie žiada. Pretože väčšinu práce ohľadom validácie, samotného pridávania, editácie a mazania vie zabezpečiť predok. Ostatné veci už budú čisto na potomkov. Samozrejme, že nie každý servis musí dediť predka, ktorý bude vedieť CRUD operácie.

O Domain-Driven Design som toho tiež veľa čítal a stále vo veľa veciach nemám jasno :-(