Quand une classe ou un module doit-il se trouver dans un assembly / DLL séparé?

22

Existe-t-il des directives pour décider quand une classe doit se trouver dans son propre assembly / DLL? Je vois souvent deux écoles de pensée:

1) Chaque "regroupement" de classes appartient à sa propre DLL, par exemple les référentiels, les services, les DTO, l'infrastructure, etc.

2) Tout doit être dans une seule DLL mais séparé via des espaces de noms / dossiers, par exemple avoir une DLL "Core" avec des espaces de noms supplémentaires, par exemple Core.Repositories, Core.Services, Core.DTO, etc.

Au travail, nous regroupons tout dans une seule assemblée appelée «Business». Il y a des dossiers mais il n'y a pas de véritable séparation - les objets métier (avec logique, dont certains ne devraient même pas être des classes) sont regroupés dans un dossier "BusinessObjects" sans précaution. Les éléments utilisés dans plusieurs classes se trouvent dans un dossier "Core". Les utilitaires sont dans un dossier "Utilitaires", l'infrastructure d'accès aux données est un dossier "Données" - vous avez l'idée.

Pour un nouveau module sur lequel je travaille, je veux / j'ai besoin d'avoir une couche d'accès aux données séparée (pensez à une implémentation rudimentaire du référentiel) mais je ne veux pas simplement le jeter sous le dossier "BusinessObjects" avec les 160 autres (!) classes là-bas. En même temps, je suis préoccupé par la création d'une nouvelle bibliothèque de classes car tout le monde a l'habitude de bourrer une classe dans la bibliothèque unique; un dossier / espace de noms pourrait cependant fonctionner.

Wayne Molina
la source

Réponses:

12

Je trouve qu'il est préférable d'avoir plus de projets (c.-à-d. Des assemblys) avec des classes divisées par catégorie dans chaque projet qu'un projet avec toutes ces classes dans des espaces de noms séparés. Vous devez viser à ce que vos projets soient réutilisables et représentent différentes couches dans une application. Vous pouvez ensuite réutiliser ces projets dans de futures applications sans avoir à inclure tout un tas de classes inutiles.

Par exemple, sur la base de ce que vous avez mentionné, j'aurais certainement les projets suivants:

  • Coeur
  • Les données
  • Domaine (ou BusinessObjects)
  • Prestations de service
  • Utilitaires (ou aides)
Bernard
la source
2
Malheureusement, je ne peux pas voter contre la réponse. C'est exactement ce qui est conseillé dans notre cabinet. Par conséquent, avoir 6 projets ou plus, par exemple pour un site Web de taille moyenne, vous ne pouvez pas maintenir une hiérarchie de répertoires basée sur les fonctionnalités, etc. Par conséquent, vous vous retrouvez avec les six projets avec chacun est un gros désordre impossible à parcourir. Habituellement, les personnes qui conseillent cela n'ont jamais essayé la structure basée sur les fonctionnalités des projets sur des projets relativement importants (désolé pour le jugement direct, mais le traitement des résultats d'un tel surfaçage est une vraie douleur). La réponse jammycakes conseille une approche correcte.
alehro
La division des classes par catégorie est ce qui mène au désordre dans les grands projets. Divisez-les par fonction / aspect. Les espaces de noms refléteront alors simplement cette division. Et toute la structure reflétera le lexique des spécifications. La division des classes par catégorie revient à ajouter des abréviations de type aux noms de variable - c'est généralement une mauvaise odeur.
alehro
Il est facile d'abuser des deux solutions proposées (c'est-à-dire trop de projets ou trop peu). Trouver un bon équilibre où la réutilisation et le faible couplage sont gardés à l'esprit devrait conduire à quelque chose de plus testable et maintenable.
Bernard
16

"Oncle Bob" Martin de Clean Code, la renommée de SOLID Principles a décrit trois principes ici :

  • Le principe d'équivalence de réutilisation des versions: le granule de réutilisation est le granule de libération.
  • Le principe commun de fermeture: les classes qui changent ensemble sont regroupées.
  • Le principe de réutilisation courant: les classes utilisées ensemble sont regroupées.

La règle générale est que vous devez limiter le plus possible le nombre de projets dans votre solution. Ne les divisez que si vous en avez besoin pour implémenter une ou plusieurs user stories spécifiques, ou si le fait d'avoir un seul assembly cause des problèmes de performances mesurables (généralement une fois qu'ils atteignent plusieurs mégaoctets).

confitures
la source
2
+1 ces principes sont de bonnes lignes directrices. La séparation des classes en différents assemblys introduit des considérations de conception supplémentaires (par exemple, les versions de chaque assemblage autorisées à fonctionner ensemble), augmentant la base de code globale, augmentant ainsi le coût de la maintenance.
Ben
1
Cependant, en plus de ces principes, j'ajouterais que c'est souvent une bonne idée de regrouper le code susceptible d'être remplacé ou échangé dans un assembly séparé. Ce code bénéficiera des considérations supplémentaires nécessaires pour coupler des assemblages séparés et le faire séparer facilitera le test et la comparaison des différentes versions.
Ben
4
Oui, mais assurez-vous qu'il existe une véritable exigence pour qu'il soit remplacé ou échangé. Par exemple, les frameworks et les bibliothèques tierces - packages NuGet et similaires - doivent généralement prendre en charge plusieurs conteneurs IOC et plusieurs frameworks de journalisation. Pour le code du pays utilisateur, en revanche, ces abstractions sont généralement purement spéculatives, inutiles et obstructives, et ne fonctionnent finalement pas dans les rares cas où elles sont réellement nécessaires.
jammycakes
"Ne les divisez que si vous en avez besoin pour implémenter une ou plusieurs user stories spécifiques, ou si le fait d'avoir un seul assembly cause des problèmes de performances mesurables (généralement une fois qu'ils atteignent plusieurs mégaoctets)." - conseils totalement aléatoires et non fondés sur la réalité
hyankov
1
Je suis désolé, mais qu'est-ce qui est "totalement aléatoire et non fondé sur la réalité"? J'ai posté cette réponse après des années de travail sur des solutions qui ont été divisées en bien plus de projets que nécessaire sans autre raison que celle-là. Le résultat? Des temps de compilation glaciaux et un bourbier d'enfer de dépendance (surtout dans les jours pré-NuGet). La division des éléments en assemblages séparés a un coût, et s'il n'y a aucun avantage à compenser ce coût, alors c'est juste un cas de vol de l'entreprise.
jammycakes
4

Quelques autres principes directeurs avec lesquels je travaille:

  • Pensez-vous que vous réutiliserez ce code dans d'autres projets? Pour un groupe d'applications Web connexes, nous avions un module lié au compte d'utilisateur que toutes les applications utilisaient, car ils utilisaient tous le même modèle pour les comptes d'utilisateur et les connexions. J'ai fait des choses similaires avec des bibliothèques de géométrie et de mathématiques et je les ai réutilisées dans plusieurs applications, simplement en incluant la DLL.

  • Voulez-vous pouvoir modifier / déployer ce code sans redéployer / recompiler tout le projet? Parfois, il a été utile de simplement reconstruire le module, déployer et redémarrer l'application Web.

Il semble que dans votre cas, un référentiel de base et générique pourrait être utile à nouveau à l'avenir, il pourrait être utile de le séparer dans une nouvelle DLL si vous le pouvez.

FrustratedWithFormsDesigner
la source
Je pense que votre deuxième point est vraiment raisonnable. La division en modules devrait faciliter les temps de développement / test et de compilation.
WM