Entwurf von Microservices mit OpenAPI

Nach meiner Erfahrung gibt es kein Patentrezept, welches automatisch zu einem "guten" Entwurf von Microservices führt. Das konkrete Vorgehen hängt in erster Linie von den Gegebenheiten des jeweiligen Problems bzw. des Projektes ab, wobei insbesondere die Ausgangssituation entscheidend ist: Handelt es sich um die Erneuerung einer bestehenden Anwendung oder wird die Anwendung von Grund auf neu entwickelt?

Im Rahmen dieses Beitrages konzentriere ich mich auf den zweitgenannten Fall und orientiere mich dabei an der SEED(S) Methodik, welche von Ronnie Mitra und Irakli Nadareishviliim in Buch Microservices: Up and Running beschrieben wurde. Die beiden Autoren empfehlen sieben aufeinander aufbauende Entwicklungsschritte (siehe nächstes Kapitel). Schritt fünf beinhaltet eine formale Beschreibung der Abfrage und Aktion als Spezifikation (aka API first Design). Die in diesem Schritt empfohlene OpenAPI-Spezifikation (OAS) werde ich nachstehend anhand eines Beispiels genauer erläutern.

Sieben Entwicklungsschritte für den Entwurf von für Services (SEED(S))

Der SEED(S)-Prozess bietet eine praxiserprobte Methodik für den Entwurf von benutzerfreundlichen und robusten Service-Schnittstellen. Er besteht aus nachstehenden Schritten:

  1. Identifizierung der Akteure
  2. Identifizierung der Aufgaben, welche die Akteure zu erledigen haben
  3. Erkennen und Darstellung von Interaktionsmustern z.B. mit Sequenzdiagrammen
  4. Ableitung von Aktionen und Abfragen auf hoher Ebene auf der Grundlage der zu erledigenden Aufgaben und der Interaktionsmuster
  5. Beschreibung der einzelnen Abfragen und Aktionen als API-Spezifikation mit einem offenen Standard (wie z.B. OpenAPI)
  6. Einholen von Feedback zur API-Spezifikation
  7. Implementierung der Microservices auf der Basis der API-Spezifikation

Beschreibung von Abfrage und Aktion als Spezifikation (API first Design)

Bevor wir mit der Implementierung in Code für einen Microservice bzw. dessen API beginnen, sollte gemäss SEED(S) ein Schnittstellenvertrag entworfen werden. Hierbei empfiehlt sich die Verwendung von offenen Standards wie die OpenAPI-Spezifikation (OAS) oder GraphQL. Beide werden durch zahlreiche Werkzeuge unterstützt.
Ich beschränke mich im Folgenden auf die OpenAPI Spezifikation.

OpenAPI-Spezifikation (OAS)

Die OpenAPI-Spezifikation (OAS) definiert eine standardisierte, von der eingesetzten Programmiersprache unabhängige Notation für die Beschreibung von RESTful APIs. Ziel dabei ist es sowohl Menschen als auch Computern zu ermöglichen, die Funktionen eines Dienstes zu verstehen, ohne dass der Zugang zu Quellcode, zusätzlicher Dokumentation oder die Untersuchung des Netzwerkverkehrs erforderlich ist.

Bei korrekter Definition über OpenAPI kann ein Nutzer / eine Nutzerin in kurzer Zeit verstehen, wie er/sie mit einem Service interagieren kann (einschliesslich der Endpunkte, Parameter, Anfragemethoden und Antwortcodes). Ähnlich wie Schnittstellenbeschreibungen die Low-Level-Programmierung erleichtern, wird durch die OpenAPI-Spezifikation das Rätselraten über den korrekten Aufruf eines Dienstes verringert.

ℹ️
Das Akronym API steht für Application Programming Interface und definiert die Art und Weise, wie wir programmatisch mit einer Anwendung interagieren können. In diesem Sinne stellt das API also einen Vertrag zwischen einem Client und einem Provider dar.

Komponenten der OpenAPI
Im Folgenden sind einige der wichtigsten Komponenten der OpenAPI-Spezifikation aufgeführt:

  • Info: Allgemeine Informationen zur API wie Name, Version, Beschreibung, Kontaktinformationen des Betreibers und Lizenzinformationen.
  • Paths: Definition der einzelnen Endpunkte oder Ressourcen der API, welche über HTTP-Anfragen aufgerufen werden. Für jeden Pfad müssen die unterstützten HTTP-Methoden (z. B. GET, POST, PUT, DELETE), Parameter, Anforderungen und mögliche Antworten angegeben werden.
  • Parameters: Definition der Parameter, welche in den Pfaden oder Ressourcen der API verwendet werden. Beispielsweise können Query-Parameter, Header-Parameter oder Pfad-Parameter definiert werden.
  • Request Bodies: Beschreibung der Daten, welche in einer HTTP-Anfrage an die API übermittelt werden.
  • Responses: Beschreibung der möglichen Antworten, welche die API zurückgibt. Für jede Antwort muss der HTTP-Statuscode, eine Beschreibung und ein Beispiel angegeben werden.
  • Security: Beschreibung, wie die API gesichert werden kann. Zum Beispiel kann die Verwendung von API-Schlüsseln, OAuth2 oder anderen Authentifizierungs- oder Autorisierungsmechanismen definiert werden.

Für weitere Informationen zu den genannten Komponenten verweise ich auf die offizielle OpenAPI-Spezifikation.

Anwendungsbeispiel

Im nachstehenden Beispiel enthält eine Product-Ressource, welche über die Pfade /products und /products/{productId} verfügbar gemacht wird. Die API unterstützt die HTTP-Methoden GET, POST, PUT und DELETE für die Verwaltung von Produkten. Weiter enthält das Beispiel unter der Komponente Schemas eine Definition für die Datenstruktur eines Produkts.

openapi: 3.0.2
info:
  title: Product API
  description: A simple API for managing products
  version: 1.0.0
servers:
  - url: http://localhost:8000
paths:
  /products:
    get:
      summary: Get all products
      operationId: getAllProducts
      responses:
        '200':
          description: OK
    post:
      summary: Create a new product
      operationId: createProduct
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Product'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
  /products/{productId}:
    get:
      summary: Get a product by ID
      operationId: getProductById
      parameters:
        - name: productId
          in: path
          required: true
          description: The ID of the product to retrieve
          schema:
            type: string
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
    put:
      summary: Update a product by ID
      operationId: updateProductById
      parameters:
        - name: productId
          in: path
          required: true
          description: The ID of the product to update
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Product'
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Product'
    delete:
      summary: Delete a product by ID
      operationId: deleteProductById
      parameters:
        - name: productId
          in: path
          required: true
          description: The ID of the product to delete
          schema:
            type: string
      responses:
        '204':
          description: No Content
components:
  schemas:
    Product:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
        description:
          type: string
        price:
          type: number
          minimum: 0

Das Ergebnis obiger Definition kann mit Hilfe von verschiedenen Tools grafisch dargestellt werden (siehe Snapshot unten).

Ein oft verwendetes Werkzeug stellt das open source Tool Swagger Editor dar, welches eine OpenAPI-Spezifikation automatisch analysieren und daraus eine grafische Benutzeroberfläche generieren kann. Das Ergebnis ist eine interaktive Dokumentation, welche es Entwicklern ermöglicht, die API zu erkunden und ihre verschiedenen Endpunkte, Parameter und Antworten zu testen.

Bekannt ist auch Stoplight Studio, eine API-Designplattform. Sie ermöglicht es, OpenAPI-Spezifikationen grafisch zu entwerfen und zu bearbeiten. Weitere Tool sind Redocly, ReDoc und Apicurio.

Grafischen Darstellung OpenAPI Dokumentation
💡
Es gibt keine strikte Regel für die Verwendung von Singular oder Plural in einer RESTful Methode. Es gibt jedoch eine weit verbreitete Konvention, dass die Pluralform verwendet wird, wenn mit einer Sammlung von Ressourcen gearbeitet wird (z.B. eine Liste von Produkten), während die Singularform verwendet wird, wenn mit einer einzelnen Ressource gearbeitet wird.
Ein Beispiel: Wenn Sie eine Liste von Produkten abrufen möchten, wäre es üblich, die URL "/products" zu verwenden. Wenn Sie jedoch ein bestimmtes Produkt abrufen möchten, wäre die URL "/products/{productId}" (wobei "{productId}" durch die ID des Produkts ersetzt wird).
Diese Konvention erleichtert es auch anderen Entwicklern, Ihre API zu verstehen und zu verwenden, da sie sich an eine allgemein akzeptierte Praxis halten können.

Nutzen der OpenAPI-Spezifikation

  • Standardisierung: OpenAPI stellt einen einheitlichen Standard für die Dokumentation von APIs bereit, welcher die Kommunikation zwischen API-Entwicklern, API-Nutzern und anderen Stakeholdern vereinfacht. Durch die Nutzung eines standardisierten Formats können auch Werkzeuge und Bibliotheken zur automatischen Generierung von Code und Dokumentationen effektiver eingesetzt werden.
  • Verbesserte Dokumentation: Durch die Verwendung von OpenAPI kann die Dokumentation von APIs automatisch generiert werden. Das spart Zeit und stellt sicher, dass die Dokumentation immer auf dem neuesten Stand ist.
  • Einfache Integration: Durch die Verwendung von OpenAPI können APIs einfacher in Anwendungen integriert werden. Entwickler können schnell und einfach auf die API-Dokumentation zugreifen und Anfragen an die API senden.
  • Bessere Zusammenarbeit: OpenAPI erleichtert die Zusammenarbeit zwischen API-Entwicklern, API-Nutzern und anderen Stakeholdern. Die Spezifikation ist eine klare und konsistente Beschreibung der API, die von allen Beteiligten leicht verstanden werden kann.
  • API-Testing: OpenAPI ermöglicht es Entwicklern, automatische Tests zu erstellen, um sicherzustellen, dass die API korrekt funktioniert und den Spezifikationen entspricht.

Zusammenfassung

Der API-Design First-Ansatz sieht vor, dass Sie Ihre API-Definition schreiben, bevor Sie mit der Implementierung des Codes beginnen. Dies ist insbesondere im Zusammenhang mit der Neuentwicklung einer Microservice basierten Architektur hilfreich.

  • Ein effektives API-Design hilft allen Beteiligten, die Ressourcen sowie den Nutzen und das Zusammenspiel der verschiedenen Microservices durch ihre APIs schnell zu verstehen.
  • Die Identifizierung von Designproblemen vor dem Schreiben von Code ist effizienter als die Suche nach Problemen nach der Implementierung.
  • Sobald die API-Definitionen erstellt wurden, kann mit der Implementierung begonnen werden. Dank einem guten Design ist es einfacher, die Implementierungsaufgaben unter den Entwicklern aufzuteilen.