Qu'est-ce qu'une couche anti-corruption et comment est-elle utilisée?

151

J'essaie de comprendre ce que la couche anti-corruption signifie vraiment. Je sais que c'est un moyen de faire la transition / de contourner le code hérité ou de mauvaises API. Ce que je ne comprends pas, c'est comment cela fonctionne et ce qui en fait une séparation nette de la couche indésirable.

J'ai fait des recherches, mais je ne trouve pas d'exemples ou d'explications simples. Je recherche donc quelqu'un qui le comprend et peut l'expliquer à l'aide d'exemples simples. Une réponse qui satisferait ma question devrait être simple (pas nécessairement courte) et fournir des exemples compréhensibles de mise en œuvre et d'utilisation.

Voir cette question , pour mon cas d'utilisation.

Connu Asilya
la source

Réponses:

147

Imaginez que vous deviez utiliser le code de quelqu'un d'autre conçu comme indiqué ci-dessous:

    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) { /* ... */ }
    }

Maintenant, imaginez que vous découvriez que votre code qui en dépend ressemble à ceci:

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?
}

... et que vous souhaitez faciliter l'utilisation, en particulier, pour vous débarrasser de l'utilisation répétitive de paramètres qui ne sont tout simplement pas nécessaires pour votre application.

Bon, vous commencez à construire une couche anti-corruption.

  1. La première chose à faire est de vous assurer que votre "code principal" ne fait pas Messydirectement référence . Par exemple, vous organisez la gestion des dépendances de telle manière que la tentative d'accès Messyéchoue lors de la compilation.

  2. Deuxièmement, vous créez un module "couche" dédié qui est le seul à y accéder Messyet vous l'exposez à votre "code principal" de manière plus logique.

Le code de couche ressemblerait à ceci:

    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); }
    }

En conséquence, votre "code principal" ne joue pas avec l' Messyutilisation Reasonablesuivante:

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");
}

Notez qu'il y a encore un peu de gâchis Messymais cela est maintenant caché assez profondément à l'intérieur Reasonable, rendant votre "code principal" raisonnablement propre et exempt de corruption qui y serait apporté par l'utilisation directe de Messychoses.


L'exemple ci-dessus est basé sur la façon dont la couche anticorruption est expliquée sur le wiki c2:

Si votre application doit traiter avec une base de données ou une autre application dont le modèle est indésirable ou inapplicable au modèle souhaité dans votre propre application, utilisez un AnticorruptionLayer pour effectuer la traduction vers / à partir de ce modèle et le vôtre.

Remarque exemple est volontairement simplifié et condensé pour garder l'explication brève.

Si vous avez un plus grand gâchis d’API à couvrir derrière la couche anti-corruption, la même approche s’applique: d’abord, assurez-vous que votre "code principal" n’accède pas directement au contenu corrompu et, deuxièmement, exposez-le de manière plus efficace. pratique dans votre contexte d'utilisation.

Lorsque vous "redimensionnez" votre couche au-delà de l'exemple simplifié ci-dessus, prenez en compte le fait que rendre votre API pratique n'est pas nécessairement une tâche triviale. Faites un effort pour concevoir votre couche de la bonne manière , vérifiez son utilisation prévue avec des tests unitaires, etc.

En d'autres termes, assurez-vous que votre API est bien une amélioration par rapport à celle qu'elle cache, assurez-vous de ne pas simplement introduire une autre couche de corruption.


Par souci d'exhaustivité, notez la différence subtile mais importante entre ce modèle et les modèles associés Adaptateur et Façade . Comme son nom l’indique, la couche anticorruption suppose que l’ API sous - jacente pose des problèmes de qualité (est "corrompue") et entend offrir une protection des problèmes mentionnés.

Vous pouvez penser de cette façon: si vous pouviez justifier que le concepteur de bibliothèque exposerait mieux ses fonctionnalités avec, Reasonableau lieu de Messy, cela signifierait que vous travaillez sur la couche anticorruption, que vous faites leur travail, corrigez leurs erreurs de conception.

Contrairement à cela, Adapter et Facade ne font aucune hypothèse sur la qualité de la conception sous-jacente. Celles-ci pourraient être appliquées à une API bien conçue pour commencer, en l'adaptant simplement à vos besoins spécifiques.

En fait, il pourrait être encore plus productif de supposer que des modèles tels que Adapter et Facade s’attendent à ce que le code sous-jacent soit bien conçu. Vous pouvez penser de cette façon: un code bien conçu ne devrait pas être trop difficile à peaufiner pour un cas d'utilisation particulier. S'il s'avère que la conception de votre adaptateur demande plus d'efforts que prévu, cela pourrait indiquer que le code sous-jacent est, d'une certaine manière, "corrompu". Dans ce cas, vous pouvez envisager de scinder le travail en phases distinctes: tout d'abord, créez une couche anticorruption pour présenter l'API sous-jacente d'une manière correctement structurée, puis concevez votre adaptateur / façade sur cette couche de protection.

moucheron
la source
1
Comment cela se passe-t-il s'il existe une structure complète de classes d'API dépendantes? Est-il encore plus facile à gérer que la couche dont il protège le reste de l'application?
connusasilya
1
@Knownasilya c'est une très bonne question, réponse élargie pour répondre à cette
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.Cette section entière est digne d'une balise audacieuse.
Lilienthal
19
Les couches anti-corruption n'ont rien à voir avec le traitement des API de mauvaise qualité. Il s’agit de traiter les incohérences conceptuelles, d’adapter des domaines que nous pourrions utiliser uniquement en "corrompant" notre code pour des domaines que nous pouvons utiliser plus facilement.
Ian Fairman
8
Ian Fairman a eu raison, alors que l'auteur de cette réponse ne l'a absolument pas fait. Si vous allez à la source du concept (le livre DDD), vous trouverez au moins deux choses qui contredisent cette réponse: 1) une couche anti-corruption est créée pour éviter de corrompre le nouveau modèle de domaine que nous développons avec des éléments à partir du modèle d'un système externe existant; ce n'est pas que l'autre système soit "corrompu", en fait, il peut être parfaitement bon et bien conçu; 2) une couche anti-corruption contiendra généralement plusieurs classes, comprenant souvent des façades et des adaptateurs , ainsi que des services .
Rogério
41

Pour citer une autre source:

Créez une couche d'isolation pour fournir aux clients des fonctionnalités correspondant à leur propre modèle de domaine. La couche communique avec l'autre système via son interface existante, ce qui nécessite peu ou pas de modification de l'autre système. En interne, la couche se traduit dans les deux sens si nécessaire entre les deux modèles.

Eric Evans, Domain Driven Design, 16ème impression, page 365

Le plus important est que des termes différents soient utilisés de chaque côté de la couche anti-corruption. Je travaillais autrefois sur un système de logistique de transport. Les rondes devaient être planifiées. Vous deviez équiper le véhicule dans un dépôt, vous rendre dans différents sites clients, les entretenir et les visiter, par exemple à une station-service. Mais à partir du niveau supérieur, tout était question de planification des tâches. Il était donc logique de séparer les termes plus généraux de planification des tâches des termes très spécifiques de logistique de transport.

Ainsi, l'isolation des couches anti-corruption ne consiste pas uniquement à vous protéger du code désordonné, elle consiste à séparer différents domaines et à s'assurer qu'ils le resteront à l'avenir.

SpaceTrucker
la source
6
C'est très important! Une ACL ne doit pas uniquement être utilisée avec du code Messy, mais comme moyen de communiquer entre des contextes liés. Il traduit d'un contexte à l'autre, de sorte que les données de chaque contexte reflètent le langage et la façon dont ce contexte pense et parle des données.
Didier A.
29

Adaptateur

Lorsque vous avez des interfaces incompatibles, qui exécutent une logique similaire, vous pouvez les adapter l'une à l'autre, de sorte que vous puissiez utiliser les implémentations de l'une avec des éléments qui attendent l'autre.

Exemple:

Vous avez un objet qui veut une voiture, mais vous n'avez qu'une classe 4WheelVehicle, vous créez donc un CarBuiltUsing4WheelVehicle et vous l'utilisez comme votre voiture.

Façade

Lorsque vous avez une API complexe / déroutante / gigantesque et que vous souhaitez la simplifier / la clarifier / la réduire. Vous allez créer une façade pour masquer la complexité / confusion / extras et exposer uniquement une nouvelle API simple / claire / petite.

Exemple:

Vous utilisez une bibliothèque qui a 100 méthodes, et pour effectuer certaines tâches, vous devez effectuer un tas d'initialisations, de connexions, d'ouverture / fermeture de choses, pour pouvoir enfin faire ce que vous voulez, et tout ce que vous vouliez, c'est 1 fonctionnalité de toutes les 50 que la bibliothèque peut faire, vous créez donc une façade qui n’a qu’une méthode pour cette fonction dont vous avez besoin et qui effectue pour vous l’initialisation, le nettoyage, etc.

Couche anti-corruption

Lorsque vous avez un système hors de votre domaine, votre entreprise a toutefois besoin de travailler avec cet autre domaine. Vous ne voulez pas introduire cet autre domaine dans le vôtre, donc le corrompre, vous allez donc traduire le concept de votre domaine dans cet autre domaine, et inversement.

Exemple:

Un système affiche le client avec un nom et une liste de chaînes, une pour chaque transaction. Vous affichez les profils en tant que classes autonomes ayant un nom et les transactions en tant que classes autonomes avec une chaîne, et le client en tant que profil et collection de transactions.

Vous créez donc une couche ACL qui permettra la conversion entre votre client et le client de l’autre système. De cette façon, vous n’avez jamais à utiliser le client de l’autre système, vous devez simplement dire à la liste de contrôle d’accès: "donnez-moi le client avec le profil X, et la liste de contrôle d’accès dit à l’autre système de lui attribuer un nom de client X.name et renvoie vous êtes client avec le profil X.

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

Tous les trois sont relativement similaires, car ils sont tous des modèles indirectionnels. Mais ils abordent différentes structures, classes / objets par rapport aux API par rapport aux modules / sous-systèmes. Vous pouvez les combiner tous si vous en aviez besoin. Le sous-système a une API complexe, vous construisez donc une FACADE, il utilise un modèle différent. Par conséquent, pour chaque représentation de données qui ne correspond pas à votre modèle, vous devez TRADUIRE ces données dans leur modélisation. Enfin, peut-être que les interfaces sont également incompatibles, vous utiliseriez donc ADAPTERS pour s’adapter de l’un à l’autre.

Didier A.
la source
12

De nombreuses réponses à cette question indiquent que les listes de contrôle d'accès ne «concernent pas seulement» le code désordonné. J'irais plus loin en disant qu'ils n'en parlent pas du tout et que s'ils le font, c'est un avantage secondaire.

Une couche anti-corruption consiste à mapper un domaine sur un autre, de sorte que les services qui utilisent le deuxième domaine ne doivent pas nécessairement être "corrompus" par les concepts du premier. Les ACL sont des modèles de domaine, ce que les adaptateurs sont des classes. Cela se produit à un niveau différent. L’adaptateur est sans doute le motif de conception le plus important (je l’utilise tout le temps), mais il n’est pas pertinent de considérer la classe emballée comme étant désordonnée ou non. C'est ce que c'est, j'ai juste besoin d'une interface différente.

Se concentrer sur le désordre est trompeur et passe à côté de l’objet de DDD. Les listes de contrôle d'accès traitent de problèmes d'inadéquation conceptuelle et non de mauvaise qualité.

Ian Fairman
la source