byteleaf logo
← zurück zum Blog

GraphQL Microservices mit Apollo Gateway verknüpfen

GraphQL gewinnt als Kommunikationsmittel zwischen Backend und Frontend immer mehr an Bedeutung. Apollo Gateway bietet eine Lösung an, um aus vielen Ressourcen/Schnittstellen eine gemeinsame Schnittstelle zu machen und dabei Entitäten ohne großen Implementierungsaufwand zu verknüpfen.
Stefan Sauterleute
Stefan Sauterleute
24. Februar 2021

Einführung

Moderne Anwendungen werden meist in fachliche Komponenten aufgeteilt. Jede Komponente wird als eigenständiger Microservice von einem dedizierten Team entwickelt und deployed. Clients wie Webapplikationen oder Mobile-Apps können auf die einzelnen Microservices via HTTP zugreifen. Um den Clients Daten aus unterschiedlichen Services aufbereitet zur Verfügung zu stellen, wird oft ein sogenanntes “Backend for Frontend” (BFF) als Vermittlungsschicht/Querschnittskomponente eingezogen.

Nachfolgendes Beispiel zeigt ein mögliches Gesamtsystem einer Microservice-Architektur für einen Onlineshop:

12c1bb17-3451-4b95-a96a-3d74dbd46bbc.png

Problemstellung

Um eine Bestellung abzurufen, sind drei Requests notwendig. Zuerst werden die Bestellungen aus dem Order-Service abgerufen. Die Order-Ressource hält die Referenzen zu dem Account und den Produkten. Der Client kann diese Referenzen durch weitere Aufrufe auf den Account- und Product-Service auflösen. Um den Client zu entlasten, wird ein BFF zwischengeschaltet, über das alle Daten mit nur einem Aufruf geladen werden können. Allerdings ist das Problem nicht gelöst, sondern in das BFF verschoben.

Probleme

  • Die Logik für das Zusammenführen muss im BFF teamübergreifend implementiert werden. Im Idealfall sind die Teams nur für ihre fachliche Komponente - den Microservice - zuständig. (Ideally each team has only one service)

  • Abstimmungsaufwand zwischen den Teams

  • Teils doppelte Implementierung (Endpoints, Models/DTOs etc. in Service und BFF)

Lösung mit Apollo Gateway

Anstelle eines “klassischen BFF” wird Apollo Gateway verwendet. Apollo Gateway bindet unterschiedliche Services an und bietet seinen Aufrufern durch die Schema-Federation eine konsolidierte GraphQL-Schnittstelle an. Nachfolgend wird erläutert, wie genau diese Schema-Federation funktioniert.

b964106e-a25c-49b2-9a47-d515562b6782.png

GraphQL Schema Federation

Mit der Schema-Federation gibt es die Möglichkeit, die GraphQL-Schemas der einzelnen Services zusammenzufassen und dadurch ein zusammengefügtes Schema nach außen bereitzustellen. Die GraphQL-Services werden hierfür anhand ihrer URL in einer Service-Liste (serviceList) am Apollo Gateway registriert. Durch Direktiven können im GraphQL-Schema Verweise / Referenzen zwischen den Services/Domänen angegeben werden.

Beispielsweise hält die Entität Order im Order-Service die Referenzen zum Account und zu den Products.

1type Order {
2    id: ID!
3    title: String!
4    account: Account!
5    products: [Product!]!
6}

Damit GraphQL weiß, dass es sich hierbei um externe Entitäten handelt, werden die Entitäten mit @extends erweitert und das Feld “id” mit @external annotiert.

1type Product @extends {
2    id: ID! @external
3}
4type Account @extends {
5    id: ID! @external
6}

Die Entität Product wird mit @key im Product-Service erweitert. Dadurch wird festgelegt, welche Felder zur Auflösung der Entitäten dienen.

1type Product @key(fields: "id") {
2    id: ID!
3    name: String!
4    description: String!
5    price: Float!
6}

Der Product-Service muss nun einen Resolver für die Entität Product zur Verfügung stellen.

1  // Code written in Kotlin
2  
3  companion object {
4        const val TYPENAME = "Product"
5    }
6
7    @Bean
8    fun customSchema(schemaParser: SchemaParser, productService: ProductService): GraphQLSchema {
9        return Federation.transform(schemaParser.makeExecutableSchema())
10            .fetchEntities { env: DataFetchingEnvironment ->
11                env.getArgument<List<Map<String, Any>>>(_Entity.argumentName)
12                    .stream()
13                    .map<Any?> { values: Map<String, Any> ->
14                        if (TYPENAME == values["__typename"]) {
15                            val id = values["id"]
16                            if (id is String) {
17                                return@map productService.product(id)
18                            }
19                        }
20                        null
21                    }
22                    .collect(Collectors.toList())
23            }
24            .resolveEntityType { env: TypeResolutionEnvironment ->
25                val src = env.getObject<Any>()
26                if (src is Product) {
27                    return@resolveEntityType env.schema.getObjectType(TYPENAME)
28                }
29                null
30            }
31            .build()
32    }

Das Gateway kann nun Referenzen zwischen den Entitäten auflösen. Die Services selbst implementieren aber die Logik, wie die einzelnen Entitäten ermittelt werden. Das unterscheidet sich zu der obigen Problemstellung. Im Gegensatz hierzu wird keine Implementierung im BFF/GraphQL-API notwendig, da der entsprechende Service für das Auflösen der jeweiligen Entitäten zuständig ist und Apollo Gateway entsprechend die Requests an die jeweiligen Services stellt.

Vorteile die daraus resultieren:

  • Teams können sich auf ihre(n) Domäne/Service konzentrieren
  • Doppelter Aufwand bei der Implementierung von Service und BFF wird hinfällig

Das komplette Beispielprojekt kann auf GitHub gefunden werden: byteleaf/graphql-microservice-example

Fazit

Apollo Gateway bietet mit der Schema-Federation eine gute Möglichkeit, Clients wie Frontends oder Mobile-Apps Daten über eine Schnittstelle bereitzustellen. Dabei übernimmt das Gateway die Koordination mit den Services. Referenzen zwischen Entitäten können einfach aufgelöst werden, ohne dass das in der Querschnittskomponente - dem BFF - implementiert werden muss. Dies erleichtert die Arbeit der Teams, sodass sie sich auf ihren Service konzentrieren können. Zudem ist es einfacher und vor allem schneller möglich, Änderungen bereitzustellen (“Time-to-Market”).

Kontakt

E-Mail

info@byteleaf.de

Telefon

+49 89 90183650

Links

Code

GitHub

Wo wir sind

Adresse

byteleaf GmbH
Adamstraße 5
80636 München

ImpressumDatenschutzCookie-Einstellungen
© 2021 - Code: byteleaf - Design: Michael Motzek