DTO (Data Transfer Object) pomaga kontrolować przepływ danych między warstwami aplikacji, zapewnia bezpieczeństwo i przejrzystość kodu – musisz znać ten wzorzec!
Czym są DTO i dlaczego ich potrzebujemy?
DTO (Data Transfer Object), to obiekt, który służy do przesyłania danych pomiędzy różnymi warstwami lub komponentami aplikacji. Przykładowo, kiedy pobieramy dane z bazy danych, mogą one zawierać zbędne informacje, których nie chcemy ujawniać lub przekazywać do innych części systemu. Stosując DTO, możemy kontrolować, które dane faktycznie trafią do odbiorcy, zapewniając jednocześnie lepszą strukturę, izolację, a także wzmacniając bezpieczeństwo danych.
Jak stosować DTO: Przykład
Aby zrozumieć, jak DTO może uprościć i zabezpieczyć nasz kod, przyjrzyjmy się przykładowi prostego systemu, który przechowuje informacje o użytkownikach. Załóżmy, że mamy obiekt User
, który przechowuje dane takie jak: hasło czy prywatne identyfikatory, których nie chcemy ujawniać poza określonymi warstwami aplikacji.
class User {
constructor(
public readonly id: UserId,
public name: string,
public email: string,
private password: string,
private dateOfBirth: Date
) {}
// methods
}
Kiedy potrzebujemy przesłać tylko część danych o użytkowniku gdzieś indziej, to tworzymy odpowiedni DTO, który zawiera wyłącznie dane niezbędne w danym kontekście:
class UserDataDTO {
constructor(
public readonly id: string,
public readonly name: string,
public readonly email: string
) {}
}
Teraz, jeśli inna część systemu (np. API) wymaga danych użytkownika, zamiast przekazywać cały obiekt User
, możemy wygenerować instancję UserDataDTO
, która zawiera tylko jawnie określone pola. Dzięki temu mamy pełną kontrolę nad przesyłanymi danymi.
Response Object
Z punktu widzenia struktury aplikacji, DTO doskonale sprawdzają się w roli obiektów odpowiedzi, czyli tzw. Response Objects. Temat response objects poruszałem w jednym z poprzednich wpisów, ale przypomnę tylko, że ich zadaniem jest formatowanie i prezentacja odpowiedzi w sposób spójny i bezpieczny. Przy pomocy DTO możemy zagwarantować, że żadne dane, które nie powinny opuścić aplikacji, nie trafią do użytkownika końcowego.
Załóżmy, że nasze API zwraca dane użytkownika po jego zalogowaniu. Zamiast przekazywać pełny obiekt User
, tworzymy UserDTO
, dzięki któremu zabezpieczamy prywatne dane, takie jak hasło, i ograniczamy informacje dostępne dla użytkownika końcowego.
class UserDTO {
constructor(
public readonly id: string,
public readonly name: string,
public readonly email: string,
public readonly dateOfBirth: Date
) {}
}
Przetwarzanie Danych Wejściowych
DTO mogą również służyć do obsługi danych wejściowych, czyli requestów. Podobnie jak w przypadku Response Object, DTO stosowane w request-ach umożliwiają nam stworzenie jasnej, ściśle określonej struktury danych, której oczekujemy od użytkownika lub innej części systemu. Dzięki temu łatwiej jest walidować dane i kontrolować przepływ informacji.
Przykładowo, załóżmy, że potrzebujemy struktury danych do logowania użytkownika. Nie chcemy w tym celu stosować pełnej struktury obiektu User
, ponieważ wymagamy jedynie login i hasło. Tworzymy więc dedykowany DTO i w sposób zależny od naszej technologii, dodajemy logikę walidacyjną, np. za pomocą dekoratorów.
class LoginDTO {
constructor(
@Email()
@Required()
public email: string,
@String()
@Required()
public password: string
) {}
}
Podsumowanie
Data Transfer Object, to prosty, ale potężny wzorzec, który pomaga zapewnić bezpieczeństwo, optymalizację i strukturę w aplikacjach każdej skali. Wykorzystując go do budowy obiektów odpowiedzi oraz do walidacji danych wejściowych, możemy zapewnić lepszą integralność i spójność całego systemu. Używając tego podejścia:
- Zyskujemy pełną kontrolę nad strukturą danych w różnych kontekstach, co pomaga unikać niepożądanych wycieków informacji.
- Oddzielamy logikę domenową od reprezentacji danych przesyłanych pomiędzy różnymi komponentami, co ułatwia utrzymanie kodu w dłuższej perspektywie.
- Ułatwiamy walidację danych wejściowych.
Dodaj komentarz