Wstęp
Korzystając w aplikacji z Hibernate czy też Java Persistence API, aby móc użyć danych korzysta się z różnych obiektów. Pierwszym rodzajem obiektów jest encja. Ale aplikacja to nie tylko dane, ale także logika biznesowa wykorzystująca dane wejściowe i wyjściowe, obiekty domenowe. Definicją struktury danych poza tabelami może być REST API, event (Kafka, RabbitMQ), biblioteka, moduł wewnętrzny, główna domena. Sama klasa opakowująca dane może mieć jakąś logikę. W jaki sposób skorzystać i kiedy z poszczególnych rodzajów obiektów zawierających dane? Jakie jest zadanie poszczególnych rodzajów obiektów. Postaram się to przybliżyć w niniejszym artykule oraz wskazać, jakie są możliwości.
Immutable
Obiekt immutable reprezentuje obiekt, który jest niemodyfikowalny. Oznacza to, iż aby uzyskać obiekt o zmienionych danych, należy stworzyć nowy, ze zmienionymi danymi. W Java istnieje wiele klas immutable – String, BigDecimal, Long etc.
Obiekt może reprezentować zarówno pojedynczą wartość, jak i zbiór atrybutów.
Może zawierać złożoną logikę w środku.
Czy encja może być immutable? – nie może. Encja jest modyfikowalna (ustawianie wartości pól reprezentujących kolumny tabeli czy widoku). Mimo iż istnieje anotacja @Immutable (Hibernate) to sama edycja encji jest możliwa – zmiany zostaną zignorowane lub zostanie rzucony wyjątek dla modyfikacji. Encja @Immutable może również zostać utworzona czy usunięta.
Plain Old Java Object
POJO jest rodzajem obiektu, który przechowuje atrybuty i pozwala na korzystanie z nich. Wg definicji nie powinien mieć odniesienia do żadnego frameworka – SIC!
Jeśli podejść 'purystycznie’ do powyższego zapisu to użycie anotacji JPA czy Jackson może być uznane za złamanie definicji POJO.
Jeśli tak to może pojawić się pytanie dlaczego w dokumentacji Hibernate jest odniesienie do POJO z anotacjami javax.persistence.Entity? Czy encja jest w takim razie klasą POJO?
Odpowiedź brzmi (jak zwykle w programowaniu) – to zależy. o ile logika encji jest prosta, opiera się na danych z encji, nie ma skomplikowanej logiki (rich domain model) to można przymknąć oko na te anotacje. W takim modelu logika domenowa jest delegowana do innych obiektów domenowych, klas odpowiedzialnych za przetwarzanie danych.
Przykładem skomplikowanej logiki biznesowej może być np. transformacja danych z/do DTO, reagowanie na zdarzenia (event – zareaguj na to co się stało – np. konto zostało zablokowane z powodu przekroczenia limitu nieudanych prób logowania) lub rozkazy (command – zrób coś – np. blokada administracyjna konta)
W przypadku Hibernate czy JPA możliwe jest także skonfigurowanie mapowania w plikach XML. Dzięki temu anotacje nie będą występowały w klasie a obiekt może być traktowany jako podręcznikowy POJO.
Czy POJO musi być edytowalny? – nie. Ale niemodyfikowalnemu POJO jest bliżej do Value Object’a.
Value Object
Value Object reprezentuje zbiór danych, który jest niemodyfikowalny (immutable). Może zawierać logikę operującą na tych danych oraz opisywać pewien stan. Nie powinien zawierać skomplikowanej logiki ani identyfikować instancji obiektu (nie posiada tożsamości).
Psst… Interesujący artykuł?
Jeżeli podoba Ci się ten artykuł i chcesz takich więcej – dołącz do newslettera. Nie ominą Cię materiały tego typu.
Dziękujemy!
Wysłaliśmy Ci mail powitalny, w którym znajdziesz link do aktywacji newslettera. Do usłyszenia!
Powyższy przykład przedstawia Value Object dla karty (można taki kod wygenerować poprzez plugin Delombok do IDE dla klasy anotowanej @Value). Jak widać, nie ma on tożsamości (żadnego identyfikatora). Dodatkowo klasa jest final – tutaj pojawia się pewien aspekt traktowania dziedziczenia jako szczegółu implementacyjnego. Zatem kolejne ograniczenie dla Value Object – brak dziedziczenia.
Wczytując się w definicję to Value Object’em nie jest
- obiekt identyfikowalny – np. encja (podstawowym atrybutem encji poza typem jest unikalny identyfikator)
- obiekt o skompilowanej logice
- obiekt, który można zmienić
- obiekt, który dziedziczy
Czy Value Object może być POJO – tak.
Czy Value Object może być encją – nie, encja jest modyfikowalna oraz identyfikowalna. Można ukryć metody ustawiające dane, ale pola nie mogą być final, i sam framework JPA/Hibernate pod spodem modyfikuje stan obiektu podczas mapowania danych.
Data Transfer Object – DTO
Podstawowym zadaniem DTO jest agregowanie zbioru danych/atrybutów oraz umożliwienie/zapewnienie mechanizmów przesyłania/odbierania tych danych (np. serializacja/deserializacja). Nie powinien on mieć praktycznie żadnej logiki.
Przesyłanie danych związane z DTO niekoniecznie musi być związane z wymianą przez sieć. DTO może być definicją danych we/wy pomiędzy modułami/subdomenami czy elementami systemu (jak np. backend i frontend).
Czy DTO może być edytowalny? – to zależy od frameworka dla serializacji/deserializacji (może on wymagać seterów i pustego konstruktora). Skoro zadaniem DTO jest wymiana danych, to edycji (setterów) nie musi być. Idealnie gdy DTO jest niemodyfikowalny.
Czy DTO może być Value Objectem? – tutaj jest pewne podobieństwo do sytuacji opisanej wcześniej – tzn. czy encja jest POJO. Możemy mieć informację o serializacji/deserializacji (np. poprzez anotacje Jackson). Ale gdy DTO nie ma żadnych odniesień do funkcjonalności związanych z frameworkami/(de)serialiazacją, nie jest edytowalny to jest pełnoprawnym Value Objectem.
Czy DTO jest POJO – podobnie jak Value Objectem – bez dodatkowych elementów związanych z transferem jest POJO.
Czy encja może być DTO – nie jest to rekomendowane.
Po pierwsze encja jest fragmentem domeny/dziedziny. Pod spodem jest jakiś framework (np. implementacja JPA), encje często są obiektami proxy, lub takie zawierają (mogą pojawić się problemy serializacją/deserializacją). Przy deserializacji pewnych danych może nie być (relacje) – czy to oznacza, iż relacja ma być rozpięta a element podrzędny usunięty – niekoniecznie (a tak się może stać przy merge i nieodpowiednim ustawieniu kaskad w relacji).
Po drugie inna jest odpowiedzialność encji a inna DTO tzw. separation of concerns.
Po trzecie DTO może zawierać inne dane niż z encja, pewne agregaty, String zamiast relacji (np. nazwa województwa).
Po czwarte rolą DTO może być odseparowanie logiki dziedzinowej od funkcjonalności modułu i użycie DTO jako interfejsu wymiany danych. Pozwoli to w przyszłości na podmianę implementacji pod spodem, czy też wykrojenie do osobnego mikroserwisu (Loose Coupling and High Cohesion).
Projekcja encji
Projekcja dotyczy innej postaci danych z tabeli i/lub encji. W przypadku JPA czy Hibernate możesz zdefiniować kilka encji o różnej postaci dla tej samej tabeli.
Dodatkowo w Spring Data masz możliwość zdefiniowania mapowania danych pomiędzy encją (poprzez Repository) a interfejsem (mapowanie poprzez gettery o tej samej nazwę dla encji i interfejsu)
lub klasą DTO (mapowanie poprzez parametry konstruktora — składowe frazy select wstrzykiwane jako kolejne atrybuty konstruktora).
W tym przypadku dla DTO nie ma transferu, ale jest pewna separacja/translacja danych. Rola projekcji jest inna niż encji – odczyt + ewentualna agregacja danych.
Czy projekcja może być Value Objectem – tak, w przypadku korzystania z interfejsów lub DTO (w Repository ze Spring Data).
Obiekt domenowy
Obiekt domenowy reprezentuje fragment dziedziny (modułu, obszaru), którego dotyczy, o określonym znaczeniu. Może on także zawierać logikę.
Przykłady obiektów domenowych
- Użytkownik jako konto w systemie autoryzacji (identyfikator, hasło, data ważności konta)
- Osoba (imię, nazwisko, identyfikator, data urodzenia) w strukturze organizacyjnej firmy
- Limit karty w systemie autoryzacji operacji kartowych (rodzaj limitu, rodzaj ograniczenia np. kwota, liczba operacji dziennych, rozmiar ograniczenia np. 3000 lub 5).
Obiektem domenowym może być np. encja, ale może być to zupełnie inny obiekt, który nie ma persystencji.
Obiekt domenowy rozumiany jako domain entity wg. DDD powinien być identyfikowalny, przez co nie może być Value Objectem (nawet jeżeli byłby niemodyfikowalny).
Przydatne linki
- https://www.baeldung.com/java-pojo-class
- https://martinfowler.com/bliki/ValueObject.html
- https://en.wikipedia.org/wiki/Separation_of_concerns
- https://www.baeldung.com/spring-data-jpa-projections
Podoba Ci się ten artykuł? Weź więcej.
Jeżeli uważasz ten materiał za wartościowy i chcesz więcej treści tego typu – nie przegap ich i otrzymuj je prosto na swoją skrzynkę. Nawiążmy kontakt.
Dziękujemy!
Wysłaliśmy Ci mail powitalny, w którym znajdziesz link do aktywacji newslettera. Do usłyszenia!
Gdybyś potrzebował jeszcze więcej:
Jesteś Java Developerem?
Przejdź na wyższy poziom wiedzy
„Droga do Seniora” 🔥💪
Jesteś Team Leaderem? Masz zespół?
Podnieś efektywność i wiedzę swojego zespołu 👌
Gdybyś potrzebował jeszcze więcej:
Jesteś Java Developerem?
Przejdź na wyższy poziom wiedzy
„Droga do Seniora” 🔥💪
Jesteś Team Leaderem? Masz zespół?
Podnieś efektywność i wiedzę swojego zespołu 👌