tag:blogger.com,1999:blog-5197374494377847819.post6096930227115595067..comments2024-03-22T22:13:46.650+01:00Comments on Holistycznie o inżynierii oprogramowania: Domain Specific QuerySławek Sobótkahttp://www.blogger.com/profile/15082577671795313109noreply@blogger.comBlogger14125tag:blogger.com,1999:blog-5197374494377847819.post-29126906963845303502009-11-27T23:09:03.265+01:002009-11-27T23:09:03.265+01:00Witam ponownie, troch innych obowiązku nie pozwoli...Witam ponownie, troch innych obowiązku nie pozwoliło na odpisanie od zaraz ale do rzeczy.<br />Architektura przedstawia się mniej więcej tak iż funkcjonalności biznesowe są przykryte warstwą managerów które to są implementowane w ejb2 oraz ejb3, modele także zostały zmapowane z bazy przy wykorzystaniu ww. technologi. Generalnie jeśli chcę powiedzmy, żeby trzymać się przykładu z dokumentami, zmienić mu status na nowy, wywołuje managera dokumentów który zachowując transakcyjność wykonuje polecenie. Problem w tym iż ów manager, korzystając z implementacji DAO, zwraca mi także listę np. aktywnych dokumentów. Aby coś z tym zrobić uważam iż należy oddzielić funkcjonalność techniczną aplikacji - wyświetlenie listy dok. wyświetlenie szczegółów dok. itd od funkcjonalności biznesowej stąd pomysł na cqs. Pokusa pojawia się gdy np lista dok. odpowiada obecnie zamapowanym encjom i szybciej jest zwrócić encje nawet nadmiarowe niż szlifować od zera sql'a na dany use case, wówczas niejako naturalnie implementacją findera staje sie DAO dając przy okazji abstrakcję od źródła danych co moim zdaniem nie jest złym rozwiązanie. Ale co z zapytaniem o pojedyńczy rekord, czy skorzystać z DAO (findByPK) czy w finderze dodać metodę zwracająca jeden rekord na podstawie kryteriów. <br />Idąc dalej mają doczynienia z wykorzystaniem tych samych funkcjonalności przez zarówno aplikacje webowe jak i standalone czy w nich przykrywać oraz grupować wszystkie funkcjnalność z BusinessDelegach czy zwracać się bezpośrednie do managerów i finderów?Maciej Mhttps://www.blogger.com/profile/00793261234971531561noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-79097376172614481062009-11-18T00:59:27.019+01:002009-11-18T00:59:27.019+01:00Chcesz zwracać DTO zatem domyślam się, że doszliśc...Chcesz zwracać DTO zatem domyślam się, że doszliście do sytuacji gdzie encje zamapowane na bazę nie są odpowiednimi nośnikami danych do klienta (klient w sensie aplikacja webowa albo zdalna aplikacja).<br /><br />Teraz zwykle robi się tak, że jakaś warstwa servisów woła DAO i przepakowuje zwracane przez DAO Encje JPA na DTO. Sam tak robiłem i wiem, że tak się często robi ale nie ma to zbytnio sensu w wielu przypadkach.<br /><br />Nie ma sensu z uwagi na to, że pobieramy z bazy niepotrzebne (zamapowane) dane, przepakowujemy, a później zwracamy DTO, ktore odpowiadada potrzebom konkretnego Use Case.<br /><br />Jest to żmudne, ale zawsze ktoś to może zrobić metodami siłowymi. Natomiast problem pojawi się z wydajnością. Szczególnie gdy będzie polegać na lazy loadingu - wówczas "n+1 slect problem" murowany!<br /><br />Dlatego można zastanowić się nad klasami, które pobierają z bazy to co jest na prawdę potrzebne i zwracają wprost DTO.<br />Można bawić się z Hibernate i pobieraniem poszczególnych pól, można bawić się z konstrukcją SELECT NEW KONSTRUKTOR_KLASY()<br />ale przy jakiś super złożonych zapytaniach skończy się i tak na SQL lub lepiej procedurce składowanej. Abstrakcję od SQL można osiągnąć np dzięki iBatis.<br /><br />I teraz odpowiadając na pytanie wreszcie: jeżeli wywnioskujesz, że lepiej pytać wprost o potrzebne dane (bez przepakowania encji z ORM) to droga jest raczej jedna. Osobne klasy "finderów". Ponieważ niezbyt dobrze będzie wyglądać DAO, którego implementacja używa zarówno ORM jak i jakiegoś "wykonawcę" SQL (jbbc, ibatis). Wiem co mówię - robiłem takie koszmarki dawno temu;P <br /><br />Zastanawia mnie jeszcze jedna kwestia: piszesz, że chcecie pożenić DDD i zaszły system (w tym jego DAO). W DDD dostęp do danych odbywa się przez Repozytorium. Różnica nie jest jedynie z nazwy - co próbuje nam wmówić Spring;P<br />Różnica jest też koncepcyjna. <br /><br />Repo zwraca Agregaty oraz pozwala na ich modyfikację. Jeżeli Repo ma jakieś wyszukiwarki to tylko wynikające z reguł biznesowych w pewnych procesach. Wyszukiwarki potrzebne do różnego rodzaju gridów na gui nie powinny zaśmiecać Repo - stąd właśnie opisywany Findery (najlepiej jeszcze w osobnym stosie;P) <br /><br />Napisz może więcej na temat jak dokładnie widzisz taką architekturę. Domyślam się, że może to być tajemnica firmowa i nie koniecznie powinno pisać się o tym na forum publicznym. Jeżeli tak to pisz na mojego prywatnego maila.Sławek Sobótkahttps://www.blogger.com/profile/15082577671795313109noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-46390096129000530182009-11-17T23:31:34.274+01:002009-11-17T23:31:34.274+01:00Hej Sławku,
Dzięki za odpowiedź, interesuje mnie ...Hej Sławku, <br />Dzięki za odpowiedź, interesuje mnie może nie typowe, klasyczne podejście tylko pewna hybryda, próba pożenienia artefaktów DDD w systemie napisanym w klasyczny bez "" sposób. Implementacja drugie stosu na razie nie wchodzi w grę, wiec zostaje skorzystanie z obecnej trzódki klas. Decydujemy się na zwracanie przez Query jakiś DTO, czy w takim przypadku warto pisać kolejne klasy dostępu do bazy? Czy dodać wyszukanie do obiektów obecnie przykrywających bazę. Idąc pierwszym tropem tworzymy nowe, kolejne DAO, które nie za wiele będzie się różnić od istniejących, jeśli dodatkowo będziemy chcieli skorzystać z posiadanego ORM, idąc drugim tropem nie wiem czy nie za bardzo przeładujemy obecne implementacje DAO odpowiedzialnościami, w końcu chcemy podążać za SRP.<br />PzdrMaciej Mhttps://www.blogger.com/profile/00793261234971531561noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-87086754370302993742009-11-17T10:02:14.368+01:002009-11-17T10:02:14.368+01:00@Maciej
W "klasycznym" podejściu można ...@Maciej<br /><br />W "klasycznym" podejściu można sobie metodę szukającą przenieść do DAO. <br /><br />Ale zauważ, że treść posta jest osadzona w kontekście Command-query Separation. Oparłem się tutaj na koncepcji Grega Younga, który CqS implementuje jako system owszem warstwowy, ALE o architekturze 2 stosów warstw. <br />http://art-of-software.blogspot.com/2009/09/lazy-loading-w-kontekscie-paradygmatu.html<br /><br /><br />Jeden stos obsługujący Commandy jest mniej więcej "klasyczny". Z tym, że jeżeli używamy DDD to zamiast DAO mamy Repozytorium zwracające Encje DDD. Repo ma metody zwracające Encje po id oraz save, delete.<br /><br />Natomiast ten drugi stos - obsługujący Query jest już nieco bardziej strywializowany ponieważ zwykle jedyne co robi to przeszukuje dane. Dodam, że u Grega 2 stosy pracują z pewnych względów na 2 bazach, ale to temat na osobne rozważania.<br /><br />Teraz JEŻELI ten nasz drugi stos zwykle będzie zwracał dane "przekrojowe" (niemal raportowe) to nie ma sensu używać ORM ponieważ nie nadaje się on do takich zadań. <br />http://art-of-software.blogspot.com/2009/04/lazy-loading-sprawa-wydajnosci.html<br /><br />Co do babrania się z technikalami dostępu do dany to wystarczy zagregować obiekt będący "koniem roboczym" - jakiś DirtyExecutor, który się tym zajmie.<br /><br />Owszem opisane podejście jest nieco na wyrost i nie warto go stosować w małych i prostych systemach. Jednak opisałem go w ramach research aby nie pisać po raz tysięczny o tym samym;)<br /><br />Odpisałem "na szybko", gdybyś miał jakieś pytania albo potrzebował wyjaśnienia jakiejś koncepcji to pisz śmiało - wieczorem odpiszę.Sławek Sobótkahttps://www.blogger.com/profile/15082577671795313109noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-55647833093655043272009-11-17T00:47:41.758+01:002009-11-17T00:47:41.758+01:00Sławku, chyba ja czegoś nie widzę ale Twój Documen...Sławku, chyba ja czegoś nie widzę ale Twój DocumentsFinder sprowadzi się do implementacji DAO, z metodą search(), budującą zapytanie na podstawie kryteriów. Najlepiej z tylko tą metodą :)<br />Czy mając DB, przykryte DAO i zaimplementowane powiedzmy przy użyciu jakiegoś ORM naprawdę chcemy babrać się w gąszczu try/catch java.sql API? - poza paroma szczególnymi przypadkami w systemie gdzie wydajność na pierwszeństwo.<br />Skoro zbudowaliśmy sobie klasy z których repozytorium złoży nam nasze Encje(takie przez E) to czemu ich nie wykorzystać?<br />Jeśli miała by to być osobna klasa obok DAO, to jakbyś to widział?<br /><br />Pomysł z zadaniem pytania/wymagan przy pomocy API zgodnie z FI very nice, dobrze obrazuje o co w tym chodzi.<br />PozdrawiamMaciej Mhttps://www.blogger.com/profile/00793261234971531561noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-38672216980675755132009-10-16T19:56:20.985+02:002009-10-16T19:56:20.985+02:00Dzieki za zwrocenie uwagi Irek - przeoczylem to. M...Dzieki za zwrocenie uwagi Irek - przeoczylem to. Myslalem, ze znacznik pre sie tym zajmie i juz nie sprawdzalem.Sławek Sobótkahttps://www.blogger.com/profile/15082577671795313109noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-71064590045194054392009-10-16T10:20:03.758+02:002009-10-16T10:20:03.758+02:00Teraz dobrze:
A tak z innej beczki: w przykładach ...Teraz dobrze:<br />A tak z innej beczki: w przykładach kodu Syntax Highlighter ci porobił cuda z < i >.<br />Trzeba po prostu < pozamieniać na &lt; a > na &gt;Irek Matysiewiczhttps://www.blogger.com/profile/02786161827081997066noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-91641435067115817932009-10-16T10:19:09.471+02:002009-10-16T10:19:09.471+02:00Kurcze jeszcze raz:
A tak z innej beczki: w przy...Kurcze jeszcze raz: <br /><br />A tak z innej beczki: w przykładach kodu Syntax Highlighter ci porobił cuda z < i >.<br />Trzeba po prostu < pozamieniać na &< a > na &>Irek Matysiewiczhttps://www.blogger.com/profile/02786161827081997066noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-82749353178730126862009-10-16T10:18:10.698+02:002009-10-16T10:18:10.698+02:00A tak z innej beczki: w przykładach kodu Syntax Hi...A tak z innej beczki: w przykładach kodu Syntax Highlighter ci porobił cuda z < i >. <br />Trzeba po prostu < pozamieniać na < a > na >Irek Matysiewiczhttps://www.blogger.com/profile/02786161827081997066noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-68702675249902976712009-10-07T12:50:34.407+02:002009-10-07T12:50:34.407+02:00@Jarek: tak, można tak to ująć.
Klient "mówi&...@Jarek: tak, można tak to ująć.<br />Klient "mówi" czego chce i nie wnika co to znaczy w języku implementacji detali (w tym wypadku atrybutów).<br /><br />@Sławek Chmiel: trafiłeś w samo sendo! Być może z czasem "current" będzie znaczyło coś innego po stronie modelu (np nowy atrybut isHidden sie w nim pojawi, a current ustawi co go false).<br /><br />@Marek: adres - ehh bez komentarza... <br /><br />@Rafał: Jako przykład system, gdzie stawiamy na usability:<br />- na gui mamy jedno pole do wpisywana frazy (nie wazne gdzie ona występuje, po prostu ma być gdzies w dokumencie: tytul, sygnatura, tresc,...)<br />- opcja current: nawet moze byc domyslnie zawsze włączona w critera gdy wykonuje je zwykły user (nie admin)<br />- moze byc sobie na gui czekbos: "wygasające", co znaczy ze mają jakieś tam statusy i date wygsniecia w ciagu co najwyzej 7 dni.<br /><br />Natomiast szczegolowe atrybuty (nie przykryte FL) moga byc uzywane w innych miejscach systemu do okreslania szczegolow przeplywu czy uprawnien.Sławek Sobótkahttps://www.blogger.com/profile/15082577671795313109noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-53820222075313296592009-10-07T01:34:49.431+02:002009-10-07T01:34:49.431+02:00Ladne polaczenie builder i fluent interface. Mam p...Ladne polaczenie builder i fluent interface. Mam pare sugestii:<br />1. Laczenie wielu builderow ze soba moze jeszcze bardziej ulatwic zycie. Jako punkt wejscia uzywamy nadal metod statycznych (mozna uzyc import static zeby miec ladna skladnie ala DSL), np:<br /><br />wyszukaj(dokument()<br />.komentowanyPrzez(uzytkownika().oImieniu("bob"))<br />.komentowanyPrzez(uzytkownika().oReputacji(100)));<br /><br />wyszukaj(uzytkownika().oImieniu("alice"));<br /><br />zamiast interfejsu o 100 metodach, mamy 2 o 50, z ktorych kazdy moze byc uzyty jako wyszukiwany korzen, lub jako kryterium zagniezdzone w innym wyszukaniu.<br /><br />2. Co do budowania kryteriow to jest to bardzo ciekawe zastosowanie. Jednak wg mnie duzo lepiej niz tworzyc hierarchie dziedziczenia kryteriow (BaseCriteria, UserCriteria, DocumentCriteria, itd.) DUZO lepiej jest stworzyc jednen interfejs kryterium, wraz z jedna sluszna implementacja, czyli taka ktora wszystko przekazuje do hibernatowej Criteria :) oraz wiele sposob na budowanie jej.<br /><br />Hierarchia dziedziczenia kryteriow to ogolnie chory pomysl (nie widze mozliwosci uzycia np. polimorfizmu), ale podalem go bo sam bylem swiatkiem proby zaimplementowania go w komercyjnym projekcie...<br /><br />3. Pomim ze niezmiernie podoba mi ten pomsyl widze jednak problem. O ile uzycie takiego DSLa sprawdza sie super przyjemnie podczas pisania np. testow jednostkowych, nie widze za bardzo mozliwosci zastosowania go w samej aplikacji... Nie jestem pewien gdzie moznaby to kryterium budowac:<br />a) w warstwie aplikacji: parametry ktore przylecialy z GUI beda wcisniete zawsze w to samo miejsce w lancuchu wywolan ktory raczej nie bedzie sie zmienial. Tracimy wiec duza zalete tego rozwiazania czyli wyrazanie swojej intencji przez to ze wywolanie jakies metody w lancuchu pojawi sie lub nie... Jeszcze gorzej gdy bedzie to wygladalo tak:<br />if(queryZGui.maBycZnutkaDekadencji()){<br /> queryBuilder.zNutkaDekadencji();<br />}<br />b) budowane na GUI(chyba nie o to chodzilo?:)): podobnie jak wyzej. Builder bedzie musial byc gdzies zapamietany zanim nacisniemy ostatecznie submit i bedzie sie zmienial w zaleznosci od stanu przyciskow GUI (czyli cos podobnego jak przerazajacy przyklad powyzej).<br />c) warstwa aplikacji ktora nie bedzie mapowana 1:1 na baze, np.<br />Wierz stworzWierszSzekspirowski(){<br /> return FabrykaWierszy.sonet().bezRymow().oMilosci().oPolityce().oMoralnosci();<br />}<br />Wyglada w porzadku, ale wydaje mi sie ze uzycie bedzie jednak bardzo ograniczone...<br /><br />Z drugiej strony takie cos swietnie nadaje sie do np. budowania asercji do testow jednostkowych (zainteresowanych odsylam do biblioteki Hamcrest).<br /><br />Na koniec dodam, ze bardzo chcialbym uzyc takiego polaczenia buildera z FI w prawdziwym projekcie, wiec jezeli jest ktos kto jest w stanie wskazac mi jakis przyklad uzycia w prawdziwym projekcie bede wdzieczny.<br /><br />PozdrawiamRafał Jamrózhttps://www.blogger.com/profile/04142472085473198015noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-74498371515492075642009-10-07T00:53:25.439+02:002009-10-07T00:53:25.439+02:00Bardzo ładne zastosowanie enkapsulacji. Elementy k...Bardzo ładne zastosowanie enkapsulacji. Elementy które mogą się zmieniać są ukryte w kilku metodach składających się na swoisty język domeny. Nice<br /><br />@Sławek ta leniwość ("nie będę klepać takiego samego kodu po raz setny") programistów czasem pomaga stworzyć śliczne rozwiązania które wymagają mniej klepania ;-) Ale z drugiej strony są programiści leniwi (mam to w dupie, nie chce mi się czytać jakichś głupich książek, chce tylko żeby konto zostało zasilone) którzy sprawiają że reszta programistów płacze gdy zobaczy kod który spłodzili ;-) Tym drugim nie da się wytłumaczyć że zastanowienie się chwilę nad "domeną" przyniesie korzyści, gettery i settery znajdziemy w każdych klasach jakie tylko zostały stworzone w systemie - "no przecież zawsze mogą się przydać" ;-)<br /><br />Ostatnio podczas refaktoringu w pewnym projekcie (aplikacja webowa) znalazłem pole "address" w serwisie biznesowym - singletonie springowym ;-)Marek Dominiakhttps://www.blogger.com/profile/17842356173813763614noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-10988461511695607282009-10-06T23:45:13.890+02:002009-10-06T23:45:13.890+02:00Hmm... fajny pomysł, tylko wydaje mi się że zapomn...Hmm... fajny pomysł, tylko wydaje mi się że zapomniałeś wspomnieć o jednej z najważniejszych zalet jakie ze sobą niesie ;)<br />Każdy leniwy programista stwierdzi, że nie ma sensu tworzyć fluent interfejsu dla "prostej" klasy SearchDocumentsQuery - lepiej stworzyć tych kilkanaście pól i wygenerować settery i gettery :)<br />Dopiero z upływem czasu może okazać się, że to był błąd, dlaczego?<br />Załóżmy że SearchDocumentsQuery będzie tworzony i wypełniany (poprzez settery) przez wiele innych klas i każda z tych klas chce dostać bieżące dokumenty. Wszystko pięknie aż do pewnego dnia w którym okazuje się, że bieżące dokumenty mogą być w statusie MODIFY. Wtedy musimy przeglądać wszystkie settery dla każdej z klas która tworzących dany obiekt. Następnie sprawdzać czy przypadkiem w danym miejscu ktoś nie miał na myśli dokumentów bieżących ustawiając dane pole na daną wartość. Stosując twoje rozwiązanie cała logika odnośnie tworzenia zapytania znajduje się tam, gdzie powinna i przy opisanym problemie zmiana będzie tylko w jednej klasie! Czasami warto pomyśleć głębiej o programowaniu OO, GRASP czy SOLID a nie tworzyć proste data sety...<br />Mam nadzieje, że dobrze zrozumiałem twoje rozwiązanie ;)Sławek Chmielhttps://www.blogger.com/profile/16793151584038902431noreply@blogger.comtag:blogger.com,1999:blog-5197374494377847819.post-20334580515651832712009-10-06T23:09:43.899+02:002009-10-06T23:09:43.899+02:00Do mnie chyba dociera, czy to znaczy, że należy st...Do mnie chyba dociera, czy to znaczy, że należy stworzyć klasy(będące rodzajem interfejsu), których metody będą stanowiły niejako pewien "język" komunikacji z resztą implementacji?ToJaJarekhttps://www.blogger.com/profile/07492446399792666065noreply@blogger.com