Warstwa kompatybilności – najłatwiejszy sposób na zapewnienie kompatybilności wstecznej
W artykule Kompatybilność wsteczna – co to oznacza i jak o nią dbać? opowiedzieliśmy już sobie czym jest kompatybilność wsteczna. Z kolei w Rozwój aplikacji bez rewolucji u klientów pokazaliśmy, czym grozi wprowadzanie zmian niekompatybilnych wstecz.
Tym razem skupimy się na jednym z wzorców pomocnym w zapewnieniu kompatybilności wstecznej jakim jest warstwa kompatybilności.
Metaforycznie
Jaki mamy problem?
Na początku zastanówmy się jak zapewnić kompatybilność wsteczną na bardziej życiowym przykładzie.Załóżmy że:
- chcemy wyremontować odcinek drogi ekspresowej,
- przejeżdża nią ok. 20 000 samochodów dziennie,
- kompatybilność wsteczną definiujemy jako przejezdność na tym odcinku, żeby kierowcy mogli kontynuować podróż,
- chcemy umożliwić kierowcom dalszą podróż bez większych problemów.
Co możemy zrobić?
Rozwiązanie w tym przypadku jest dość intuicyjne. Wystarczy wyznaczyć objazd. Przy jego wyznaczaniu musimy wziąć pod uwagę kilka spraw. Po pierwsze upewnić się, że wszystkie samochody (nawet te o wysokości 4 metrów) będą w stanie tamtędy przejechać. Co więcej musimy się również upewnić, że objazd będzie dobrze oznaczony, żeby nie wprowadzać kierowców w błąd.
To rozwiązanie możemy nazwać warstwą kompatybilności. Pozwala ono na:
- swobodne zmiany w ramach głównego interfejsu (drogi),
- zapewnienie ciągłości ruchu pojazdów.
Technicznie
Przejdźmy jednak do bardziej technicznych rozważań związanych z rozwojem oprogramowania.
Jaki mamy problem?
Załóżmy, że:
- rozwijamy system używany przez wielu klientów. Może to być zarówno wewnątrz-firmowy mikroserwis lub też usługa dostępna przez API, którą nasza firma sprzedaje innym (np. bramka płatności).
- chcemy wprowadzić sporą zmianę do systemu, która z definicji jest niekompatybilna wstecz. Przykładem może być bramka płatności, gdzie przy starcie płatności decydujemy się rozdzielić od siebie start płatności od definiowania produktów, za które płacimy. Zmiana ta ma na celu możliwość reużywania przez system wcześniej zdefiniowanych produktów. Oczywistym jest, że systemy korzystające z obecnego interfejsu nie implementują niczego takiego, a w kolejnej wersji chcielibyśmy tego już wymagać.
- chcielibyśmy nie popsuć integracji pomiędzy naszymi obecnymi klientami.
Co możemy zrobić?
Również w tym przypadku dobrym rozwiązaniem będzie wprowadzenie warstwy kompatybilności.
Polega to na tym, że w głównym systemie modyfikujemy interfejsy w docelowy sposób, jednak aby zachować kompatybilność wsteczną pozostawiamy możliwość rozpoczęcia płatności w stary sposób. Pokazuje to na diagram poniżej.
W przykładzie widać, że zostaje stworzony osobny komponent w celu zachowania kompatybilności z poprzednią wersją. Tak więc systemy, które nie zmigrują się do nowego rozwiązania będą nadal korzystały z pojedynczego zapytania.
Może to zostać zrealizowane na różne sposoby. Zarówno jako stworzenie zupełnie osobnego serwisu (tylko do tego celu) lub jako wydzielona cześć jednego API. To o czym jednak zawsze trzeba pamiętać, to ustalenie strategii wyjścia (rezygnacji) z warstwy kompatybilności. Czyli kiedy będziemy mogli ją skasować.
Warstwa kompatybilności to przydatne rozwiązanie, jednak nie ma nic za darmo.
Wróćmy na chwilę do remontowanej drogi. Tu sprawa strategii wyjścia jest bardzo prosta – kończymy przebudowę głównej trasy, na którą wraca cały ruch, usuwane są znaki i ewentualne modyfikacje stworzone na potrzeby objazdu. Co więcej, oczywiste jest, że nikt nie będzie próbował korzystać z okrężnej drogi, gdy główna droga będzie już przejezdna. W świecie programowania to nie jest już takie pewne, bo niektórzy klienci nie są gotowi na dopłacanie do zmian po swojej stronie. Można powiedzieć, że dla nich stara droga mogłaby wcale nie być remontowana (jeśli tylko zmiany nie wynikają np. z luk bezpieczeństwa bądź przepisów prawa). Może się zatem okazać, że warstwa kompatybilności zostanie z nami na długo.
Jaki jest koszt warstwy kompatybilności?
Głównym kosztem realizacji warstwy kompatybilności jest potrzeba jej późniejszego utrzymania. To może się wiązać nie tylko z utrzymaniem samego endpointu na API, ale również (w niektórych przypadkach) z utrzymaniem całych procesów, z których zrezygnowaliśmy. Idąc dalej, może oznaczać spory koszt, ale zawsze w takim przypadku warto rozważyć alternatywę. Tutaj alternatywą jest na przykład wersjonowanie całej aplikacji, co jeszcze bardziej zwiększa ilość starego kodu do utrzymania.
Warto też zawsze zastanowić się, czy nie da się naszego wymagania zrealizować w taki sposób, żeby jednak docelowe rozwiązanie było kompatybilne wstecz, już na etapie projektu.
W naszym przypadku dałoby się to zrobić poprzez wprowadzenie dodatkowego pola w endpoincie rozpoczęcia płatności, które wskazywałoby id produktu i służyłoby do reużywania już zdefiniowanych produktów. Wspominam o tym w ramach ciekawostki i pewnego rodzaju wyzwania, ponieważ zapewnienie kompatybilności wstecznej nie jest intuicyjne, ale często jest możliwe. Inne strategie zachowania kompatybilności wstecznej opisuję w artykule Kompatybilność wsteczna – co to oznacza i jak o nią dbać?