Nicht erst seit dem Hype um Microservices ist Schnittstellendesign ein essenzieller Bestandteil von Softwareentwicklung. Doch jede noch so gut definierte Schnittstelle kann an einen Punkt kommen, an dem sie weiterentwickelt werden muss. Sei es nur, weil sich die Anforderungen geändert haben. Kommt man an diesen Punkt, stellt sich die Frage: Muss ich meine Schnittstelle versionieren? Wenn ja, wie gehe ich vor? Wie müssen sich die Clients der Schnittstelle verhalten, um nicht plötzlich (nach einem neuen Release des Servers) eine böse Überraschung zu erleben? Die Weiterentwicklung einer Schnittstelle wird leider initial häufig nicht mit bedacht. Hoher Wartungsaufwand für die Bedienung alter und neuer Schnittstellen sind die Folge. Wie kann ich das verhindern? Wie muss ich bei der Weiterentwicklung vorgehen, um alte Clients nicht inkompatibel werden zu lassen und dennoch nicht in der Versionierungshölle zu landen? Diese und weitere Fragen werden in dieser Session beantwortet. Dabei werden die Konzepte nicht nur in der Theorie beleuchtet. Am Beispiel von JAX-RS zeige ich, wie Versionierung in der Praxis realisiert werden kann, ohne sich im Support alter Versionen zu verlieren.
31. Tolerant Reader Pattern
Tolerant gegenüber unbekannten Feldern
Umgang mit x-extensible-enum
Tolerant gegenüber unbekannten Statuscodes
http://zalando.github.io/restful-api-guidelines
HTTP Status 301 folgen
37. Und was ist, wenn der Client kein
Tolerant Reader ist?
38. PROJEKTIONEN
// Client kann entscheiden, welche Felder er bekommen möchte
// Facebook Style
GET /addresses/3?fields=street,city
GET /addresses/3?fields=street.name,street.number,city
// LinkedIn Style
GET /addresses/3?fields=street:(name,number),city
39. OK, so soll sich
der Client verhalten.
Aber was ist mit dem Server?
40. Photo by Irene Fertik, USC News Service. Copyright 1994, USC.
„Be conservative in what you do,
Be liberal in what you accept
from others“
RFC 793, Robustness Principal (John Postel)
41. Es darf nichts entfernt werden
Keine Veränderung von Verarbeitungsregel
Optionales darf nie Required werden
http://zalando.github.io/restful-api-guidelines
Alles was hinzugefügt wird, muss optional sein
54. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName;
private String streetNumber;
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
Wird mit null aufgerufen
Wird gar nicht aufgerufen
55. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName; // = null
private String streetNumber; // = null
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
56. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName; // = null
private String streetNumber; // = null
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
Simple Solution:
Don‘t use PATCH at all
57. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName; // = null
private String streetNumber; // = null
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
Advanced Solution:
Use initial values
58. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName = "<initial value>";
private String streetNumber = "<initial value>";
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
59. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName = "<initial value>";
private String streetNumber = "<initial value>";
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
Wird mit null aufgerufen
Wird gar nicht aufgerufen
60. MERGE PATCH UND NULL VALUES
public class Street {
private String streetName; // = null
private String streetNumber = "<initial value>";
public void setName(String name) { setStreetName(...
public void setNumber(String number) { setStreetNum...
}
Wird mit null aufgerufen
Wird gar nicht aufgerufen
69. Was ist daran jetzt besser als an
einer Mapping-Schicht?
70. Migrations-Strategie für jede Attributänderung
Code für Migration sehr nah an der Schnittstelle
Migrations-Regeln innerhalb des Objekts
Code unabhängig
80. CONSUMER-DRIVEN CONTRACT TEST
• Versteht der Provider die Anfragen?
• Liefert er die erwarteten Antworten?
Consumer
Provider
Stub
Provider
Request
Generator
• Erzeugt der
Consumer die
erwarteten Anfragen?
• Kann er mit den
Antworten umgehen?
81. CONSUMER-DRIVEN CONTRACT TEST
• Startet Provider
• Interaktionen abspielen Consumer
Provider
Stub
Provider
Request
Generator
• Unit-Tests
• Responses auf
erwartete Requests
Interactions
82. CONSUMER-DRIVEN CONTRACT TEST
• Versteht der Provider die Anfragen?
• Liefert er die erwarteten Antworten?
Consumer
Provider
Stub
Provider
Request
Generator
• Erzeugt der
Consumer die
erwarteten Anfragen?
• Kann er mit den
Antworten umgehen?
87. Version 2.0
ist nur inkompatibel zu 1.0!
Version 2.0 ist identisch zu 1.x!
Das erleichtert das Mapping
zwischen den Versionen!
88. Ein solcher Versionssprung ist
nicht anforderungsgetrieben,
sondern viel besser und
langfristiger planbar
89. • Über URL-Pfad
/v2/addresses
• Über Query-Parameter
/addresses?version=v2
• Über Version-Header
Api-Version: v2
• Über Version-Attribut am Media-Type
application/xml;version=v2
• Über Media-Type
application/vnd.de.openknowledge+v2+json
ERMITTELN DER VERSION