Jesteś tutaj
Główna > Kreatywne IT > Umieszczanie logiki w aplikacji webowej – zrób to dobrze [serwisy w RoR]

Umieszczanie logiki w aplikacji webowej – zrób to dobrze [serwisy w RoR]

W ostatni poniedziałek (24.10) miałem przyjemność brać udział w Łódź User Ruby Group jako prelegent (już drugi raz, o pierwszym – czyli czemu nie warto pisać aplikacji bez testów – przeczytaj tutaj). Jeśli jesteś początkującym programistą webowym (lub programistką, of kors;-) ), to zapraszam do lektury. W sumie najlepiej, jeśli masz do czynienia z Ruby on Rails, ale nie jest to konieczne, ze względu na to, że część prezentacji miała wymiar „filozoficzny”.

6 dzielnica, Maro opowiada o tym gdzie trzymać logikę;-)
6 dzielnica, Maro opowiada o tym gdzie trzymać logikę;-)

Istotą wszystkiego i pytaniem, które spowodowało powstanie prezentacji był dylemat – gdzie w aplikacjach webowych umieszczać logikę? No to do dzieła!

(Prezentacja w PDF została umieszczona w materiałach, czyli tutaj).

Model-View-Controller (MVC)

Jeśli tworzysz już aplikacje webowe, to pewnie wiesz co to jest MVC. Jeśli tak rzeczywiście jest, śmiało ruszaj do punktu następnego. Jeśli jednak nie jest to dla Ciebie jasne – pozwól, że w prostych, żołnierskich słowach wyjaśnię „z czym to się je”.

Model View Controller – wzorzec architektoniczny, służący do organizowania struktury aplikacji posiadających graficzne interfejsy użytkownika

Tyle jeśli chodzi o oficjalną definicję. Jeśli chodzi o praktykę, w skrócie MVC to sposób dzielenia kodu, który tworzysz między 3 warstwy – Widok (czyli View – plik, który w efekcie daje Ci stronę, którą możesz oglądać) z jednej strony. Przeciwny biegun zajmuje Model – czyli pewna reprezentacja tabeli w bazie danych, same „bebechy” aplikacji (np. klasa, która reprezentuje tabelę – w niej określamy relację z innymi tabelami, czy walidacje na konkretne pola). Łączy je Kontroler (controller), który dokonuje niezbędnych operacji (jak na przykład znalezienie jakiegoś obiektu i przekazanie go do widoku).

MVC (Model-View-controller) przedstawione na prostym schemacie
MVC (Model-View-controller) przedstawione na prostym schemacie

Po co taki podział? Daje nam bardzo wiele korzyści. Przede wszystkim porządkuje, wprowadza wyższy poziom bezpieczeństwa, oraz sprawia, że w kodzie można się połapać (kiedy wiesz czego gdzie szukać, rozwijać aplikację jest dużo prościej).

Gdzie w MVC umieszczać logikę i dlaczego tego nie robić?

Podstawowe pytanie brzmi – w której z tych warstw (Modelu, Kontrolerze, czy Widoku) umieścić logikę?

  1. Widok (View) – mam nadzieję, że tutaj sprawa dla Ciebie jest jasna. Kod widoku powinien być możliwie pozbyty jakiejkolwiek logiki. Jedyne wyjątki to each (pętla, którą wywołujemy na kolekcji), ewentualnie instrukcja if. Pamiętaj jednak, że i tu każde użycie tego typu rzeczy powinno być uzasadnione. Nie stosujemy logiki w widoku, ponieważ ma to być przestrzeń tylko dla wizualizacji danych, tworząc go skupiamy się na tym, żeby aplikacja była możliwie ładna i przejrzysta, a nie żeby porządnie działała w sensie inżynierskim. Pozwala to też podzielić zadania w drużynie, oraz ułatwia zachowanie Prawa Demeter.
  2. Kontroler (Controller) – kiedy spytałem na prezentacji o to, gdzie powinna być umieszczona logika, jeden z słuchaczy zaproponował właśnie kontroler. Na pierwszy rzut oka wydaje się to być całkiem niezły pomysł – w końcu kontroler ma pośredniczyć, między danymi z modelu, a widokiem. Nie jest to jednak wszystko takie różowe – powinniśmy się starać, żeby akcje (metody) kontrolera były możliwie chude i przejrzyste. Jeśli trzeba zastosować tam jakąś logikę, to warto przemyśleć „wypchnięcie” jej do innego pliku. W przeciwnym wypadku kontroler po niedługim czasie robi się niebywale opasły, a jego akcje pełnią bardzo dużo funkcji, do których nie zostały przeznaczone. Z resztą – przez wiele miesięcy sam pisałem logikę w kontrolerach (na początku mojej pracy zawodowej), więc wiem co mówię. Nie rób tego. Korzystaj z moich doświadczeń. Nie rób tego.
  3. Model – tak, wreszcie dochodzimy do ostatniej warstwy aplikacji webowych – czyli do modelu. W najprostszym przypadku MVC to właśnie tutaj powinniśmy umieszczać większość logiki aplikacji.

Logika w modelu? Może jednak niekoniecznie…

Chciałbym, żebyś pomyślał nad rozwojem aplikacji, w której stosujesz logikę w modelu. Zasymulujmy jak może się rozwijać kod (na szczegóły implementacyjne nie zwracaj uwagi póki co).

Krok 1 – Jest dobrze!

Na początku model „z logiką” wygląda fajnie.

Model z jedną metodą wygląda dobrze
Model z jedną metodą wygląda dobrze
Krok 2 – Powiedzmy…

Po niedługim czasie, zaczynasz widzieć jak się rozrasta…

Model z kilkoma metodami powinien zapalić ostrzegawczą lampkę
Model z kilkoma metodami powinien zapalić ostrzegawczą lampkę
Krok 3 – WTF?

No tak, to jest ten moment, do którego niewątpliwie musisz dojść. A jeśli tak się stanie, wtedy albo przyjdzie Ci zrobić refactor kodu, albo brnąć dalej w takie coś. Obie opcje są niezbyt przyjemne.

Im dalej w las...

Tak więc – jak widzisz – trzymanie logiki całej aplikacji w modelach, to nie jest najlepszy pomysł. Na szczęście z odsieczą przychodzą…

Serwisy! Czyli jak sobie ułatwić życie

Jeśli jesteś początkującym programistą, to musisz wiedzieć, że serwisy to najzwyklejsze w świecie klasy. W Ruby on Rails umieszczamy je w katalogu app/services (za pierwszym razem musisz katalog services stworzyć). To tam wrzucamy większość logiki, odciążając w ten sposób model, oraz kontroler. Ot co, cały sens serwisów streszczony w kilku słowach. Spójrzmy więc i zapamiętajmy…

Serwis tworzymy, jeśli chcemy stworzyć jakąś część logiki, odciążając w ten sposób model i kontroler. Serwis umieszczamy w folderze app/services.

Oczywiście otwartą sprawą zostaje to jak technicznie tworzyć serwisy. Ja zaprezentuję tutaj dwa sposoby pisania ich, nie określam jednak który jest lepszy – wszystko zależy od Ciebie, twoich upodobań i konkretnej sytuacji.



Sposób pierwszy – „serwis jako zbiór metod”

Ta wspaniała nazwa jest oczywiście mojego autorstwa i nie radzę się do niej przywiązywać.

Ten sposób tworzenia serwisów jest bardzo prosty i intuicyjny, powiedziałbym nawet że bardzo naturalny. Swoisty wzór na niego przedstawiam poniżej.

Jak widać sprawa ma się bardzo prosto, dla nieobeznanych wyjaśnienia wymaga jedynie metoda initialize. To ona będzie wywołana w momencie, w którym tworzymy obiekt klasy SomethingService (taki konstruktor w Ruby). Wewnątrz można stworzyć zmienne z „@” – wtedy dostępne będą w całej klasie, we wszystkich metodach.

Poniżej przedstawiam użycie takiego serwisu w dowolnym miejscu w kodzie.

W pierwszej linijce stworzyłem obiekt ServiceName – wtedy automatycznie została wywołana metoda initialize. Następne linijki to wywołanie konkretnych metod.

Zwracam uwagę, że nazwa każdego z serwisów powinna się kończyć słówkiem „Service” (np. SkillService, StatsService itd), zaś nazwa pliku powinna kończyś się „_service” (np. skill_service, stats_service).

Sposób drugi – „serwis z pojedynczą odpowiedzialnością (single responsibility service)”

Nazywany potocznie „ten z call’em”. Różni się od pierwszego tym, że cały serwis pełni jedną funkcję. Jeśli nie jest to jeszcze jasne, spójrz na wzór na serwis z call’em.

Tutaj sprawa ma się tak, że tylko metoda call jest publiczna i tylko ją wywołujemy. Wszystkie inne metody są prywatne i służą jej, jeśli oczywiście zachodzi taka potrzeba. W przeciwieństwie do poprzedniego serwisu nie daje nam tak różnorakich możliwości, pozostaje za to bardziej klarowny i przejrzysty.

A oto jak wywołujemy serwis jak ten:

Prawda, że proste? No bardzo proste. Tutaj również zwracam uwagę na nazewnictwo – ponieważ tutaj serwis odpowiada za jedną rzecz, nie musi kończyć się dopiskami „service” – niemniej pozostawiam tą kwestię twojej wygodzie.

Przykład – tworzymy statystyki na stronę

Oczywiście do zrozumienia przyda się przykład. Posłużę się moją aplikacją, która wspiera usystematyzowane rozwijanie umiejętności. Sprawa jest niezwykle prosta – gdy zaczynamy pracować nad jakąś rzeczą (nazwaną w aplikacji Prefabem) – klikamy przycisk start. Kiedy kończymy – stop. W tym momencie tworzy nam się log_time. Chcemy wyciągnąć informacje o tym ile pracowaliśmy nad daną umiejętnością (prefab może realizować kilka umiejętności) w określonym okresie czasu, oraz z konkretnym wykresem dzień-czas. W efekcie będzie to wyglądało mniej więcej tak jak na screenie poniżej.

Warthog - statystyki umiejętności
Warthog – statystyki umiejętności

W tym celu musimy wykonać cała masę operacji (wyciągnąć logtime’y, policzyć z nich czas, przyporządkować do dni, policzyć czas ogólnie, zamienić czas na konkretną datę… i kilka innych). Do tego stworzymy serwis SkillsStatsService – to będzie „serwis jako zbiór metod”. Ponieważ jednak w kontrolerze potrzebujemy dosłownie kilku danych, do zwrócenia ich użyjemy SkillsDataSummaryService – serwis ten będzie typowym serwisem „z call”. Będzie wykorzystywał SkillsStatsService.

Dzięki połączeniu tych dwóch serwisów, bardzo ładnie możemy przekazać do kontrolera tylko niezbędne dane.

Stosuj serwisy i buduj dobre aplikacje!

To już wszystko, gdybyś miał (lub miała) jakiekolwiek pytania, to śmiało do mnie pisz (marek.czuma@gmail.com). Zachęcam do budowania swoich aplikacji – możliwie kreatywnych, które rozwiązują twoje konkretne problemy.

Powodzenia!


Ja nazywam się Marek Czuma, a to jest IT-Blog Wolnego Człowieka

Piszę do Ciebie Prosto z Łodzi


Jeśli uważasz, że artykuł był pomocny, lub po prostu lubisz być kreatywnym w IT – polub mój Fanpage na Facebook’u. Zapraszam do zostawienia maila – zero spamu, 100% dobrych treści.


Marek Czuma
Autor Bloga Republikańskiego. Chrześcijanin, Polak, Łodzianin. Wierzy w ludzi i ich możliwości, kocha pomagać innym. Uważa, że człowiek wolny kształtuje siebie poprzez własne wybory oraz pracę. Poza tym fan Wiedźmina i CD Projektu - zarówno na giełdzie, jak i w działaniu.

Dodaj komentarz

Top