Domain-driven API-first Design mit Schema.org

1 Kommentar

APIs sind längst mehr als technische Schnittstellen zwischen Backend und Frontend. Für viele Unternehmen werden APIs zum Kern des Geschäftsmodells. Unter den Stichworten „API as a Product“ und „API-first“ rücken disruptive Unternehmen APIs in den Fokus ihres geschäftlichen Handelns und bedienen so völlig neue Märkte.

Um diesen Ansatz zum Erfolg zu führen, muss bereits beim Design einer API darauf geachtet werden, dass das fachliche Angebot im Vordergrund steht und nicht technische Implementierungsdetails. Ich zeige in diesem Artikel anhand eines Beispiels, wie dies mit Hilfe von Schema.org gelingen kann.

Fachlichkeit fokussieren – auch bei API-first

Beim Design einer API sollte sich ein Unternehmen zunächst folgende Fragen stellen:

  • Welche Dienste bieten wir an?
  • Welche Daten bieten einen Nutzen für unsere Kunden?
  • Was möchten Kunden mit unserer API erreichen?

Die Überlegungen sind also sehr Konsumenten-orientiert und betrachten die API tatsächlich als Produkt welches angeboten wird. In Verbindung mit „API as a Product“ bietet sich ein „API-first“ Ansatz an, bei dem die API die erste Benutzungsschnittstelle einer Anwendung darstellt, noch bevor ein grafisches Interface entwickelt wird.

Für ein erfolgreiches API-Produkt reicht es nicht aus, das interne Datenmodell als JSON auszuliefern. Anbieter einer API müssen Domänen orientiert denken und nicht in technischen Details. Fragen nach JSON-Bezeichnern, Daten-Verschachtelung und URL-Strukturen sollten nicht im Fokus stehen, sondern erst nachrangig betrachtet werden. Wichtiger ist eine klar kommunizierte Fachlichkeit und sehr gute Zugänglichkeit für Entwickler:innen (Developer Experience).

Der Fokus auf die Fachlichkeit deckt sich mit dem Anspruch des Domain-driven Designs (DDD). Bei DDD werden Konzepte der Fachdomäne auf die Modellierung in Software übertragen. Produkt-Teams können Methoden aus DDD nutzen, um fachliche Konzepte aus der Geschäftsdomäne zu identifizieren, die über eine API bereitgestellt werden sollen. Bei der Modellierung dieser Konzepte als Web-Ressourcen kann Schema.org helfen, das ein breites, erprobtes Vokabular für unterschiedlichste Domänen bereitstellt.

Beispiel: Kino-API

Um dies zu verdeutlichen, möchte ich zunächst mit einem Negativbeispiel beginnen. Anschließend werden wir die Domänenkonzepte heraus arbeiten und diese mit Hilfe von Schema.org in einer besseren Variante der API abbilden. Als Beispiel dient eine API zum Buchen von Kinotickets.

Die API könnte derart gestaltet sein, dass über /events/<id> Daten zum Ausstrahlungstermin eines Films abgerufen werden können:

GET /events/4711
{
  "id": "4711",
  "start": "2019-03-28 21:00",
  "movie_id": 14123,
  "movie_name": "Aquaman",
  "image": "static/images/aquaman.png",
  "price": 1600,
  "location_id": "123",
  "available": true,
  "tickets_left": 120,
  "rebate_tariff": "A-12",
  "hidden": false,
  "promote": true
}

Dieser Datensatz birgt zahlreiche Probleme:

  • Er vermischt mehrere Domänenkonzepte, wie Ausstrahlungstermin, Film, Preise, Verfügbarkeiten…
  • Er enthält eher technische Flags, ohne klare fachliche Bedeutung (hidden, available, promote)
  • Es ist unklar, ob und wie weitere Daten abgerufen werden können (z.B. Filmdetails)
  • Er exponiert interne Identifier (id, rebate_tariff, location_id)
  • Es fehlen Informationen (Zum Beispiel beim Preis: Ist 1600 eine ID oder ein Wert? Welche Einheit / Währung?)
  • Was stellt das verlinkte Bild (image) dar? Ist es für den Film generell gültig, oder nur für diesen Ausstrahlungstermin?

Solche Strukturen gelten bei bestehenden APIs oft als „historisch gewachsen“. Felder kamen hinzu, weil „Das Frontend“™ oder irgendein (Micro)service die Daten „nun einmal braucht“. Der Knackpunkt hierbei ist jedoch nicht das Alter einer API, sondern der mangelnde Fokus auf die Fachlichkeit. Auch ein API-first Ansatz kann sich in die völlig falsche Richtung entwickeln, wenn die Geschäftsdomäne aus dem Blickfeld gerät.

Wie sieht ein besserer Ansatz aus? Zunächst einmal gilt es zu überlegen, welche fachlichen Entitäten über eine API bereitgestellt werden sollen. Werkzeuge aus DDD können helfen, diese zu identifizieren. Hier sind es zum Beispiel:

  • Filme
  • Vorstellungstermine
  • Kinos
  • Preise / Angebote
  • Verfügbarkeiten

Weiterhin gilt es zu überlegen, welche Services über die API bereitgestellt werden:

  • Vorstellungstermine zu Filmen suchen
  • Angebote einsehen
  • Tickets buchen
  • Platzreservierung vornehmen

Einer guten API gelingt es, diese fachlichen Konzepte und Dienste nach außen zu kommunizieren, sodass diese zumindest leicht verständlich für Client-Entwicker:innen sind – im Idealfall sogar maschinenverständlich. Um dies zu erreichen, kann ein Blick in das Vokabular von Schema.org helfen, das bereits eine Vielzahl von Konzepten im Web modelliert. Für Angebote von Kinos werden wir fündig:

EntitätSchema.org Typ
Filmhttp://schema.org/Movie
Vorstellungsterminhttp://schema.org/ScreeningEvent
Angebothttp://schema.org/Offer
Kinohttp://schema.org/MovieTheater
Tickethttp://schema.org/Ticket
Sitzplatzhttp://schema.org/Seat
Reservierunghttp://schema.org/Reservation

In vielen Fällen muss man für die eigene API nicht das Rad neu erfinden, sondern kann auf bestehende und allgemein verständliche Konzepte zurückgreifen. Selbst in hoch-innovativen Geschäftsfeldern finden sich immer wieder allgemeine Konzepte, wie Preise, Personen, Orte, die sich über Schema.org abbilden lassen. Ein erfolgreiches API-Produkt überzeugt durch die angebotenen Daten und Services sowie leichte Integration. Es hält Clients nicht allein durch Kopplung an das eigene proprietäre Datenmodell bei sich (Vendor-lock-in).

Für die Nutzung von Schema.org in APIs bietet sich eine JSON-LD Repräsentation an. Mit Hilfe von JSON-LD können wir erreichen, dass unsere API diese fachlichen Konzepte in Form von klassischem JSON darstellen kann. Näheres habe ich an anderer Stelle bereits beschrieben.

Das Beispiel von oben können wir schrittweise als JSON-LD Datensatz wie folgt modellieren. Ein Ausstrahlungstermin lässt sich zunächst als http://schema.org/ScreeningEvent beschreiben:

{
   "@context": "https://schema.org/",
   "type": "ScreeningEvent",
   "startDate": "2019-03-21T20:00"
 }

Weiterhin benötigen wir Informationen über den ausgestrahlten Film. Dieser lässt sich über http://schema.org/workPresented, mit dem Ausstrahlungstermin verknüpfen. Ähnlich lässt sich der Ort über http://schema.org/location angeben:

{
  "@context": "https://schema.org/",
  "type": "ScreeningEvent",
  "startDate": "2019-03-21T20:00",
  "workPresented": {
    "type": "Movie",
    "name": "Aquaman",
    "image": "/static/images/aquaman.png"
  },
  "location": {
    "type": "MovieTheater",
    "name": "Kino City-Central"
  }
}

Hier werden nun der ausgestrahlte Film und das Kino klar als eigene fachliche Entitäten kommuniziert. Später können wir noch entscheiden, ob wir die Feldnamen von Schema.org 1:1 übernehmen, oder mittels JSON-LD-Kontext das Vokabular auf eigene Bezeichner abbilden. Ebenso können wir entscheiden, unter welchen URIs die Ressourcen ausgeliefert werden, ob wir die Daten des Films bzw. Kinos teilweise oder komplett einbetten, oder über deren IDs verlinken. Dies sind jedoch Implementierungsdetails der API, welche nachrangig betrachtet werden können.

Spannender sind zunächst noch Preise, Angebote und Verfügbarkeiten. Bei Schema.org findet sich das Konzept einer Offer bzw. AggregateOffer, welche zum Beispiel so aussehen könnte:

{
  "@context": "https://schema.org/",
  "type": "AggregateOffer",
  "price": 16,
  "priceCurrency": "EUR",
  "availability": "OnlineOnly",
  "offerCount": 120
}

Die Ressource kommuniziert klar den Preis und die Verfügbarkeit. Boolesche-Flags sind an keiner Stelle notwendig. Eine Offer ist entweder an einem ScreeningEvent vorhanden, oder nicht. Auch Sondertarife und Preisnachlässe lassen sich über dieses Konzept kommunizieren:

{
   "@context": "https://schema.org/",
   "type": "ScreeningEvent",
   // [...]
   "offers": [
      {
        "@type": "AggregateOffer",
        "price": 16,
        "priceCurrency": "EUR",
        "availability": "OnlineOnly",
        "offerCount": 120
      },
      {
        "type": "AggregateOffer",
        "name": "Rebate for students",
        "price": 12,
        "priceCurrency": "EUR"
      }
   ]
 }

Was wir aus dem Negativbeispiel bisher noch nicht berücksichtigt haben ist das promote-Flag. Es ist unklar, was damit überhaupt gemeint ist. Dies lässt sich nur mit den Fachverantwortlichen klären. Nehmen wir hier an, es dient dazu, bestimmte Filmvorstellungen besonders in den Vordergrund zu stellen, z.B. in einem Frontend prominent auf der Startseite zu platzieren. Dies hat jedoch fachlich überhaupt nichts mit der Vorstellung an sich zu tun, ist also fehl am Platz. Für solche Fälle ist es besser, getrennte Resource-Collections für unterschiedliche Use-Cases in der API anzubieten, z.B.:

Collection-URIBedeutung
/eventsLiefert alle aktuellen Filmvorstellungen
/promotionsLiefert besonders beworbene Filmvorstellungen

Auch wenn die eigene Domäne nicht zu 100% über Schema.org abgebildet werden kann, sollte man beim Design der API stets die Fachlichkeit im Blick behalten und die API so gestalten, dass fachliche Entitäten und Eigenschaften ausgeliefert werden. Auch sollte man genau prüfen, ob sich Konzepte wirklich nicht abbilden lassen, oder ob die Fachlichkeit in Wahrheit durch technische Details verdeckt wird. Implementierungsdetails einer Anwendung sollten keine Auswirkung auf deren API haben. Es kann auch sinnvoll sein, auf spezialisierte Vokabulare zurück zu greifen. Im eCommerce findet zum Beispiel GoodRelations breite Verwendung.

Entitäten die tatsächlich inhärent eigene Konzepte abbilden, können durch einen neuen Typen im unternehmenseigenen Vokabular aus der Ubiquitous Language bedient werden:

 {
   "@context": "https://enterprise.example/vocab/",
   "type": "Quuxbaz",
   "foobarization_level": 17
 }

Mit solchen Schöpfungen sollte man jedoch möglichst sparsam umgehen, da sie die Interoperabilität und allgemeine Verständlichkeit der API einschränken. Mindestens sollte das entsprechende Vokabular gut dokumentiert sein, idealerweise über den Abruf der zugehörigen URLs:

https://enterprise.example/vocab/Quuxbaz
https://enterprise.example/vocab/foobarization_level

Ruft man diese URLs im Browser ab, sollte man direkt auf der entsprechenden Dokumentationsseite landen. Auch diesbezüglich geht Schema.org mit gutem Beispiel voran und man darf sich gerne daran orientieren, um eine großartige Developer Experience zu schaffen, die sich positiv auf eine breite Adaption der API auswirkt.

Fazit

In diesem Artikel habe ich dargelegt, dass erfolgreiche API-Produkte sich auf die Geschäftsdomäne konzentrieren sollten. Ein Negativbeispiel hat verdeutlicht, welche Probleme mangelnder Fokus auf die Fachlichkeit birgt. Mit Hilfe von Schema.org haben wir ein besseres Design entwickelt. Abschließend habe ich gezeigt, dass die Grenzen dieses Ansatzes nicht bei Schema.org liegen. Bei Bedarf können spezialisierte oder unternehmenseigene Vokabulare hinzugezogen werden.

Angelo Veltens arbeitet als Web-Developer bei codecentric Hamburg. Er begeistert sich sowohl für die Frontend-Entwicklung mit JavaScript, als auch die Backend-seitige Entwicklung mit Frameworks wie Grails oder Spring Boot.
Testautomatisierung auf allen Ebenen von Unit- bis Akzeptanztests, sowie der Aufbau von Continuous-Delivery-Pipelines zählen dabei ebenfalls zu seinen Stärken.

Kommentare

  • Alexander Zeitler

    8. März 2019 von Alexander Zeitler

    Spannendes Beispiel – ich habe in einem Projekt um 2014 ähnliche Erfahrungen mit schema.org gemacht. Damals ging es um die Domäne „Leitstelle“. Da das Projekt international war, war es dank schema.org viel einfacher, mit Kollegen in Thailand die Ubiquitous Language sowie die API zu spezifizieren.

Kommentieren

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.