Mikroserwisy - jak uniknąć duplikacji kodu?

0

Pytanie bazuję na następujących założeniach:

  • Mikroserwisy są zazwyczaj wdrażane w ten sposób, że każdy serwis to osobne repo
    • Jednym z założeń mikroserwisów jest umożliwienie niezależnego od siebie wersjonowania i wdrażania komponentów aplikacji. Gdyby korzystać z "tradycyjnego" modelu, tj. jedno repo z wieloma projektami kompilowanymi jako biblioteki (np. .dllki), to każda zmiana w danej bibliotece pociąga za sobą konieczność podniesienia wersji i wdrożenia wszystkich bibliotek (oraz oczywiście samego exeka), które zależą od tej biblioteczki, która uległa zmianie. Nie dzieje się tak, jeśli zamiast bibliotek mamy osobne pliki wykonywalne, które są kompilowane niezależnie od siebie.
  • Mikroserwisy komunikują się ze sobą za pomocą serializacji i mechanizmów takich, jak REST API. Wymaga to, by komunikujące się ze sobą mikroserwisy korzystały z tego samego modelu danych

Przedstawiłem swój "mental model", bo wiele razy się orientowałem, że pytania, które chcę zadać są oparte na niepoprawnym rozumieniu zagadnienia - jeśli teraz też tak jest, to może dzięki temu uda się uniknąć konfuzji.

Teraz właściwe pytanie.

Załóżmy, że mamy mikroserwis A oraz mikroserwis B i A przesyła jakieś dane B.

Wobec tego w A będziemy mieć klasę jakąś taką:

public class ModelDanych
{
    public string JakisNapis {get; set;}
    public int JakasLiczba {get; set;}

    public List<PodModel> JakasKolekcja {get; set;}
}

public class PodModel
{
    public double // ...
    /*
        ...
    */
}

(Javowców przepraszam za C#, niestety ten język na razie znam najlepiej, z pewnością jednak zagadnienie jest analogiczne w Javie)

Serwis A zserializuje tę klasę do np. JSONa, wyśle ją do serwisu B, który otrzyma JSONa i zdeserializuje go do klasy jakiejś takiej:

public class ModelDanych
{
    public string JakisNapis {get; set;}
    public int JakasLiczba {get; set;}

    public List<PodModel> JakasKolekcja {get; set;}
}

public class PodModel
{
    public double // ...
    /*
        ...
    */
}

Tak, zrobiłem tu Ctrl+C Ctrl+V podczas pisania pytania. Analogiczny Ctrl+C Ctrl+V trzeba by zrobić podczas pisania tych mikroserwisów. A tymczasem Ctrl+C Ctrl+V jest operacją podobno zakazaną podczas programowania?

W przypadku monolitu zamiast serwisów A i B mielibyśmy biblioteki A i B i one zależałyby od klasy ModelDanych, która siedziałaby pewnie w jakiejś wspólnej bibliotece Modele.dll czy coś w tym stylu. Dlatego zawartość tej klasy nie byłaby przeklejona. A jak uniknąć duplikacji w przypadku mikroserwisów?

2

To akurat proste- od tego stosuje się paczki, np. w .Net za pomocą NuGet. Wtedy serwis A wystawia paczkę ze swoimi DTOs, a serwis B może tę paczkę zainstalować i używać DTO typy wystawione przez A. Pozwala to również lepiej kontrolować wersje modeli. To oczywiście w przypadku bardziej "tradycyjnych" metod komunikacji (HTTP, eventy itp). Np. w takim gRPC nie ma koniecznie potrzeby dzielenia się typami, ale dzieli się plikami *protobuf *które są swoistymi definicjami wiadomości (typów).

6

Jednak dokonałeś tu założenia. Założeniem jest że wszystkie mikroserwisy są w C#. Wtedy można jak najbardziej wystawiać paczki z DTO. Tak samo jak się robiło dla starego dobrego SOAP.
Co jednak jeśli drugi mikroserwice będzie np w Javie? Wtedy wystawianie paczek z DTO nie wystarczy. O czywiście można użyć jakiejś metody opisu DTO niezależnej od języka programowania i na tej podstawie generować DTO. W starym dobrym SOAP był to XML a konkretnie WSDL. Teraz jest multum schematów do wybrania jak Protocol Buffet, Thrift, AVRO czy zwykły JSON Scheme (oprócz oczywiście open api)
Mimo to często kończy się na klepaniu w klientach DTO raz jeszcze. Co ciekawe nie muszą być to te same klasy jak w oryginale. Clienty mogą nie używać wszystkich pól lub ( o zgrozo :) ) posiadać dodatkowe metody

0

Słuszna uwaga, faktycznie dokonałem takiego założenia- głównie dla tego że OP wspomniał C#. W przypadku mikroserwisów pisanych w różnych językach to z pomocą mogą przyjść narzędzia dla OpenAPI o których wspomniał @Pazura.

Chociaż prawda jest taka że przypadki systemu opartego o mikroserwisy, gdzie używa się różnych języków, to raczej mniejszość- najczęściej chyba spotykana w korpo-molochach.

3
YetAnohterone napisał(a):

A tymczasem Ctrl+C Ctrl+V jest operacją podobno zakazaną podczas programowania?

To tylko takie kłamstwo dla dzieci, jak to, że niebo jest niebieskie, albo nie ma pierwiastka z -1. ;)

A jak uniknąć duplikacji w przypadku mikroserwisów?

Generujesz klienta do Twojej zależności na podstawie OpenAPI: https://github.com/Azure/autorest

0

@YetAnohterone: przecież to główna zasada mikroserwisów: piszesz jakieś 10x więcej kodu, ale twój system jest dostosowany do wstecznie kompatybilnych zmian. Ctrl + C/V nie jest zakazane: po prostu zasady i podejścia wewnątrz jednego serwisu są diametralnie inne niż w ekosystemie mikroserwisów. Najlepiej to widać, gdy przyjrzysz się strukturom danych używanych w obu podejściach. W przypadku logiki wewnątrz serwisu chcesz używać mocnych typów, żeby twój kod był prosty i miał mało przypadków. Mocne typy sprawiają, że nie musisz robić wiele ifów np. czy wartość nie jest nullem. Zmiana czegokolwiek jest bardzo prosta, wystarczy użyć refactoru z IDE i poprawiać aż kompilator/testy przestaną krzyczeć. W przypadku komunikacji pomiędzy serwisami używamy "głupich" formatów danych takich jak JSON czy protobuf. W takim protobufie możesz zmienić nazwę pola albo listę na pojedyńczą wartość i nic się nie zepsuje, bo format wymusza na tobie dokładne sprawdzenie wszystkich możliwych przypadków.

0
  1. Jak technologia ta sama (np. JVM), to możesz opublikować klienta do swojego serwisu i drugi serwis weźmie go jako zalezność
  2. Jak technologie są różne to cudów nie ma, klienta trzeba będzie zrobić kilka razy
1
Shalom napisał(a):
  1. Jak technologie są różne to cudów nie ma, klienta trzeba będzie zrobić kilka razy

Może się robić sam - wystawiasz po jednej stronie OpenAPI, po drugiej zaciągasz i swagger genem tego klienta tworzysz. Albo jak to teraz modne, przygotowujesz openAPI i generujesz zarówno serwer, jak i klienta.

Kod klienta oczywiście będzie nadmiarowy, ale jak sądzę można z tym żyć. DRY stosuje się po to, żeby po kodzie nie rozłaziły się pasty jakiegoś kawałka i w razie konieczności jakiejś zmiany nie trzeba było przez 2 dni szukać wszystkich możliwych miejsc gdzie trzeba coś zmienić, a przez kolejne pół roku dowiadywać się które nie zostały jednak znalezione. Jeżeli zautomatyzujemy taki proces, to swagger file jest naszą zależnością, a dynamicznie generowany service stub, czy http client kawałkami kodu na które nie zwracamy większej uwagi przy utrzymaniu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1