Andrzej Stasiak

Andrzej Stasiak Software Engineer

Temat: Architektura aplikacji w Spring MVC

Witam,

zakładam nowy temat, żeby jego nazwa dotyczyła zawartości (poprzedni był tu: http://www.goldenline.pl/forum/2703360/dostep-do-sesji.... Dzięki za wszystkie poprzednie rady, bardzo mi pomogły :-) Mam jednak kolejne pytanie:

Tworzę małą aplikację używając Spring MVC i Hibernate i mam wątpliwości odnośnie układu komponentów w niej. Pytanie dotyczy więc dobrych praktyk programistycznych raczej niż konkretnych problemów technicznych.

Stosuję pewien schemat, który przejąłem z tutoriala na którego bazie zacząłem się uczyć: Mam pewne klasy zawierające encje, które zapisuję w bazie danych. Do większości z nich przyporządkowane są odpowiednie klasy DAO (np. User ma swoje UserDAO itp.), jako @Repository, ponadto mam DataService (@Service), który na dobrą sprawę tylko deleguje żądania metod do konkretnych DAO (np. listUser() wywołuje tylko UserDAO.listUser()).

No i właśnie, nie rozumiem tego podziału na dwie warstwy (Service i DAO), skoro service tylko przekazuje wywołania dalej. Najchętniej wywaliłbym wszystkie DAO i bezpośrednio z DataService robił za pomocą Hibernate'a co chcę. Ale we wszystkich przykładach taki podział się pojawia, więc może jest w tym jakiś sens? Jeszcze jedna rzecz, która mnie niepokoi, że mam jedno DataService, które ma bardzo dużo metod, z drugiej strony, gdybym podzielił to na kilka mniejszych klas, to musiałbym w kontrolerach dbać o to, żeby do dobrego serwisu się odwoływać. Czy ktoś chciałby wyjaśnić mi pokrótce jak to się ładnie robi i czemu tak?

Dzięki, że chciało Ci się to wszystko przeczytać, i z góry dzięki, jeśli będzie Ci się chciało coś odpisać. ;-)
Łukasz D.

Łukasz D. Software Developer,
DevOps Fan

Temat: Architektura aplikacji w Spring MVC

Podział na Service i Dao służy do tego, aby lepiej obsługiwać bardziej zaawansowanej operacje, np. usunięcie z systemu faktury. Wymaga to najpierw usunięcia z niego informacji o rozliczeniu tej faktury, a potem samej faktury i np. przeliczeniu statystyk klienta. Dzięki rozbiciu na dwie warstwy, możemy zrobić jedną metodę serwisową np. removeInvoice(...), w której będzie odwoływać się do dwóch obiektów Dao: InvoiceDao i PaymentDao. W PaymentDao wywołujemy remove(invoice.getPayment()) i updateStats(invoice.getCustomer()), a następnie w InvoiceDao remove(invoice) a potem updateStats(invoice.getCustomer()). Dzięki takiej konstrukcji mamy wydzielony kod do usuwania płatności poza kod obsługujący dao obiektów Invoice, przez co nie musimy dublować tego kodu przy usuwaniu płatności w innym miejscu systemu.

Podział na takie warstwy pozwala też na objęcie metod serwisowych transakcjami, natomiast dao nie musi już nic wiedzieć o nich. Wtedy cała transakcja jest cofana, gdy którakolwiek z metod dao rzuci wyjątkiem.

Reasumując Dao reprezentuje stosunkowo proste operacje na encjach, natomiast metody serwisowe zawierają ciąg takich wywołań, często z dodatkową logiką biznesową objęte transakcją.

P.S. Do poczytania:
Najpierw http://blog.springsource.org/2011/01/04/green-beans-ge... a potem ważniejszy artykuł: http://blog.springsource.org/2011/01/07/green-beans-ge...Łukasz Dziedziul edytował(a) ten post dnia 19.12.11 o godzinie 15:39
Piotr Wierzbowski

Piotr Wierzbowski IT Architect, Asseco
Poland S.A.

Temat: Architektura aplikacji w Spring MVC

Ja przyjmuję takie założenie, że warstwa DAO ma w sobie logikę dostępu do danych, ewentualne mapowania na obiekty i operacje specyficzne dla tej warstwy np. w Twoim przypadku na obiekty Query, może miec wpisane jakieś HQLe, ewentualnie jakies logowanie, co sie dodało jakie id sie gdzies zapisało itp.

Warstwa serwisów z kolei jest wyższym poziomem abstrakcji, który zlepia operacje z niższy warstw logicznych (np. DAO) i zarządza transakcjami. Z reguły to właśnie operacje serwisów powinny startować i kończyć transakcje, DAO wprost nie powinny mieć korzystać z API transakcji (poza pesimistic/optymistic locking oraz deklaracją wymagalności transakcji).

Poziom wyżej (w kontrolerach - GUI, WS, itp.) powinny być same odwołania do warstwy serwisowej.

W prostej aplikacji rozdział na DAO i Servisy może sie wydawac bez sensu, ale sens może tu nadac własnie zarządzanie transakcjami. Obiekty DAO powinny jedynie obsługiwac dostęp do źródeł danych, serwisy z kolei nie powinny mieszać się do tego
Andrzej Stasiak

Andrzej Stasiak Software Engineer

Temat: Architektura aplikacji w Spring MVC

Dzięki, ale czy w porządku jest, gdy mam tylko jedną klasę DataService, do której metod odwołują się wszystkie kontrolery, a które agreguje funkcjonalności wszystkich DAO?

Generalnie budzi moją wątpliwość to, że DAO i kontrolery są u mnie tylko takimi workami do gromadzenia metod zajmujących się podobnymi rzeczami. W sumie każdą z metod DAO lub kontrolera można by teoretycznie przenieść do innego DAO/kontrolera i by działało, bo np. jeśli mam metodę, która dodaje obiekt do użytkownika, to ona mogłaby być zarówno w UżytkownikDAO, jak i ObiektDAO... a kontrolery to już wydaje mi się, że podział może być bardzo płynny.... czy tak właśnie powinno być, że podział na poszczególne kontrolery/DAO jest luźny i zasadniczo nic (poza czytelnością kodu) nie trzyma metod w konkretnej klasie?
Piotr Wierzbowski

Piotr Wierzbowski IT Architect, Asseco
Poland S.A.

Temat: Architektura aplikacji w Spring MVC

W zasadzie cięzko powiedzieć nie operując na konkretach, jeżeli DAO operują na kilku 2-3 powiązanych ze soba tabelach, to wtedy liczba klas serwisów może być nawet w stosunku 1 do 1.
Aby podział na klasy był dobry klasa powinna cechowac się wysoką spoistością i oferowac interfejs dotyczący wąskiej grupy operacji, byc może sam sobie narzuciłeś taki podział nazywając klase DataService - brzmi jak coś co obsługuje cały dostęp do danych. Zobacz na to: http://en.wikipedia.org/wiki/Cohesion_(computer_science)
Serwisy powinny agregowac grupe pewnych operacji, byc o poziom abstrakcji wyżej. Przykładowo masz serwis do zarządzania uprawnieniami użytkownika UserRightService może on opierac sie na operacjach z UserDAO, GroupDAO, RightsDAO, gdzie każdy z DAO operuje na swojej tabeli plus może jakas tabela słownikowa.

konto usunięte

Temat: Architektura aplikacji w Spring MVC

Podepnę się do tematu.

Czy przekazywanie requesta do metod w warstwie "service" jest dobrym rozwiązaniem?
Podejrzewam, że odpowiedź brzmi "nie", więc jakie potencjalne zagrożenia to niesie?

IMHO warstwy powinny być logicznie odrębne, jednak odziedziczyłem pewien projekt, gdzie "widnieje" takie rozwiązanie.Andrzej Cichoń edytował(a) ten post dnia 03.01.12 o godzinie 12:14
Łukasz Żuchowski

Łukasz Żuchowski Software Wizard

Temat: Architektura aplikacji w Spring MVC

Po pierwsze uzależniasz swoją warstwę logiki biznesowej od warstwy widoku/prezentacji.
Wady:
- nie możesz testować kodu bez mockowania requesta.
- w razie zmiany widoku (np. dołożysz interface swingowy, web-servicowy) będziesz miał problem chcąc ponownie użyć tej klasy z logiką biznesową [nie będziesz miał obiektu request].
- HttpRequest zamienia się w zmienną globalną do której (oraz do HttpSession bo jest w requeście metoda getSession) pakowane są różne rzeczy, a potem z niej wyciągane. To jest prosta droga do kodu który jest bardzo trudny lub wręcz niemożliwy w sensownym utrzymaniu.

Nie wspomnę już o pogwałceniu reguł SOLID i GRASP.

Rada: zobacz po co ten request jest tam pchany (idę o zakład, że jest wyciągany user, basket, accout etc.) wyciągnij to pobieranie do warstwy widoku (kontroler, akcja, komponent etc.) a potrzebny obiekt przekaż jako parametr do logiki biznesowej. Jeżeli coś pakujesz w metodzie biznesowej do requesta, to powinieneś zwracać ten obiekt i pakować go w warstwie prezentacji. Polecam zacząć refaktoring właśnie od tego, nie rozwiąże to wszystkiego ale pozwoli ci napisać test jednostkowe i pojechać z refaktoringiem dużo dalej.Łukasz Żuchowski edytował(a) ten post dnia 03.01.12 o godzinie 12:51
Łukasz D.

Łukasz D. Software Developer,
DevOps Fan

Temat: Architektura aplikacji w Spring MVC

Warstwa serwisowa powinna być odseparowana do warstwy kontrolera i widoku na wypadek gdybyś chciał użyć tej samej logiki w innym programie. Na przykład w programie konsolowym do generowania nocą raportów z całego roku. Jednocześnie możesz przez web generować takie raporty na żądanie z okresu jednego tygodnia.

Gdy wrzucisz obiekty request/response do logiki to masz problem i zaczynają się jakieś kombinacje z mockami, albo ręcznym tworzeniem obiektów request/response, ewentualnie bolesna refaktoryzacja.

Poza tym, obiekty request niosą ze sobą dane prosto od użytkownika, co wg mnie jest potencjalnym źródłem niebezpieczeństwa.

konto usunięte

Temat: Architektura aplikacji w Spring MVC

Łukasz Żuchowski:
Rada: zobacz po co ten request jest tam pchany (idę o zakład, że jest wyciągany user, basket, accout etc.) wyciągnij to pobieranie do warstwy widoku (kontroler, akcja, komponent etc.) a potrzebny obiekt przekaż jako parametr do logiki biznesowej. Jeżeli coś pakujesz w metodzie biznesowej do requesta, to powinieneś zwracać ten obiekt i pakować go w warstwie prezentacji. Polecam zacząć refaktoring właśnie od tego, nie rozwiąże to wszystkiego ale pozwoli ci napisać test jednostkowe i pojechać z refaktoringiem dużo dalej.

No właśnie to nie jest takie proste, cała logika w warstwie 'service' jest uzależniona od tego, co wypluwa request. Trzeba będzie wyciągać w kontrolerze całe listy, pojedyncze klasy etc. Refaktoryzacja tych obszarów w całym programie potrwa X dni, więc spytałem się o potencjalne zagrożenia :)
tak czy siak, dzięki wszystkim.
Mariusz S.

Mariusz S. Programista Java

Temat: Architektura aplikacji w Spring MVC

Skoro piszemy o spring MVC - to zapraszam do Łodzi 19.01 na 18.00 na JUG-a. Odbędzie się tam darmowa prezentacja, która może was zainteresuje.

https://sites.google.com/site/juglodz/opis_spotkan/1901...

Następna dyskusja:

JavaServer Faces (JSF) 2.* ...




Wyślij zaproszenie do