Ostatnio na blogu sporo o SOA. W poprzednim wpisie, poruszyłem temat kompatybilności, teraz czas na coś, co ma na celu zminimalizowanie ilości różnych wersji usług. Najprostszym sposobem na uniknięcie problemów z kompatybilnością jest po prostu nie wprowadzenie niekompatybilnych zmian.
Consumer-driven contracts to prosty sposób, aby mieć pod kontrolą śledzenie zmian oraz ich wpływ na konsumentów naszej usługi.
Zwykle usługi posiadają pewien schemat (schema) czyli po prostu kontrakt. Najbardziej restrykcyjną polityką jest dopasowywanie kontraktu posiadanego przez klienta z tym dostępnym aktualnie po stronie serwera. Jeśli nie są one takie same, wtedy uznawane jest, że klient jest niekompatybilny z serwerem.
Powyższa strategia jest nieco zbyt restrykcyjna. W końcu dodanie nowej metody do API nie powinno mieć wpływu na klientów. Dlatego zwykle uznaje się, że schemat (schema) można rozszerzać o nowe wpisy czy pola. Jeśli np. encja Person zawiera FirstName oraz LastName, wtedy dodanie nowej właściwości do niej (np. MiddleName) nie powinno mieć znaczenia. W końcu taka zmiana, nie powinna zaszkodzić aktualnym klientom.
Co jednak z usunięciem pola? Taka zmiana w standardowym SOA jest uznawana za łamiącą kompatybilność i w przypadku semantycznego wersjonowania, powinno zwiększyć się segment “Major”. Jest to oczywiście naturalne i logiczne ponieważ istnieje ryzyko, że ktoś korzysta np. z FirstName i usunięcie tego pola jest niedopuszczalne.
Consumer-driven contracts wychodzą z założenia, że często nawet usunięcie pola nie powinno wiązać się ze zmianą “major”. Jeśli nikt z niego aktualnie nie korzysta, to po co tym martwić się? Wystarczy, że będziemy wiedzieć jak nasz serwis jest używany. Konsumenci kształtują nasze API, stąd nazwa consumer driven contracts.
Klienci definiują zatem ich wymagania za pomocą testów jednostkowych. Test jednostkowy może sprawdzać czy dane pole występuje w schema.
Następnie serwis bierze napisane testy przez konsumentów i wykonuje je (jako część CI) na realnym serwisie. Jeśli jakieś pole zostało usunięte, a było wykorzystywane przez jednego z konsumentów, test zakończy się oczywiście błędem. Po stronie serwisu możemy zatem dokonać jakichkolwiek zmian (usunięcie, dodanie nowych pól itp) dopóki żaden z testów “konsumenckich” nie zostanie popsuty.
Konsumenci zwykle piszą klasyczne testy jednostkowe, które wykonywane są na mock’u, a nie realnym serwisie. Serwis z kolei, zbiera wszystkie testy konsumentów i wykonuje normalny, integracyjny test. Jakakolwiek modyfikacja API jest teraz bezpieczna i przewidywalna jeśli chodzi o rezultat.
W jaki sposób piszemy testy to już zależy od konkretnego API. Możemy sprawdzać XML\JSON czy dane pole istnieje. W zależności od konkretnego protokołu inaczej to będzie wyglądać. Cel jest zawsze ten sam – wyrazić jakie pola i elementy są wymagane dla danego konsumenta i ich zmiana musi zakończyć się wydaniem nowej wersji ze zmianą “major”.
Skąd weźmiemy testy napisane przez naszych klientów?
Jeśli np. klientem jest inna firma, a nasze API publiczne ?
To oznacza, że jeśli ktoś chce abyśmy dbali o kompatybilność z nim to musi nam dostarczyć swoje testy ?
@Wojciech:
To nadaje sie bardziej do microserwisow, ktore zwykle powinny byc wewnetrzne.