Verzování webových služeb, SOAP

Před časem jsem napsal lehký úvod do SOA governance. Chtěl bych se k tomuto tématu vracet a prezentovat principy, které postupně zavádíme na projektu. Momentálně nás daleko víc pálí věci, které spadají do design fáze služeb, takže řešíme věci jako verzování, reusabilita, granularita služeb apod.

Aspekt, který jsme řešili jako první (a nyní už i zavedli v praxi) je verzování služeb. Jelikož používáme řešení založené na SOAP webových službách (jak jinak v enterprise :-) podíval bych se právě na toto téma. Jestliže budu dále mluvit o webových službách (WS), mám tím vždy na mysli WS založený na SOAP.

Proč?

Otázka je, proč vlastně webové služby verzovat? Dejme tomu, že z nějakého důvodu chceme, na určitém prostředí, držet více verzí jedné služby. Tím důvodem může být např. to, že už máme nějaké stávající konzumenty dané služby a zároveň chceme touto službou poskytnout nové (business) funkcionality. Stávající klienti ovšem nemohou (nebo také odmítnou) přejít na novou verzi služby. Starou verzi služby tedy necháme funkční a zároveň nasadíme verzi novou. V případě SOA by toto mělo být definováno/podpořeno nějakou politikou, např. podpora více verzí služeb.



Kontrakt

Jak z hlediska SOA, tak z hlediska klienta se webová služba (a její konzumace) točí kolem kontraktu. Kontrakt, jako takový, se skládá z několika částí. Jednak definuje nějaké designové věci jako datové typy, zprávy a rozhraní (ve smyslu OOP). Dále definuje technologické záležitosti, tj. jaké se používají protokoly, jaké jsou endpointy služby. A konečně jsou to nefunkční požadavky.

Pro SOAPovské služby je tímto kontraktem WSDL (Web Service Definition Language). Pokud pomineme nefunkční požadavky (které stejně nejsou definovány ve WSDL namespacu, ale jinde), má WSDL následující strukturu (zeleně jsou designové části, červeně implementační):

  • types - element definující pomocí XML Schema datové typy používané ve zprávách.
  • message - abstraktní definice přenášených dat (zpráv), sestavená z typů definovaných v předešlém elementu. Každá zpráva se skládá z jedné a více logických částí (parts). Pokud je částí ve zprávě více, jde o službu založenou na RPC.
  • portType - množina abstraktních operací, víceméně odpovídá rozhraní v OOP. Každá operace (metoda) by měla odkazovat vstupní a výstupní zprávu. Operace může být jedním ze čtyř typů: Request-response (klasika), One-way (obsahuje pouze vstupní zprávu), Notification (obsahuje pouze výstupní zprávu) a Solicit-response (endpoint pošle zprávu a očekává odpověď).
  • binding - sváže definovaný portType s konkrétním protokolem (SOAP, HTTP, JMS) a formátem zpráv (např. Document/literal, RPC/encoded).
  • port - definuje endpoint služby.
  • service - množina souvisejících portů.

Sémantika verzování

Není potřeba znovu-vymýšlet-kolo. Archetypem verzování je formát <major>.<minor>.<micro>. U služeb nás micro verze nebude moc zajímat - je vyhrazená pro implementační změny, které neovlivňují vyšší řády verze a nijak se tedy nepromítají do kontraktu. minor verze je vyhrazená pro změny rozhraní, které jsou zpětně kompatibilní. No a major verze indikuje změnu rozhraní, která není zpětně kompatibilní, tj. rozbíjí kontrakt.

Změny, které jsou zpětně kompatibilní:
  • přidání nové operace do služby,
  • přidání nového XML typu do schématu.

Změny, které nejsou zpětně kompatibilní:
  • odebrání operace ze služby,
  • přejmenování operace,
  • změna XML typů a atributů zprávy,
  • změna namespace.

Jak?

Jako best-practice se uvádí, že verzování by pro konzumenty mělo být explicitní, tj. uvádět číslo verze v elementech, URL apod. Co všechno v kontraktu verzovat, může být předmětem diskuzí na konkrétním projektu a technologiích, nicméně dá se vyjít z těchnto pravidel:
  1. Vkládat major a minor verzi do názvu WSDL souboru: MyService-v1.2.wsdl.
  2. Vkládat major verzi do targetNamespace WSDL souboru:
    <definition
      targetNamespace=
          "http://sw-samuraj.cz/ws/MyService-v1"
      xmlns="http://schemas.xmlsoap.org/wsdl/">
  3. Vkládat major a minor verzi do portType elementu:
    <portType name="MyServicePort-v1.2">
  4. Vkládat major a minor verzi do service elementu:
    <service name="MyService-v1.2">
  5. Vkládat major verzi do endpointu služby:
    <soap:address
      location=
        "http://sw-samuraj.cz/myService/v1"/>
Pokud bychom si ukázali celé WSDL, vypadalo by takto:
<?xml version="1.0" encoding="UTF-8"?>
<definitions
targetNamespace=
"http://sw-samuraj.cz/ws/MyService-v1"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<types>
<xsd:schema
targetNamespace=
"http://sw-samuraj.cz/ws/MyService-v1">
<!-- schema omitted -->
</xsd:schema>
</types>

<!-- messages omitted -->

<portType name="MyServicePort-v1.2">
<!-- operations omitted -->
</portType>

<!-- binding omitted -->

<service name="MyService-v1.2">
<port binding="MyServiceSOAP"
name="MyServiceSOAP">
<soap:address
location="http://sw-samuraj.cz/myService/v1"/>
</port>
</service>

</definitions>

Závěr

Verzování poměrně úzce souvisí s dalším SOA governance aspektem a sice životním cyklem služeb (což je téma, na které se podíváme někdy příště). To že držíme na prostředí (typicky produkci) více verzí jedné služby má samozřejmě svoje náklady a ideálním stavem je, když služba běží pouze v jedné (nejaktuálnější) verzi.

Se správou více verzí nám může pomoci vhodný nástroj - SOA governance repository, která drží o konrétní verzi služby různá meta-data a mmj. také to, v jaké životní fázi se služba nachází, nebo kdo ji konzumuje (a koho musíme notifikovat o penzionování služby).