Consumer-Driven Contracts mit Pact – Teil 1: Einführung

29.09.2020

Ein Beitrag von Tobias Eberle und Sarah Mildenberger
In diesem ersten Teil der Blogpostreihe zu Consumer-Driven Contracts werden zunächst Begrifflichkeiten erläutert und das Tool Pact vorgestellt. Im zweiten Teil steht die Implementierung von Consumer-Driven Contracts mit Pact und Java im Fokus.

TL;DR

Consumer-Driven Contracts und insbesondere der Einsatz von Contract Testing kann eine sinnvolle Erweiterung jeder Test-Suite sein. Die Definition von Anforderungen seitens der Service-Konsumenten an die bereitstellende API sorgt für Transparenz über deren Erwartungen. Das stärkt zum einen die Kommunikation zwischen den Entwicklungsteams einzelner Services, zum anderen bekommen Service-Provider ein besseres Verständnis dafür, was ihre Konsumenten tatsächlich benötigen.

 

Contract Testing stellt außerdem technisch sicher, dass die fachlich definierten Schnittstellenverträge auch tatsächlich von allen beteiligten Systemen eingehalten werden. Sollte also trotz der durch den Einsatz von Consumer-Driven Contracts gesteigerten Transparenz und der geförderten Kommunikation zwischen den Entwicklungsteams ein Breaking Change durch ein Partnersystem verursacht werden, wird der Change technisch unterbunden. Es kann nicht mehr deployed werden, bis die Kompatibilität mit der Schnittstelle wieder gewährleistet ist.

Die Problematik: Breaking Api Changes

Als Beteiligter eines Softwareentwicklungsprojekts waren Sie vermutlich schon einmal in der Situation, dass eine Schnittstelle zu einem Partnersystems plötzlich nicht mehr wie erwartet funktionierte. Oft geschieht das, weil die API des Partnersystems ohne Absprache oder Vorankündigung durch den Schnittstellenpartner geändert wird – der damit einen Breaking Change der API verursacht. In der Regel fällt dies im Rahmen des Deployments bei den Integrationstests auf. Im schlimmsten Fall jedoch wird die betroffene API in der Test-Suite erst gar nicht berücksichtigt. Dann wird erst bemerkt, dass die benötigte Schnittstelle nicht mehr funktioniert, wenn es zu spät ist – im Produktivbetrieb.

Doch selbst wenn im Rahmen der Integrations- oder Smoke-Tests auffällt, dass Fehler auftreten, ist das alles andere als optimal. Eine (unter Umständen aufwändige) Analyse muss zunächst zeigen, ob es sich bei der Fehlerursache um einen Breaking Change des Partnersystems oder um ein Problem der Infrastruktur handelt. Wäre es hier nicht schön, wenn es einen Mechanismus gäbe, der alle Partnersysteme zwingt, ihre Schnittstellenänderungen rechtzeitig bekannt zu machen – und der unverhandelte Schnittstellenänderungen verhindert? Auf diese Weise könnten Breaking API Changes als Fehlerursache ausgeschlossen werden, da sie gar nicht mehr passieren könnten.

Eine Lösung: Consumer-Driven Contracts

Die gute Nachricht: Diesen Mechanismus gibt es! Hierfür bietet sich die Integration von technischen Schnittstellenverträgen („Interface Contracts“, kurz „Contracts“) in den Softwareentwicklungsprozess an, die automatisiert gegen die Partnersysteme validiert werden können („Contract Testing“). Darüber hinaus lässt sich durch den Einsatz solcher Interface Contracts und entsprechender Contract Tests das Prinzip der „Consumer-Driven Contracts“ (CDC) realisieren.

Die zugrunde liegende Idee von Consumer-Driven Contracts ist, dass Consumer (Aufrufer einer API) zunächst ihre Erwartungen an die API eines Providers (den Bereitsteller der API) formulieren. Diese Erwartungen gelten dann als Grundlage für die Definition von Schnittstellenverträgen (Contracts) zwischen den beteiligten Systemen, deren Einhaltung über sogenannte Contract Tests technisch geprüft werden kann. [1]

Ein Contract ist eine Sammlung von Interaktionen zwischen API-Consumer und API-Provider. Eine Interaktion beschreibt die Struktur eines Requests und der zugehörigen Response zwischen beiden Akteuren. Interaktionen beschreiben jedoch nicht das erwartete Verhalten einer Software, sondern betrachten ausschließlich statische Aspekte bei der Kommunikation mit einer Schnittstelle. Sie definiert also, WELCHE Funktionalität oder Ressource WIE angefordert werden kann.

Darüber hinaus sorgen Contract Tests dafür, dass ein gemeinsames Verständnis über die Struktur der zwischen den Partnersystemen ausgetauschten Nachrichten sichergestellt ist und diese zentral dokumentiert wird. Ein weiterer Vorteil von CDC ist, dass sich der API-Provider frühzeitig mit den verschiedenen Bedürfnissen etwaiger Konsumenten arrangieren kann, um den Anfragen aller API-Consumer bestmöglich gerecht werden zu können.

Tools

Generell ist für die Implementierung von CDC kein spezielles Tool notwendig. Jedoch erleichtern Werkzeuge wie Pact, Spring Cloud Contract, etc. die Implementierung von CDC. Sie stellen einen technischen Rahmen für die Spezifizierung und die Sicherstellung der Einhaltung der Schnittstellenverträge durch den Einsatz von Contract Tests bereit. Die Contract Tests werden als Unit-Tests – und nicht mehr erst als Integrationstests – ausgeführt und stellen so frühzeitig im Entwicklungs-Zyklus die Einhaltung der Schnittstellenverträge sicher.

Kontinuierliche Prüfung der Einhaltung der Contracts

Da Contract Tests als Unit-Tests realisiert werden, können sie problemlos in ein CI/CD-Umfeld integriert werden. So können APIs regelmäßig und automatisiert auf Breaking Changes geprüft werden. Ohne den Einsatz von Contract Tests wäre frühestens zur Zeit der Ausführung von Smoke- bzw. Integration-Tests zu erkennen, dass eine Schnittstelle nicht mehr kompatibel ist – also dann, wenn tatsächlich ein Request gegen den betroffenen API-Endpunkt gesendet wird.
Die Integration von Contract Tests in den Softwareentwicklungsprozess zwingt die Schnittstellenpartner in einer Systemlandschaft zudem „ganz nebenbei“ zu einer stärkeren Kommunikation, da alle Beteiligten an der Gestaltung und Einhaltung der Schnittstelle mitarbeiten müssen, um auch selbst deployfähig zu bleiben.

Das gibt es zu bedenken

Diese Kommunikation ist auch deshalb wichtig, weil bei Consumer-Driven Contracts die Consumer „bestimmen“, welche Schnittstellen sie haben möchten. Stellen viele Consumer ohne Absprache Anforderungen an die API, kann es sein, dass der Provider diesen Anforderungen nicht gerecht werden kann. Zusätzlich wird die Komplexität, mit Änderungen umzugehen, von den Consumern auf den Provider verlagert.
Der Provider verliert außerdem seine eigene Flexibilität, wenn er sich ausschließlich den Anforderungen und Änderungswünschen der verschiedenen Consumer anpassen muss.

Die Implementierung und Pflege von CDC bedeutet zunächst einen Mehraufwand, der abschreckend wirken kann. Gerade bei großen Projekten mit mehreren Parteien kann sich dieser Mehraufwand jedoch lohnen, da die Konsistenz von Schnittstellen nicht mehr manuell sichergestellt werden muss. Der Einsatz von CDC sollte daher zumindest in Betracht gezogen und Aufwand gegen Nutzen abgewogen werden.

Contract Testing mit Pact

Pact (docs.pact.io) bietet die Möglichkeit, Schnittstellenverträge in einer domänenspezifischen Sprache (Pact DSL) zu erstellen. Zur Definition dieser DSL unterstützt Pact verschiedene Programmiersprachen, darunter Java, Python, Ruby, JavaScript und C++ [2].

Ablauf

  1. Der Consumer erstellt einen technischen Schnittstellenvertrag („Contract“), der die von ihm erwarteten Interaktionen mit der Schnittstelle enthält.
  2. Die erwarteten Anfragen aus dem Contract werden im Rahmen eines Unit-Tests an einen Mock-Provider (Mock-Server, der vom Pact-Framework auf Grundlage der Contracts für die Unit-Tests bereitgestellt wird) gestellt. Dabei wird ein Contract (Pact)-File erstellt, das die Bedingungen des Schnittstellenvertrags enthält.
  3. Dieses File wird an den Provider übergeben.
  4. Auf Basis des Contracts wird auf Provider-Seite ein Consumer simuliert, der die erwarteten Anfragen an den Provider stellt. Dies geschieht im Rahmen von Unit-Tests, die aus dem Contract generiert werden.
  5. Wenn alle Unit Tests
    1. erfolgreich sind, kann der Provider seine Änderungen deployen, da nun klar ist, dass der Consumer mit ihm kommunizieren und seine Antworten verarbeiten kann.
    2. nicht erfolgreich sind, darf der Provider seine Änderungen nicht veröffentlichen, da der Consumer nicht mit ihm kommunizieren und/oder seine Antworten nicht verarbeiten könnte. Der Provider muss seine API nun so anpassen, dass sie dem Schnittstellenvertrag entspricht und die Unit Tests nicht mehr fehlschlagen.

 

Ablauf von Consumer-Driven-Contracts mit Pact
Ablauf von Consumer-Driven Contracts mit Pact [3]

Austausch von Pact-Files

Der beschriebene Ablauf lässt offen, wie die Contracts ausgetauscht werden. Theoretisch könnten sie einfach „von Hand“ (per Email, über ein SCM, …) an die beteiligten Parteien verteilt werden, was jedoch zum einen wieder einen manuellen Verwaltungsaufwand schafft und zum anderen die gewünschte technische Unterstützung von CDC doch wieder von manuellen Eingriffen abhängig macht. Um diesen Prozess zu vereinfachen, stellt das Framework den sogenannten Pact Broker als zusätzliche Infrastrukturkomponente bereit. Über diesen Broker werden die Contracts vom Consumer veröffentlicht und vom Provider abgerufen, sodass die Pact-Files nicht von Hand ausgetauscht werden müssen. Außerdem hat der Broker den Vorteil, dass der Provider die Ergebnisse seiner Verifizierung an den Broker zurückspielen kann, wo sie wiederum vom Consumer abgefragt werden können. Auf diese Weise findet auch der Consumer heraus, ob die vom Provider angebotene Schnittstelle seinen Anforderungen entspricht und er deployen kann. [4]

 

Ausblick

In diesem ersten Teil der Blogpost-Reihe zu Consumer-Driven Contracts wurde das Grundprinzip von Consumer-Driven Contracts dargelegt und Pact als Werkzeug zur Erstellung und Ausführung von Contract Tests vorgestellt. Der nächste Teil der Serie wird sich um die konkrete Implementierung von Contract Tests mit Pact in Java anhand eines Beispiel-Projekts drehen. Dabeibleiben lohnt sich also!


Quellen

Titelbild: https://raw.githubusercontent.com/pact-foundation/pact-logo/master/media/logo-black.png

[1] https://www.martinfowler.com/articles/consumerDrivenContracts.html

[2] https://docs.pact.io/implementation_guides

[3] https://pactflow.io/how-pact-works/?utm_source=ossdocs&utm_campaign=getting_started#slide-5

[4] https://docs.pact.io/pact_broker/advanced_topics/provider_verification_results

Zurück zur Übersicht

Kommentar verfassen

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

*Pflichtfelder

*