web-dev-qa-db-fra.com

L'architecture propre de l'oncle Bob - Une classe d'entité / modèle pour chaque couche?

CONTEXTE:

J'essaie d'utiliser l'architecture propre d'oncle Bob dans mon Android. J'ai étudié de nombreux projets open source qui essaient de montrer la bonne façon de le faire, et j'ai trouvé n mise en œuvre intéressante basé sur RxAndroid.

CE QUE J'AI AVISÉ:

Dans chaque couche (présentation, domaine et données), il existe une classe de modèle pour la même entité (parler UML). De plus, il existe des classes de mappeur qui prennent en charge la transformation de l'objet chaque fois que les données franchissent les limites (d'une couche à une autre).

QUESTION:

Est-il nécessaire d'avoir des classes de modèle dans chaque couche lorsque je sais qu'elles se retrouveront toutes avec les mêmes attributs si toutes les opérations CRUD sont nécessaires? Ou, s'agit-il d'une règle ou d'une meilleure pratique lors de l'utilisation de l'architecture propre?

49
Rami Jemli

À mon avis, ce n'est absolument pas ce que cela signifie. Et c'est une violation de DRY.

L'idée est que l'objet entité/domaine au milieu est modélisé pour représenter le domaine aussi bien et aussi pratique que possible. Il est au centre de tout et tout peut en dépendre puisque le domaine lui-même ne change pas la plupart du temps.

Si votre base de données à l'extérieur peut stocker ces objets directement, les mapper à un autre format pour séparer les couches n'est pas seulement inutile, mais crée des doublons du modèle et ce n'est pas l'intention.

Pour commencer, l'architecture propre a été conçue avec un environnement/scénario typique différent à l'esprit. Applications de serveur d'entreprise avec des couches externes géantes qui ont besoin de leurs propres types d'objets spéciaux. Par exemple, les bases de données qui produisent des objets SQLRow et ont besoin de SQLTransactions en retour pour mettre à jour les éléments. Si vous deviez les utiliser au centre, vous violeriez le sens de la dépendance car votre cœur dépendrait de la base de données.

Avec des ORM légers qui chargent et stockent des objets d'entité, ce n'est pas le cas. Ils font le mappage entre leur SQLRow interne et votre domaine. Même si vous devez mettre un @Entitiy annotation de l'ORM dans votre objet de domaine, je dirais que cela n'établit pas une "mention" de la couche externe. Étant donné que les annotations ne sont que des métadonnées, aucun code qui ne les recherche pas spécifiquement ne les verra. Et plus important encore, rien ne doit être modifié si vous les supprimez ou les remplacez par une annotation de base de données différente.

En revanche, si vous changez votre domaine et que vous avez créé tous ces mappeurs, vous devez beaucoup changer.


Amendement: ci-dessus est un peu simpliste et pourrait même être faux. Parce qu'il y a une partie dans une architecture propre qui veut que vous créiez une représentation par couche. Mais cela doit être vu dans le contexte de l'application.

À savoir ce qui suit ici https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

L'important est que des structures de données isolées et simples traversent les frontières. Nous ne voulons pas tricher et transmettre des entités ou des lignes de base de données. Nous ne voulons pas que les structures de données aient une sorte de dépendance qui viole la règle de dépendance.

Le passage d'entités du centre vers les couches externes ne viole pas la règle de dépendance, pourtant elles sont mentionnées. Mais cela a une raison dans le contexte de l'application envisagée. Le passage d'entités déplacerait la logique d'application vers l'extérieur. Les couches externes devraient savoir comment interpréter les objets internes, elles devraient effectivement faire ce que les couches internes telles que la couche "use case" sont censées faire.

En plus de cela, il dissocie également les couches afin que les modifications apportées au noyau ne nécessitent pas nécessairement des modifications dans les couches externes (voir le commentaire de SteveCallender). Dans ce contexte, il est facile de voir comment les objets doivent représenter spécifiquement le but pour lequel ils sont utilisés. De plus, les calques doivent se parler entre eux en termes d'objets spécialement conçus pour cette communication. Cela peut même signifier qu'il y a 3 représentations, 1 dans chaque couche, 1 pour le transport entre les couches.

Et il y a https://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.html qui adresse ci-dessus:

D'autres personnes s'inquiètent du fait que le résultat net de mes conseils serait beaucoup de code dupliqué et beaucoup de copie par cœur de données d'une structure de données à une autre à travers les couches du système. Je ne veux certainement pas non plus cela; et rien de ce que j'ai suggéré ne conduirait inévitablement à la répétition des structures de données et à une copie démesurée du champ.

Cet IMO implique que la copie 1: 1 d'objets est une odeur dans l'architecture parce que vous n'utilisez pas réellement les couches et/ou les abstractions appropriées.

Il explique plus tard comment il imagine toutes les "copies"

Vous séparez l'interface utilisateur des règles métier en passant des structures de données simples entre les deux. Vous ne laissez rien savoir à vos contrôleurs des règles métier. Au lieu de cela, les contrôleurs décompressent l'objet HttpRequest dans une structure de données Vanilla simple, puis transmettent cette structure de données à un objet interacteur qui implémente le cas d'utilisation en appelant des objets métier. L'interacteur rassemble ensuite les données de réponse dans une autre structure de données Vanilla et les retransmet à l'interface utilisateur. Les vues ne connaissent pas les objets métier. Ils regardent simplement dans cette structure de données et présentent la réponse.

Dans cette application, il y a une grande différence entre les représentations. Les données qui circulent ne sont pas seulement les entités. Et cela justifie et exige différentes classes.

Cependant, appliqué à une simple application Android comme une visionneuse de photos où l'entité Photo a environ 0 règles métier et le "cas d'utilisation" qui les traite est presque inexistant et est en fait plus préoccupé par la mise en cache et le téléchargement (ce processus devrait être représenté plus explicitement par l'OMI), le point de faire des représentations distinctes d'une photo commence à disparaître. J'ai même l'impression que la photo elle-même est l'objet de transfert de données tandis que le réel la couche de cœur de logique métier est manquante.

Il existe une différence entre "séparez l'interface utilisateur des règles métier en passant des structures de données simples entre les deux" et "lorsque vous souhaitez afficher une photo, renommez-la 3 fois sur le façon ".

En plus de cela, le point où je vois ces applications de démonstration ne parviennent pas à représenter l'architecture propre est qu'elles ajoutent une grande importance à la séparation des couches pour séparer les couches mais masquent efficacement ce que fait l'application. C'est en contraste avec ce qui est dit dans https://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.html - à savoir que

l'architecture d'une application logicielle hurle sur les cas d'utilisation de l'application

Je ne vois pas l'accent mis sur la séparation des couches dans l'architecture propre. Il s'agit de la direction des dépendances et de se concentrer sur la représentation du cœur de l'application - les entités et les cas d'utilisation - dans l'idéal Java sans dépendances vers l'extérieur. Il ne s'agit pas tant de dépendances envers ce cœur.

Donc, si votre application a un noyau qui représente les règles métier et les cas d'utilisation et/ou différentes personnes travaillent sur différentes couches, veuillez les séparer de la manière prévue. Si vous êtes par contre en train d'écrire une application simple par vous-même, n'en faites pas trop. 2 couches avec des limites fluides peuvent être plus que suffisantes. Et des couches peuvent également être ajoutées ultérieurement.

57
zapl

Vous avez bien compris. Et il n'y a aucune violation de DRY parce que vous acceptez SRP.

Par exemple: Vous avez une méthode métier createX (nom de chaîne), puis vous pouvez avoir une méthode createX (nom de chaîne) dans la couche DAO, appelée dans la méthode métier. Ils peuvent avoir la même signature et peut-être qu'il n'y a qu'une délégation mais ils ont des objectifs différents. Vous pouvez également avoir un createX (nom de chaîne) sur UseCase. Même alors, ce n'est pas redondant. Ce que je veux dire par là, c'est que les mêmes signatures ne signifient pas la même sémantique. Choisissez d'autres noms pour que la sémantique soit claire. En se nommant, cela n'affecte pas du tout SRP.

UseCase est responsable de la logique spécifique à l'application, l'objet métier est responsable de la logique indépendante de l'application et le DAO est responsable du stockage.

En raison de la sémantique différente, toutes les couches peuvent avoir leur propre modèle de représentation et de communication. Souvent, vous voyez les "entités" comme des "objets métier" et souvent vous ne voyez pas la nécessité de les séparer. Mais dans les projets "énormes", l'effort doit être pris pour séparer correctement les couches. Plus le projet est grand, plus il est possible que vous ayez besoin des différentes sémantiques représentées dans différentes couches et classes.

Vous pouvez penser à différents aspects de la même sémantique. Un objet utilisateur doit être affiché à l'écran, il a des règles de cohérence interne et il doit être stocké quelque part. Chaque aspect doit être représenté dans une classe différente (SRP). La création des cartographes peut être pénible, donc dans la plupart des projets sur lesquels j'ai travaillé, ces aspects sont fondus en une seule classe. C'est clairement une violation de SRP mais personne ne s'en soucie vraiment.

J'appelle l'application d'architecture propre et S.O.L.I.D. "pas socialement acceptable". Je travaillerais avec si je le pouvais. Actuellement, je ne suis pas autorisé à le faire. J'attends le moment où nous devons penser à prendre S.O.L.I.D. sérieusement.

7
user205407

Non, vous n'avez pas besoin de créer de classes de modèle dans chaque couche.

Entité (DATA_LAYER) - est une représentation complète ou partielle de l'objet Database. DATA_LAYER

Mappeur (DOMAIN_LAYER) - est en fait une classe qui convertit Entity en ModelClass, qui sera utilisée sur DOMAIN_LAYER

Jetez un oeil: https://github.com/lifedemons/photoviewer

5
deathember