W świecie rozproszonych systemów webowych, utrzymanie równowagi między backendem a frontendem często jest wyzwaniem, któremu niełatwo sprostać. Pracując z oddzielnymi warstwami, programiści napotykają problemy związane z synchronizacją, komunikacją i skalowalnością. Na szczęście, na horyzoncie pojawia się wyjątkowe rozwiązanie – Hilla.
W tym artykule odkryjemy, jak Hilla ułatwia płynną komunikację między warstwą backendu a warstwą frontendu, gwarantując jednocześnie efektywność. Następnie skoncentrujemy się na walidacji formularzy, prezentując, jak Hilla sprawia, że proces ten staje się prostszy, bezpieczniejszy i bardziej efektywny.
Czym jest Hilla?
Hilla to innowacyjny framework stworzony przez zespół Vaadin. Łączy w sobie narzędzie Spring Boot oraz bibliotekę React. Oferuje nie tylko skuteczną i łatwą integrację, ale rozwiązuje również problemy z utrzymywaniem oddzielnych warstw backendu i frontendu. Zapewnia wygodę i efektywność w procesie tworzenia aplikacji webowych.
Struktura projektu w Hilla
Warunkiem rozpoczęcia pracy z Hilla jest zainstalowanie na komputerze NodeJS oraz interfejsu wiersza poleceń npm (npx jest już zainstalowany w npm od wersji 5.2.0).
Aby zacząć pracę z Hilla, konieczne jest utworzenie projektu. Poniżej znajduje się komenda, która to umożliwi:
npx @hilla/cli init hilla-blog
Oto jak wygląda struktura katalogów po wygenerowaniu projektu:
- Frontend (React)
- frontend/components –katalog zawierający komponenty React, które są budulcem interfejsu użytkownika. Tutaj zawarte są elementy wizualne i funkcjonalne, takie jak przyciski, formularze czy karty.
- frontend/themes –katalog dedykowany stylom i motywom graficznym. Zawiera pliki dotyczące arkuszy stylów, ustawień kolorów czy czcionek, umożliwiając łatwe zarządzanie wyglądem aplikacji.
- frontend/util –katalog z narzędziami i funkcjami pomocniczymi, które mogą być używane w różnych miejscach projektu. Zawiera funkcje ogólnego przeznaczenia, które ułatwiają manipulację danymi czy wykonywanie pewnych operacji.
- frontend/views –katalog z widokami, czyli głównymi strukturami stron w aplikacji. Tutaj umieszczane są pliki odpowiedzialne za renderowanie konkretnych sekcji interfejsu użytkownika.
- Backend (Spring Boot)
- src/main/java/com/example/application –klasyczna struktura plików w projekcie Spring Boot.
Ta struktura projektu ułatwia nawigację oraz również organizuje kod w sposób, który wspiera rozwój zarówno frontendu, jak i backendu. Dzięki oddzieleniu katalogów „frontend” i „src,” Hilla umożliwia skoncentrowanie się na efektywnej pracy nad obiema warstwami aplikacji.
Jak działa Hilla?
Hilla umożliwia wywoływanie metod Java z poziomu TypeScript. Kluczowym elementem tej funkcjonalności jest adnotacja @BrowserCallable. Wszystkie metody publiczne w klasie, oznaczonej tą adnotacją, stają się dostępne i wywoływalne po stronie frontendu. Podczas uruchamiania projektu Hilla wygeneruje w TypeScript odpowiedniki struktur danych użytych w klasie oznaczoną adnotacją @BrowserCallable. Dodatkowo, tworzy odpowiednik tej klasy, umożliwiając płynne i bezproblemowe korzystanie z tych metod po stronie frontendowej.
Demonstracja działania Hilla na przykładzie projektu
Warstwa backend
Na potrzeby tego artykułu przygotuję warstwę backendową składającą się z encji, repozytorium, dto, mappera i serwisu w celu pokazania działania frameworka Hilla.
- Encja:
- Repozytorium:
- DTO:
- Mapper (wykorzystam narzędzie MapStruct):
- Serwis:
Dodałem do serwisu dwie metody:
- findAll() – która posłuży do wyszukania wszystkich rekordów Car,
- create(CarDTO carDTO) – która posłuży do stworzenia nowego rekordu Car.
W serwisie użyłem metody @BrowserCallable, aby umożliwić wywoływanie metod z aplikacji React. Dodatkowo użyłem adnotacji @AnonymousAllowed, aby wyłączyć kontrolę dostępu, którą zapewnia Hilla (nie zalecam robienia tego w aplikacji – robię to tylko na potrzeby tego artykułu).
Typy non-nullable są obowiązkowe, wymagając wartości, podczas gdy typy nullable są opcjonalne, pozwalając na brak wartości.
Domyślnie reguły Java dyktują mapowanie i generowanie typów:
- Typy pierwotne, takie jak „float”, nie mają wartości null.
- Typy referencyjne, takie jak „String” lub „Float” są nullable.
- Kolekcje akceptują wartość null, chyba że typ elementu jest prymitywny.
- Mapy akceptują wartość null, chyba że typ elementu jest prymitywny.
Użyłem również adnotacji @Nonnull. Deweloperzy Hilla zdecydowali się dodać tę adnotację, ponieważ istniejąca adnotacja `jakarta.annotation.Nonnull` nie ma zastosowania do parametrów typu. Jest ona używana przez generator TypeScript jako źródło informacji o nieważności typu.
W ramach projektu wykorzystam bazę danych H2 tworzoną w pamięci aplikacji przy jej starcie. Dodam również testowe dane, aby móc wyświetlić je po stronie frontend.
Warstwa frontend
Kolejnym etapem jest uruchomienie projektu, umożliwiając Hilla automatyczne wygenerowanie plików gotowych do użycia po stronie frontendu.
Oto jak wygląda struktura wygenerowanych plików po uruchomieniu projektu:
CarDTO.ts odzwierciedla strukturę pliku CarDTO.java, który został wcześniej zdefiniowany po stronie backendu.
Plik CarDTOModel.ts zawiera definicje pól oraz zasady walidacyjne przypisane do poszczególnych pól w DTO dla obiektu Car.
Plik CarService.ts stanowi odniesienie do klasy CarService.java, zawierając deklaracje poszczególnych metod zdefiniowanych w tej klasie.
Z takim zestawem wygenerowanych plików przystępuję do stworzenia widoku CarView.ts, w którym dodam formularz składający się z dwóch pól tekstowych oraz przycisku, mających umożliwić dodawanie nowych rekordów. Pod formularzem dodam tabelę, w której będą wyświetlane dane.
Po uruchomieniu projektu możemy zobaczyć taki wygląd aplikacji. Jak widać, nasz widok został wygenerowany, podobnie jak wysuwane menu po lewej stronie. Podczas generowania projektu, Hilla domyślnie dodaje takie menu w pliku MainLayout.tsx za pomocą komponentu AppLayout. Można go dowolnie konfigurować.
Walidacja danych
Walidacja danych to zapobieganie błędom oraz efektywne zarządzanie sytuacjami, gdy dane nie spełniają oczekiwanych kryteriów. W tym podrozdziale zgłębimy techniki obsługi błędów związane z walidacją w Hilla Framework.
Hilla upraszcza proces sprawdzania poprawności danych wprowadzanych przez użytkownika, wykorzystując model danych zaplecza Java. Interpretując adnotacje Bean Validation (JSR-380) obecne w typach danych Java, płynnie wymusza te ograniczenia na przychodzących danych wejściowych użytkownika. Dodajmy adnotacje walidacyjne do naszych pól w pliku CarDTO.java. Załóżmy, że pola „brand” i „model” nie mogą być puste oraz muszą zawierać przynajmniej 3 znaki. Wykorzystam do tego adnotację @NotEmpty oraz @Size.
Następnie uruchamiam projekt – spójrzmy, jak wygląda plik CarDTOModel.ts. Zawiera on te same zasady walidacyjne, które zamieściłem po stronie backendu w pliku CarDTO.java:
Teraz mogę przystąpić do podłączenia walidacji do naszego formularza w pliku CarView.ts.
Podczas wykonania metody submit wykonywana jest metoda CarService.create(car), która wywołuje metodę napisaną na backend w CarService.java. Jako odpowiedź otrzymuje stworzony obiekt, dodaje nowo stworzony obiekt do tablicy obiektów „cars”. Na koniec formularz jest czyszczony za pomocą metody clear().
Sprawdźmy, jak działa aplikacja
Po próbie wciśnięcia przycisku „Save” z nieuzupełnionymi polami w formularzu otrzymujemy informację pod konkretnymi polami, jakie zasady walidacyjne zadziałały.
W tym przykładzie text field „Brand” zarówno jak i text field „Model” są puste, więc zadziałała walidacja, którą napisałem po stronie backendu, stosując adnotację @NotEmpty.
Teraz spróbuję wpisać dwuznakowe inputy w celu sprawdzenia działania drugiej walidacji. Jak widać na poniższym obrazku, zadziałała druga walidacja, którą dodałem na backend, stosując adnotację @Size.
Następnie spróbuję wpisać dane spełniające dwa warunki walidacyjne. Formularz zaobserwował zmianę danych na poprawne i pozwala je zapisać.
Po naciśnięciu przycisku „Save” możemy zaobserwować poprawne dodanie naszych danych do tabeli z wynikami.
Przykład komponentu gotowego do użycia
Hilla wychodzi naprzeciw wymaganiom tworzenia aplikacji internetowych i dostarcza gotowe komponenty.
Zaprezentuję działanie komponentu AutoGrid. Obsługuje on funkcje sortowania, filtrowania oraz pobiera dane z backendu za pomocą lazy loading. Osiąga się to poprzez wywołanie metody ListService<T>.list(Pageable pageable, @Nullable Filter filter).
Na początek musimy dostosować backend. Dodaję interfejs JpaSpecificationExecutor do repozytorium, aby umożliwić wykonywanie określonych zapytań do bazy danych.
Rozszerzę klasę CarService. Rozszerzy ona dedykowaną klasę (ListRepositoryService<T, ID, R>
która z kolei implementuje ListService<T>
), którą Hilla dostarcza do obsługi komponentu Auto Grid.
Po stronie frontendu możemy zastąpić komponent Grid komponentem AutoGrid, który będzie używał odpowiednik TypeScript naszego serwisu z backend oraz model CarDTO w parametrach.
Po uruchomieniu aplikacji możemy zobaczyć jej wygląd.
Jak widać, mamy możliwość sortowania i filtrowania wartości. Zobaczmy, jak działa sortowanie.
Widzimy, że wartości zostały posortowane według pola Brand. Spróbujmy odfiltrować wartości Model.
Wartości pola Model zostały poprawnie odfiltrowane. Jak możemy zauważyć, Hilla dysponuje szeroką gamą rozwiązań, które mogą nam pomóc w implementacji naszych projektów.
Konfiguracja i wdrożenie
W projekcie Hilla można skonfigurować zarówno frontend, jak i backend. Aby skonfigurować frontend, możemy użyć pliku tsconfig.json, w którym konfigurujemy kompilator TypeScript. Do konfiguracji backendu możemy użyć pliku pom.xml, w którym da się dodawać zależności i zarządzać produkcyjnymi kompilacjami naszej aplikacji.
Jeśli chcemy wygenerować kompilację produkcyjną, wystarczy uruchomić polecenie:
mvn clean package -Pproduction
Wykonanie tego polecenia spowoduje utworzenie pliku JAR ze wszystkimi zależnościami i zasobami frontendu, gotowego do wdrożenia.
Dokumentacja Hilla zapewnia gotowe instrukcje wdrażania u najlepszych dostawców usług w chmurze (tj. AWS, Azure, Google Cloud, Heroku).
Podsumowanie
W artykule starałem się rzucić światło na fascynujący świat frameworka Hilla, ukazując jego unikalne zalety i potencjał w szybkim tworzeniu aplikacji fullstack. Mam nadzieję, że udało mi się przekazać, jak w prosty sposób możemy wykorzystać to narzędzie do efektywnej integracji warstwy frontendowej i backendowej, tworząc spójne i wydajne aplikacje webowe.
Hilla ułatwia nie tylko proces tworzenia aplikacji, ale także skraca drogę do osiągnięcia pełnej funkcjonalności. Podkreślam, że Hilla oferuje szeroki wachlarz gotowych komponentów, usprawniając tym samym proces budowy interfejsu użytkownika. Dodatkowo, framework dostarcza rozbudowane implementacje zabezpieczeń, co pozwala z łatwością dbać o bezpieczeństwo aplikacji.
Z racji, że Hilla jest inicjatywą open source, zachęcam was do brania udziału w tym projekcie, gdzie możecie poszerzać swoje umiejętności i zapisać się na kartach historii. Na oficjalnej stronie znajdziecie dokumentację, a także pełen zakres dostępnych komponentów i narzędzi, które sprawią, że tworzenie aplikacji stanie się jeszcze bardziej efektywne i przyjemne.
Zostaw komentarz