Co to jest warstwa antykorupcyjna i jak z niej korzystać?

151

Próbuję dowiedzieć się, co tak naprawdę oznacza warstwa antykorupcyjna. Wiem, że to sposób na przejście / obejście starszego kodu lub złych interfejsów API. Nie rozumiem, jak to działa i co sprawia, że ​​jest to czyste oddzielenie od niepożądanej warstwy.

Przeprowadziłem pewne wyszukiwanie, ale nie mogę znaleźć żadnych prostych przykładów ani wyjaśnień, więc szukam kogoś, kto to rozumie i może wyjaśnić za pomocą prostych przykładów. Odpowiedź, która zaspokoi moje pytanie, powinna być prosta (niekoniecznie krótka) i zawierać zrozumiałe przykłady wdrożenia i użycia.

Zobacz to pytanie w moim przypadku użycia.

znane
źródło

Odpowiedzi:

147

Wyobraź sobie, że musisz użyć kodu innej osoby, który został zaprojektowany w sposób pokazany poniżej:

    class Messy {
        String concat(String param, String str) { /* ... */ }
        boolean contains(String param, String s) { /* ... */ }
        boolean isEmpty(String param) { /* ... */ }
        boolean matches(String param, String regex) { /* ... */ }
        boolean startsWith(String param, String prefix) { /* ... */ }
    }

Teraz wyobraź sobie, że dowiadujesz się, że Twój zależny od niego kod wygląda następująco:

String process(String param) {
    Messy messy = new Messy();
    if (messy.contains(param, "whatever")) {
        return messy.concat(param, "-contains");
    }
    if (messy.isEmpty(param)) {
        return messy.concat(param, "-empty");
    }
    if (messy.matches(param, "[whatever]")) {
        return messy.concat(param, "-matches");
    }
    if (messy.startsWith(param, "whatever")) {
        return messy.concat(param, "-startsWith");
    }
    return messy.concat(param, "-whatever");
    // WTF do I really need to repeat bloody "param" 9 times above?
}

... i chcesz ułatwić korzystanie, w szczególności, aby pozbyć się powtarzalnego używania parametrów, które po prostu nie są potrzebne w Twojej aplikacji.

Okej, więc zacznij budować warstwę antykorupcyjną.

  1. Po pierwsze, upewnij się, że twój „główny kod” nie odnosi się Messybezpośrednio. Na przykład organizujesz zarządzanie zależnościami w taki sposób, aby próba uzyskania dostępu Messynie powiodła się.

  2. Po drugie, tworzysz dedykowany moduł „warstwy”, który jako jedyny uzyskuje dostęp Messyi udostępniasz go swojemu „głównemu kodowi” w sposób, który jest dla ciebie bardziej sensowny.

Kod warstwy wyglądałby następująco:

    class Reasonable { // anti-corruption layer
        String param;
        Messy messy = new Messy();
        Reasonable(String param) {
            this.param = param;
        }
        String concat(String str) { return messy.concat(param, str); }
        boolean contains(String s) { return messy.contains(param, s); }
        boolean isEmpty() { return messy.isEmpty(param); }
        boolean matches(String regex) { return messy.matches(param, regex); }
        boolean startsWith(String prefix) { return messy.startsWith(param, prefix); }
    }

W rezultacie Twój „główny kod” nie zadziera z Messy, używając Reasonablezamiast tego około:

String process(String param) {
    Reasonable reasonable = new Reasonable(param);
    // single use of "param" above and voila, you're free
    if (reasonable.contains("whatever")) {
        return reasonable.concat("-contains");
    }
    if (reasonable.isEmpty()) {
        return reasonable.concat("-empty");
    }
    if (reasonable.matches("[whatever]")) {
        return reasonable.concat("-matches");
    }
    if (reasonable.startsWith("whatever")) {
        return reasonable.concat("-startsWith");
    }
    return reasonable.concat("-whatever");
}

Zauważ, że wciąż jest trochę bałaganu z Messytym bałaganem, ale teraz jest on ukryty dość głęboko w środku Reasonable, dzięki czemu Twój „główny kod” jest dość czysty i wolny od uszkodzeń, które zostałyby wprowadzone przez bezpośrednie użycie Messyrzeczy.


Powyższy przykład oparty jest na wyjaśnieniu warstwy antykorupcyjnej na wiki wiki c2:

Jeśli twoja aplikacja musi obsługiwać bazę danych lub inną aplikację, której model jest niepożądany lub nie ma zastosowania do modelu, który chcesz w swojej własnej aplikacji, skorzystaj z AnticorruptionLayer, aby przetłumaczyć na / z tego modelu i twojego.

Uwaga: przykład został celowo uproszczony i skrócony, aby wyjaśnienie było krótkie.

Jeśli masz większy bałagan interfejsu API, który można pokryć za warstwą antykorupcyjną, obowiązuje to samo podejście: po pierwsze, upewnij się, że „główny kod” nie ma bezpośredniego dostępu do uszkodzonych rzeczy, a po drugie, ujawnij go w sposób bardziej wygodne w kontekście użytkowania.

Kiedy „skalujesz” warstwę poza uproszczony przykład powyżej, weź pod uwagę, że uczynienie twojego API wygodnym niekoniecznie jest trywialnym zadaniem. Zainwestować wysiłek, aby zaprojektować warstwę właściwą drogę , zweryfikować jego przeznaczeniem z testów jednostkowych itp

Innymi słowy, upewnij się, że Twój interfejs API jest rzeczywiście lepszy od tego, który ukrywa, i upewnij się, że nie wprowadzasz kolejnej warstwy korupcji.


Dla kompletności zauważ subtelną, ale ważną różnicę między tym a pokrewnymi wzorami Adapter i Fasada . Jak wskazuje jego nazwa, warstwa antykorupcyjna zakłada, że u podstaw interfejsu API występują problemy z jakością (jest „uszkodzona”) i zamierza oferować ochronę wspomnianych problemów.

Można myśleć o tym w ten sposób: jeśli można uzasadnić, że projektant biblioteki byłoby lepiej odsłaniając jego funkcjonalność Reasonable, a nie Messyoznaczałoby to, że pracujesz na warstwie antykorupcyjnej, czyniąc ich pracę, ustalając swoje błędy projektowe.

W przeciwieństwie do tego, Adapter i Fasada nie przyjmują założeń dotyczących jakości projektu bazowego. Można je zastosować do interfejsu API, który jest dobrze zaprojektowany na początek, po prostu dostosowując go do konkretnych potrzeb.

W rzeczywistości bardziej produktywne byłoby założenie, że wzorce, takie jak Adapter i Fasada, oczekują, że kod bazowy będzie dobrze zaprojektowany. Możesz o tym pomyśleć w ten sposób: dobrze zaprojektowany kod nie powinien być zbyt trudny do dostosowania do konkretnego przypadku użycia. Jeśli okaże się, że projektowanie twojego adaptera wymaga więcej wysiłku niż się spodziewano, może to oznaczać, że kod źródłowy jest w jakiś sposób „uszkodzony”. W takim przypadku możesz rozważyć podzielenie zadania na oddzielne fazy: po pierwsze, utwórz warstwę antykorupcyjną, aby zaprezentować podstawowy interfejs API w odpowiednio ustrukturyzowany sposób, a następnie zaprojektuj adapter / fasadę na tej warstwie ochronnej.

komar
źródło
1
Jak skaluje się ta skala, jeśli istnieje cała struktura zależnych klas API? Czy jest to łatwiejsze do zarządzania niż warstwa, przed którą chroni resztę aplikacji?
knownasilya
1
@Knownasilya to bardzo dobre pytanie, odpowiedź została poszerzona, aby rozwiązać ten problem
gnat
4
In other words, make sure that your API is indeed an improvement over one it hides, make sure that you don't just introduce another layer of corruption.Ta cała sekcja zasługuje na odważny tag.
Lilienthal
19
Warstwy antykorupcyjne nie mają nic wspólnego z radzeniem sobie z interfejsami API niskiej jakości. Chodzi o radzenie sobie z niedopasowaniami pojęciowymi, dostosowywanie domen, których moglibyśmy używać tylko poprzez „niszczenie” naszego kodu do domen, z których możemy łatwiej korzystać.
Ian Fairman
8
Ian Fairman miał rację, a autor tej odpowiedzi zdecydowanie nie. Jeśli przejdziesz do źródła tej koncepcji (książki DDD), znajdziesz co najmniej dwie rzeczy, które są sprzeczne z tą odpowiedzią: 1) tworzona jest warstwa antykorupcyjna, aby uniknąć uszkodzenia nowego modelu domeny, który opracowujemy za pomocą elementów z modelu istniejącego systemu zewnętrznego; nie jest tak, że drugi system jest „zepsuty”, w rzeczywistości może być doskonale dobry i dobrze zaprojektowany; 2) warstwa antykorupcyjna zwykle zawiera kilka klas, często w tym fasady i adaptery , a także usługi .
Rogério,
41

Aby zacytować inne źródło:

Utwórz warstwę izolującą, aby zapewnić klientom funkcjonalność w zakresie ich własnego modelu domeny. Warstwa komunikuje się z innym systemem za pośrednictwem istniejącego interfejsu, wymagając niewielkich modyfikacji lub nie wymagając żadnych modyfikacji w innym systemie. Wewnętrznie warstwa przekłada się w obu kierunkach w razie potrzeby między dwoma modelami.

Eric Evans, Domain Driven Design, 16. drukowanie, strona 365

Najważniejsze jest to, że po każdej stronie warstwy antykorupcyjnej stosowane są różne terminy. Kiedyś pracowałem nad systemem logistyki transportu. Rundy musiały zostać zaplanowane. Trzeba było wyposażyć pojazd w magazynie, pojechać do różnych miejsc klientów i serwisować je oraz odwiedzić inne miejsca, takie jak przystanek tankowania. Ale z wyższego poziomu chodziło o planowanie zadań. Dlatego sensowne było oddzielenie bardziej ogólnych warunków planowania zadań od bardzo szczegółowych warunków logistyki transportu.

Tak więc izolacja warstw antykorupcyjnych nie polega tylko na ochronie przed niechlujnym kodem, ale na oddzieleniu różnych domen i upewnieniu się, że pozostaną one oddzielone w przyszłości.

SpaceTrucker
źródło
6
To jest bardzo ważne! Listy ACL nie należy używać tylko z niechlujnym kodem, ale jako środek komunikacji między ograniczonymi kontekstami. Przekłada się z jednego kontekstu na drugi, dzięki czemu dane w każdym kontekście odzwierciedlają język oraz sposób, w jaki kontekst ten myśli i mówi o danych.
Didier A.
29

Adapter

Gdy masz niekompatybilne interfejsy, które wykonują podobną logikę, aby dostosować jeden do drugiego, abyś mógł używać implementacji jednego z rzeczami, które oczekują drugiego.

Przykład:

Masz obiekt, który chce samochodu, ale masz tylko klasę 4WheelVehicle, więc tworzysz CarBuiltUsing4WheelVehicle i używasz go jako swojego samochodu.

Fasada

Gdy masz skomplikowane / mylące / gigantyczne API i chcesz uprościć / uprościć / zmniejszyć. Stworzysz fasadę, aby ukryć złożoność / zamieszanie / dodatki i ujawnić tylko nowe proste / jasne / małe API.

Przykład:

Korzystasz z biblioteki, która ma 100 metod, i aby wykonać określone zadanie, musisz wykonać szereg inicjalizacji, łączenia, otwierania / zamykania rzeczy, aby w końcu móc robić to, co chciałeś, a wszystko czego chciałeś to 1 cecha wszystkie 50 bibliotek może to zrobić, więc tworzysz fasadę, która ma tylko jedną metodę, której potrzebujesz, i która wykonuje całą inicjalizację, czyszczenie itp.

Warstwa antykorupcyjna

Jeśli masz system spoza domeny, a potrzeby Twojej firmy wymagają współpracy z tą inną domeną. Nie chcesz wprowadzać tej drugiej domeny do swojej, a zatem ją korumpować, więc przetłumaczysz pojęcie swojej domeny na inną domenę i odwrotnie.

Przykład:

Jeden system wyświetla klienta z nazwą i listą ciągów, po jednym dla każdej transakcji. Profile są postrzegane jako samodzielne klasy o nazwie, a Transakcje - jako samodzielne klasy zawierające ciąg znaków, a Klient - jako profil i zbiór Transakcji.

Tworzysz więc warstwę ACL, która pozwoli tłumaczyć między klientem a klientem innego systemu. W ten sposób nigdy nie musisz korzystać z klienta innego systemu, musisz po prostu powiedzieć ACL: „daj mi klienta z profilem X, a ACL każe drugiemu systemowi dać klientowi o nazwie X.name i zwraca jesteś klientem o profilu X.

====================

Wszystkie trzy są względnie podobne, ponieważ wszystkie są wzorcami pośrednimi. Ale dotyczą one różnych struktur, klas / obiektów w porównaniu do interfejsów API w porównaniu do modułów / podsystemów. Możesz połączyć je wszystkie, jeśli zajdzie taka potrzeba. Podsystem ma złożony interfejs API, więc budujesz dla niego FACADE, używa on innego modelu, więc dla każdej reprezentacji danych, która nie pasuje do twojego modelu, przetłumaczysz te dane z powrotem na sposób, w jaki je modelujesz. Wreszcie, być może interfejsy są również niekompatybilne, więc użyłbyś ADAPTERÓW, aby dostosować się od jednego do drugiego.

Didier A.
źródło
12

Wiele odpowiedzi tutaj mówi, że listy ACL „nie tylko” dotyczą zawijania niechlujnego kodu. Chciałbym pójść dalej i powiedzieć, że wcale tak nie jest, a jeśli tak, to jest to dodatkowa korzyść.

Warstwa antykorupcyjna polega na mapowaniu jednej domeny na drugą, aby usługi korzystające z drugiej domeny nie musiały być „zepsute” przez koncepcje z pierwszej. Listy ACL służą do modelowania domen, czym są adaptery do klas, dzieje się to po prostu na innym poziomie. Adapter jest prawdopodobnie najważniejszym wzorcem projektowym - używam go cały czas - ale ocenianie owiniętej klasy jako bałaganu lub nie jest nieistotne. Tak właśnie jest, potrzebuję tylko innego interfejsu.

Koncentrowanie się na bałaganie jest mylące i nie rozumie, o co chodzi w DDD. Listy ACL dotyczą radzenia sobie z niedopasowaniami pojęciowymi, a nie złej jakości.

Ian Fairman
źródło