Hackathon Spring4 – podsumowanie

W ramach firmowego hackathonu w e-point zaprosiliśmy Jakuba Nabrdalika żeby zrobił nam code review naszej nowej aplikacji i poopowiadał o springu to czego sami dotychczas nie znaleźliśmy. Okazał się to strzał w dziesiątkę, bo poza samymi błędami koncepcyjnymi dowiedzieliśmy się wielu nowych rzeczy spoza springa: z projektowania aplikacji, Domain Driven Design i innych, o czym poniżej.

IMG_004_s

Architektura, wzorce projektowe, model bazy danych

Rozmawialiśmy o następujących aspektach:

  • Zmiana podejścia do deployowania aplikacji: zamiast budować repo WAR i wrzucać je do serwera aplikacyjnego, budujemy plik JAR, który zawiera serwer aplikacyjny. To nie pasuje do naszych procedur i gotowych narzędzi ale jest bardzo atrakcyjne przy małych projektach.
  • Uruchamianie zadań wsadowych: Jakub, podobnie jak my, miał już produkcyjne problemy związane z uruchamianiem takich zadań wymagających ręcznego sterowania. Stąd padła sugestia, żeby w przypadku użyciu Quartz’a umieścić go w osobnym pliku JAR/WAR, tak aby można było go łatwo wyłączyć.
  • Rozmawialiśmy o projektach modułowych i o tym, które moduły powinny odwoływać się do bazy danych. Jakub poddał pod wątpliwość nasz model, gdzie wszystkie moduły korzystają z jednej bazy sugerując że każdy moduł powinien mieć własną bazę, a komunikacja powinna się odbywać pomiędzy nimi bezpośrednio, np. przez API restowe.
  • Podejście do relacyjnej bazy w kontekście użycia Hibernate: podobnie jak ja, Jakub przedstawił opinię, że nie można projektować wyłącznie obiektowo, „udając” że pod spodem nie ma bazy danych, bo ona tam jest… Okazało się że jednak lubimy relacyjne bazy danych, bo łatwo się je projektuje, optymalizuje, lubią je także ludzie z biznesu, np. do raportowania. Jednak przy mniejszych projektach warto rozważać lżejsze rozwiązania, jak np. MongoDB.
  • Rozmawialiśmy o takim odzwierciedleniu bazy w modelu obiektowym na przestrzenie żeby zawierała przecięcia, tzn. niekoniecznie wszystkie referencje były trawersowalne, w szczególności żeby nie było cykli tylko relacje jednokierunkowe, dzięki czemu unikniemy „mesha”.
  • Jakub przedstawił nam sposoby na przekazywanie danych z obiektów domenowych do interfejsu użytkownika przy pomocy Data transfer object oraz „bokiem” tylko na potrzeby prezentacji zgodnie z wzorcem Command Query Responsibility Segregation. Takie podejście wydaje mi się bliskie temu co uprawiamy aktualnie, prezentując dane przy pomocy widoków z bazy.
  • Omawialiśmy także nasze podejście do „napełniania” bazy danymi testowymi. Pokazaliśmy naszego DBM-plugina wlewającego dane z plików CSV, który działa bardzo fajnie ale jest rozwiązaniem zamkniętym. Jakub zachęcił nas z kolei do zapoznania się z podobnym rozwiązaniem wbudowanym w Spring, tj. Repository Populator (używający Jackson), który jako źródła danych używa formatu JSON. Mi to rozwiązanie się mniej podoba, bo ciężej takie dane przygotować np. w Excelu. Mimo że znalazłem info o możliwości użycia tam formatu CSV, to wątpię aby była tam taka świetna obsługa typów tekstowych i binarnych jak u nas :-).
  • Jakub zachęcał też do zapoznania się z narzędziem LiquidBase zarządzania modelem bazy danych, w szczególności do nalewania danych a potrzeby testów.
  • Pokazał nam także usługę Spring Initialzr, dzięki której można w prosty „wizardowy” sposób wygenerować konfiguracje nowego projektu Spring, podobnie jak widzieliśmy to na prezentacji Spring Boot, gdzie prowadzący pokazywał podobną funkcję w InteliJ Idea.

Użycie Spring

W dalszej części prezentacji przedstawione zostały aspekty Springa które najprawdopodobniej znaliśmy zbyt słabo i wymagają poprawienia:

  • Typy obiektów wykorzystywanych w Dependency Injection: @Component, @Service (technicznie taki sam sens jak komponent tylko oznaczony dla wyróżnienia roli), @Repository i @Controller.
  • Zasięg transakcji: na cały request, kontroler, serwis lub repository (default).
  • Kwestia lazy-loading i różnice pomiędzy annotacjami fetch=LAZY i @LazyCollection.
  • Różnica pomiędzy Repository (dostęp do różnych obiektów z domeny) i DAO dostęp do krotek w bazie pojedynczego obiektu.
  • Konwencje nazw i struktury pakietów, tak aby dzieliły one przestrzeń na obszary domenowe, tzn. unikanie wrzucania wszystkich Repository do jednego pakietu, kontrolerów do kolejnego, itd. Jakub zasugerował użycie narzędzia veripacks, które umożliwia analizę zależności pomiędzy pakietami oraz wpięcie tego w testy jednostkowe, tak aby zależności pomiędzy wskazanymi pakietami były wykazywane jako błąd – ciekawe narzędzie które możemy już użyć w naszych nie-spring’owych projektach.
  • Rezygnacja z interfejsów: tam gdzie interfejs ma tylko jedną implementacje i kończy ona się suffiksem Impl, to nie jest on potrzebny. Mój ostatni argument że potrzebne to jest do mock’owania w czasie testów jednostkowych został obalony informacją że Mockito od dłuższego czasu doskonale sobie radzi z bezpośrednim mockowaniem klas.
  • Rezygnacja z serwisów – tam gdzie serwisy są tylko pustą przelotką do repozytorium, to nie ma sensu z nich korzystać i można np. bezpośrednio z kontrolerów wywoływać metody na repozytoriach.
  • Do wstrzykiwania zależności nie powinniśmy używać @Autowire na polach klasy, tylko wstrzykiwać zależności przez konstruktor. Wyjątkiem są klasy testów, gdzie można tak robić.
  • Nie powinniśmy używać „if(xxx == null) throw new RuntimeException(…);” tylko skrótowców „notNull” ze Springa albo Preconditions z Google Guava.
  • Użycie wzorca Pageable, czyli obsługa komponentu tabelki do wyświetlania posortowanego podzbioru danych.
  • Umieszczanie logiki biznesowej nie w serwisie, tylko bezpośrednio w Domain Object (w naszym przypadku jest to POJO zarządzane przez Hibernate).
  • Automatyzacja przekazywania przez kontroler gotowego obiektu POJO pobranego z bazy danych na podstawie identyfikatora obiektu przekazanego w zapytaniu – minimalizuje ilość powtarzalnego kodu ale trzeba pamiętać o sprawdzaniu uprawnień aby nie dopuścić do wycieku danych.
  • Tworząc obiekty POJO powinniśmy nie używać setterów tylko konstruktorów z parametrami i robić te obiekty Immutable.
  • Do konstruowania adresów URL nie powinniśmy używać konkatenacji napisów tylko dedykowanego obiektu MvcUriComponentsBuilder który zapewnia że tak utworzone adresy będą wskazywały istniejące kontrolery z właściwymi atrybutami.
  • Przy implementowaniu, testowaniu oraz umieszczaniu w środowisku produkcyjnym aplikacji powinniśmy korzystać z dedykowanych profili.

Testowanie przy pomocy Spock

Inną ciekawą rzeczą, którą pokazał nam Jakub była biblioteka Spock, która daje nowe możliwości w testowaniu jednostkowym, w porównaniu do standardowego JUnit. Dzięki użyciu języka Groovy zapis scenariuszy testowych stał się dużo bardziej czytelny i łatwy do napisania niż w języku Java. Ja osobiście widzę w tym bardzo dużą moc, szczególnie do zapisu przypadków na podstawie danych dostarczanych przez klienta – w końcu można takie dane w kodzie zapisać w postaci czytelnej tabelki a opakowania w tablice/kolekcje w języku Java:

spock_data_tables

Podsumowanie

Poza samą prezentacją oraz pracą nad naszym kodem porozmawialiśmy na trochę „luźniejsze” tematy, jak sytuacja na warszawskim rynku pracy programistów, pracy zdalnej, używaniu laptopów do pracy w firmie i w domu, itp.

Spotkanie z Jakubem, oprócz samej wiedzy nt. Springa dało nam inne spojrzenie na pewne obszary w których mamy bardzo konserwatywne podejście. Także reasumując: było warto :-).

O autorze

Marek Berkan Marek Berkan: programista, entuzjasta tworzenia oprogramowania, zarządzania zespołami technicznymi. Prywatnie motocyklista, kolarz MTB, biegacz, żeglarz, rekreacyjny wspinacz, zamiłowany turysta. Witryny: , , .

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *