Netflix DGS (Domain Graph Service) to najnowsze dziecko w rodzinie narzędzi open source od tego giganta streamingowego. Dzisiaj zobaczymy, co ciekawe można znaleźć w tej bibliotece.
Netflix DGS, czyli …?
Na stronie głównej projektu możemy przeczytać, że za sprawą Netflix DGS praca z GraphQL staje się prosta w Spring Boot. Brzmi nieźle, prawda?
Netflix nie jest firmą, która publikując swoje rozwiązania obiecuje gruszki na wierzbie. Dlatego dostajemy na dzień dobry już mocno przetestowaną w bojach bibliotekę. A kto, jak kto, Netflix ma na czym testować swoje oprogramowanie.
W tej chwili możemy korzystać już w Spring Boot z takich elementów jak:
- adnotacji DGS,
- wsparcia dla testów,
- generowania modelu prosto ze scheme GraphQL,
- subskrypcji GraphGL w WebSockets i SSE,
- wgrywania plików poprzez GraphGL,
- integracji z modelem Graph Federation.
Przejdźmy teraz do prostego przykładu.
Netflix DGS w akcji
W celu zobrazowania działania Netflix DGS posłużymy się przykładem katalogu projektów. Przede wszystkim chcielibyśmy, żeby nasz projekt miał dwa pola, tj. nazwę i krótki opis. A nasz GraphQL powinien nam pomóc pobierać dane poprzez Query. Fajnie, jak nasze zapytanie będzie sparametryzowany. Tzn. jeśli podamy w zapytaniu name to chcielibyśmy dostać listę tylko tych projektów, które w nazwie będą miały taki tekst.
Opisz dane z GraphQL Schema
Z punktu widzenia GraphQL to, jak nasze dane będą wyglądały opisujemy w GraphQL Schema. Jest to po prostu notacja definiująca m. in. typy danych, zapytania i rodzaje mutacji, jakie możemy wykonywać. Pozwala nam, jak również i maszynom, na interpretację, jakie będą możliwości naszej usługi. To nasz kontrakt.
W naszym przypadku schema będzie wyglądała następująco:
type Query {
projects(name: String): [Project]
}
type Project {
name: String
description: String
}
Powyżej określiliśmy typ obiektu Project oraz specjalny typ Query.
Query ma szczególne znaczenie. Jest to miejsce, gdzie definiujemy wszystkie zapytania, które będziemy obsługiwać. Potocznie jest to tak zwany entry point do zapytań.
W naszym przypadku jednym z elementów będzie więc zapytanie projects. Parametryzowane nazwą projektu i zwracające listę projektów spełniających to kryterium.
Model danych
Dzięki GraphGL Schema możemy przygotować sobie również model danych po stronie naszej usługi. Jest kilka możliwości.
W większych projektach na pewno korzystnie będzie skorzystać z automatycznego generowania modelu. Do tego Netflix przewidział plugin DGS codegen dla Gradle.
W naszym przypadku model jest na tyle mały, że ręcznie łatwo stworzyć prostą klasę:
public class Project {
private String name;
private String description;
public String name() {
return name;
}
public String description() {
return description;
}
public Project(String name, String description) {
this.name = name;
this.description = description;
}
}
Osobiście nie znoszę notacji get/set do pobierania i ustawiania pól w obiekcie. Wolę, gdy metoda zwracająca name nazywa się po prostu name(), a nie getName(). Dlatego bardzo się ucieszyłem, że DGS w tym względzie nie robił problemów przy późniejszej serializacji.
Nasze projekty będziemy trzymać w repozytorium. Poniżej zauważysz, że nie będzie to zbyt wyrafinowana struktura. To prosta lista z kilkoma elementami:
public class DATA {
public static final List<Project> PROJECTS = List.of(
new Project("GraphQL", "A query language for APIs"),
new Project("Netflix DGS", "GraphQL Made Easy for Spring Boot"),
new Project("Spring Boot", "Takes an opinionated view of building Spring applications and gets you up and running as quickly as possible"),
new Project("Spring Framework", "Provides core support for dependency injection, transaction management, web apps, data access, messaging, and more")
);
}
Netflix DGS jako komponent Spring
Przechodzimy teraz do serca usługi. Czyli miejsca, gdzie realnie obsłużymy nasze zapytanie. To tutaj widać mocne połączenie DGS z modelem programowania komponentów Spring.
@DgsComponent
public class ProjectFetcher {
@DgsData(parentType = "Query", field = "projects")
public List<Project> projects(@InputArgument("name") String name) {
return Optional.ofNullable(name)
.map(this::byName)
.orElse(DATA.PROJECTS);
}
private List<Project> byName(String name) {
return DATA.PROJECTS.stream()
.filter(p -> p.name().contains(name))
.collect(Collectors.toList());
}
}
Dzięki adnotacjom @DgsComponent i @DgsData widzimy, za jaki fragment z GraphQL Schema będzie odpowiedzialny ten komponent. Tu wszystkie elementy się łączą. Nasza metoda jest opisana jako obsługująca Query, a konkretnie zapytanie (pole) projects. Jednocześnie argument wejściowy oznaczony poprzez adnotację @InputArgument będzie przekazany do ciała metody. Na wyjściu otrzymamy listę projektów.
Jak możemy sprawdzić działanie?
Netflix DGS dostarcza nam do tego od razu narzędzie. Przy korzystaniu ze starter:
<dependency>
<groupId>com.netflix.graphql.dgs</groupId>
<artifactId>graphql-dgs-spring-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
od razu w naszej usłudze pojawia endpoint http://localhost:8080/graphiql. Jest to w rzeczywistości bardzo mała aplikacja frontendowa, która pozwala nam wykonywać zapytania bezpośrednio do naszej usługi.
Poniżej przykład zapytania pobierającego listę projektów, w których nazwach występuje fraza „Spring”:
Możemy zmienić zapytanie i odpytać usługę tylko o projekty z frazą „Graph”. Ale tym razem poprosić tylko o nazwy projektów:
Czym to się różni do REST API?
Nie chciałbym w tym miejscu pisać o wszystkich różnicach, bo jest ich dużo i pewnie mógłby na ten temat powstać oddzielny artykuł. Skupię się tylko na kilku elementach, które są widoczne przy bliższym przyjrzeniu się Netflix DGS:
- Netflix DGS stanowi bardzo wygodną nakładkę na biblioteki niższego poziomu, które do tej pory były wykorzystywane do obsługi GraphQL w Javie.
- Sam GraphQL jest tak naprawdę ewolucją sposobów komunikacji. Niektórzy nazywają go lepszym RESTem (choć pewnie kilka osób może się z tym stwierdzeniem nie zgodzić).
- Technicznie, jeśli popatrzymy w wysyłane żądania do usługi np. poprzez narzędzia developerskie Chrome, to zauważymy, że wszystkie zapytania idą na jeden enpoint http://localhost:8080/graphql. Czyli inaczej niż tradycyjna obsługa REST API, która do tego wykorzystywała oddzielny enpoint dla każdego typu żądania.
- Netflix DGS poza samym modelem programowania zapewnia dodatkowe narzędzia (generowanie kodu, frontend do zapytań), które na dzień dobry ułatwiają z nim pracę.
- W GraphQL możemy pobierać tylko te dane, które rzeczywiście nas interesują (np. jeśli chcemy pobrać tylko tytuły projektów, to nie dostaniemy ich opisów).
- Gołym okiem widać, że REST jest prostszy przy pierwszym użyciu, jak również jest to cały czas de facto standard branżowy.
Podsumowanie
Ja bym powiedział tak: Netflix DGS mi się podoba.
Samo API i podejście do integracji ze Springiem nie zaskakuje. Jest jasne i klarowne. Robi to, co powinno.
Bardzo cieszy fakt, jak sami twórcy to podkreślają, DGS jest już od 2019 roku produkcyjnie wykorzystywany przez Netflix. A zatem zdążył przeżyć swoje. I nie dostajemy wersji alfa, czy beta, a już realnie działającą i odporną wersję 3 tej biblioteki.
A Tobie się podoba? Gdzie byś zastosował to w swoich projektach?
Fajny ten Netflix DGS. Tutaj znajdziesz więcej informacji o nim:
BTW, tutaj jest kilka ciekawych linków w temacie:
Open Sourcing the Netflix Domain Graph Service Framework: GraphQL for Spring Boot od Netflix Technology Blog
GraphQL server in Java: Part I: Basics od Tomka Nurkiewicza (wszystkie 3 części)
How Netflix Scales its API with GraphQL Federation (Part 1) od Netflix Technology Blog (wszystkie 2 części)
Rzetelne źródło wiedzy w polskim internecie 🙂
Dzięki. Staram się 🙂
Zabrakło mi informacji czym różni się ten netfliksowy twór od aktualnie dostępnych bibliotek do obsługi graphql dla springa/Javy.
Rzuciło mi się w oczy póki co jedynie inne deklarowanie metod przechwytujących żądanie (Netflix dorzucił własne adnotacje).
Wygląda to póki co na lekkie uporządkowanie i lukier ale chętnie zajrzę do dokumentacji 😉
Dzięki za wpis!
Zdecydowanie to lukier. Ale taki fajny, dający szybszą wartość bez zbędnego konfigurowania.
Biblioteka bardzo spoko, natomiast brakuje mi tutaj jednej zasadniczej rzeczy, tj. mocka w stylu wiremock. Gdzie podczas testów clienta graphQL nawet ui testów (cypress/selenium) mockujemy sobie odpowiedzi w zalezności od specyfikacji testu przy pomocny resta. Jest tylko wspomniane w dokumentacji o tym ze mozemy sobie zkodować mocka statycznego, co jednak wiarze sie niestety z jego pozniejszym utrzymywaniem.
Sam DGS jest dosyć młodą bibliteką dostępną publicznie. Stąd też pewnie niektóre elementy jeszcze mogą ulec poprawie 🙂