web-dev-qa-db-fra.com

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

J'essaie de comprendre ce que la couche anti-corruption signifie vraiment. Je sais que c'est un moyen de transition/contourner le code hérité ou les 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 quelques recherches, mais je ne trouve pas d'exemples ou d'explications simples, donc je cherche quelqu'un qui le comprend et peut l'expliquer avec des 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 ceci question , pour mon cas d'utilisation.

157
knownasilya

Imaginez que vous devez 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) { /* ... */ }
    }

Imaginez maintenant que vous découvrez 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.

D'accord, vous commencez donc à créer une couche anti-corruption.

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

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

Le code de la 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); }
    }

Par conséquent, votre "code principal" ne dérange pas avec Messy, en utilisant Reasonable à la place, à peu près comme suit:

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 désordre avec Messy mais cela est maintenant caché assez profondément à l'intérieur de Reasonable, ce qui rend votre "code principal" raisonnablement propre et exempt de corruption qui y serait apporté par utilisation directe de Messy stuff.


L'exemple ci-dessus est basé sur la façon dont Anticorruption Layer est expliqué sur c2 wiki:

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 que vous souhaitez dans votre propre application, utilisez un AnticorruptionLayer pour traduire vers/depuis ce modèle et le vôtre.

Remarque exemple est intentionnellement rendu simple et condensé pour garder l'explication brève.

Si vous avez un plus gros mess-of-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 à truc corrompu directement et ensuite , exposez-le d'une manière plus pratique dans votre contexte d'utilisation.

Lorsque vous "mettez à l'échelle" votre couche au-delà d'un exemple simplifié ci-dessus, n'oubliez pas que rendre votre API pratique n'est pas nécessairement une tâche triviale. Investissez un effort à concevez votre couche de la bonne façon , vérifiez son utilisation prévue avec tests unitaires etc.

En d'autres termes, assurez-vous que votre API est en effet 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 cela et les modèles associés Adaptateur et Façade . Comme indiqué par son nom, la couche anticorruption suppose que sous-jacent L'API a problèmes de qualité (est "corrompu") et a l'intention d'offrir une protection des problèmes mentionnés.

Vous pouvez y penser de cette façon: si vous pouvez justifier que le concepteur de bibliothèque ferait mieux d'exposer ses fonctionnalités avec Reasonable au lieu de Messy, cela signifierait que vous travaillez sur une couche anticorruption, en faisant leur travail, correction leur des erreurs de conception.

Contrairement à cela, Adaptateur et Façade ne font pas d'hypothèses sur la qualité de la conception sous-jacente. Ceux-ci pourraient être appliqués à une API bien conçue au départ, il suffit de l'adapter à vos besoins spécifiques.

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

147
gnat

Pour citer une autre source:

Créez une couche isolante pour fournir aux clients des fonctionnalités en fonction de leur propre modèle de domaine. La couche communique avec l'autre système via son interface existante, nécessitant peu ou pas de modification de l'autre système. En interne, la couche se traduit dans les deux directions selon les besoins entre les deux modèles.

Eric Evans, Domain Driven Design, 16e impression, page 365

La chose la plus importante est que des termes différents sont utilisés de chaque côté de la couche anti-corruption. Je travaillais une fois sur un système de logistique de transport. Des tournées devaient être planifiées. Vous deviez équiper le véhicule dans un dépôt, vous rendre sur différents sites clients et les entretenir et visiter d'autres endroits, comme un arrêt de réservoir. Mais à partir du niveau supérieur, il s'agissait de planifier des tâches. Il était donc logique de séparer les termes de planification des tâches plus généraux des termes logistiques de transport très spécifiques.

Ainsi, l'isolement des couches anti-corruption ne consiste pas seulement à vous protéger contre le code en désordre, il consiste à séparer les différents domaines et à vous assurer qu'ils restent séparés à l'avenir.

42
SpaceTrucker

adaptateur

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

Exemple:

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

Façade

Lorsque vous avez une API complexe/déroutante/gigantesque et que vous souhaitez la rendre plus simple/plus claire/plus petite. Vous allez créer une façade pour cacher 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 faire un tas d'initialisation, de connexion, d'ouverture/fermeture de choses, juste pour enfin pouvoir faire ce que vous vouliez, et tout ce que vous vouliez, c'est une fonctionnalité de tout ce 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 fait tout l'initialisation, le nettoyage, etc. pour vous.

Couche anti-corruption

Lorsque vous avez un système hors de votre domaine, les besoins de votre entreprise nécessitent que vous travailliez 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 vice-versa.

Exemple:

Un système affiche le client ayant un nom et une liste de chaînes, une pour chaque transaction. Vous voyez les profils comme des classes autonomes ayant un nom, et les transactions comme des classes autonomes ayant une chaîne, et le client comme ayant un profil et une collection de transactions.

Vous créez donc une couche ACL qui permettra de traduire 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, il vous suffit de dire à l'ACL: "donnez-moi le client avec le profil X, et l'ACL dit à l'autre système de lui donner un client de nom X.name, et retourne vous un client avec le profil X.

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

Tous les trois sont relativement similaires, car ce sont tous des modèles d'indirection. Mais ils s'adressent à différentes structures, classes/objets versus API versus modules/sous-systèmes. Vous pourriez les avoir tous combinés si nécessaire. Le sous-système a une API complexe, donc vous construisez une FACADE pour lui, il utilise un modèle différent, donc pour chaque représentation de données qui ne correspond pas à votre modèle, vous TRADUIREZ ces données dans la façon dont vous les modélisez. Enfin, peut-être que les interfaces sont également incompatibles, vous utiliserez donc des ADAPTATEURS pour vous adapter de l'un à l'autre.

32
Didier A.

Beaucoup de réponses ici disent que les listes de contrôle d'accès ne sont pas "juste" pour emballer du code désordonné. J'irais plus loin et je dirais que ce n'est pas du tout cela, et s'ils le font, c'est un avantage secondaire.

Une couche anti-corruption consiste à mapper un domaine sur un autre afin que les services qui utilisent le deuxième domaine n'aient pas à être "corrompus" par les concepts du premier. Les ACL sont aux modèles de domaine ce que les adaptateurs sont aux classes, cela se produit simplement à un niveau différent. L'adaptateur est sans doute le modèle de conception le plus important - je l'utilise tout le temps - mais juger la classe encapsulée comme étant en désordre ou non n'est pas pertinent. C'est ce que c'est, j'ai juste besoin qu'il ait une interface différente.

Se concentrer sur le désordre est trompeur et ne correspond pas à l'objectif de DDD. Les listes de contrôle d'accès concernent le traitement des asymétries conceptuelles, et non la mauvaise qualité.

16
Ian Fairman