¿Cuándo debe estar una clase o módulo en un ensamblado / DLL separado?

22

¿Hay alguna guía para decidir cuándo una clase debe estar en su propio ensamblado / DLL? A menudo veo dos escuelas de pensamiento:

1) Cada "agrupación" de clases pertenece a su propia DLL, por ejemplo, repositorios, servicios, DTO, infraestructura, etc.

2) Todo debe estar en una única DLL pero separada por espacios de nombres / carpetas, por ejemplo, tener una DLL "Core" con espacios de nombres adicionales, por ejemplo, Core.Repositories, Core.Services, Core.DTO, etc.

En el trabajo, agrupamos todo en una sola Asamblea llamada "Negocios". Hay algunas carpetas, pero no hay una separación real: los objetos comerciales (con lógica, algunos de los cuales ni siquiera deberían ser clases) se agrupan en una carpeta "BusinessObjects" sin cuidado. Las cosas usadas en más de una clase están en una carpeta "Core". Las utilidades se encuentran en una carpeta de "Utilidades", la infraestructura de acceso a datos es una carpeta de "Datos": se entiende la idea.

Para un nuevo módulo en el que estoy trabajando, quiero / necesito tener una capa de acceso a datos separada (piense en una implementación de repositorio rudimentaria) pero no quiero simplemente arrojarla a la carpeta "BusinessObjects" con los otros 160 (!) clases allí. Al mismo tiempo, me preocupa crear una nueva Biblioteca de clases, ya que todos están acostumbrados a rellenar una clase en la Biblioteca individual; Sin embargo, una carpeta / espacio de nombres podría funcionar.

Wayne Molina
fuente

Respuestas:

12

Creo que es mejor tener más proyectos (es decir, ensamblados) con clases divididas por categoría en cada proyecto que un proyecto con todas estas clases en espacios de nombres separados. Debe intentar que sus proyectos sean reutilizables y que representen diferentes capas en una aplicación. Luego, puede reutilizar estos proyectos en futuras aplicaciones sin tener que incluir un montón de clases innecesarias.

Por ejemplo, en base a lo que ha mencionado, definitivamente tendría los siguientes proyectos:

  • Núcleo
  • Datos
  • Dominio (u BusinessObjects)
  • Servicios
  • Utilidades (o ayudantes)
Bernardo
fuente
2
Lamentablemente no puedo rechazar la respuesta. Esto es exactamente lo que se aconseja en nuestra firma. Como resultado, tener más o menos 6 proyectos, por ejemplo, para un sitio web de tamaño medio, no puede mantener la jerarquía de directorios basada en características, etc. Como resultado, termina con los seis proyectos con cada uno, es una gran pila de archivos imposible de navegar. Por lo general, las personas que aconsejan esto nunca han probado la estructura basada en características de los proyectos en proyectos relativamente grandes (perdón por el juicio directo, pero lidiar con los resultados de tales excesos es un verdadero dolor). La respuesta de jammycakes aconseja un enfoque correcto.
alehro
Dividir las clases por categoría es lo que conduce al desorden en grandes proyectos. Dividirlos por característica / aspecto. Entonces los espacios de nombres reflejarán esta división. Y toda la estructura reflejará el léxico de la especificación. Dividir las clases por categoría es como agregar abreviaturas de tipo a nombres de variables, por lo general es mal olor.
alehro
Es fácil abusar de ambas soluciones propuestas (es decir, demasiados proyectos o muy pocos). Encontrar un buen equilibrio donde se tenga en cuenta la reutilización y el bajo acoplamiento debería conducir a algo más comprobable y mantenible.
Bernard
16

"Tío Bob" Martin de Clean Code, SOLID Principles fame ha esbozado tres principios aquí :

  • Principio de equivalencia de reutilización de liberación: el gránulo de reutilización es el gránulo de liberación.
  • El principio de cierre común: las clases que cambian juntas se empaquetan juntas.
  • El principio de reutilización común: las clases que se usan juntas se empaquetan juntas.

La regla general es que debe mantener el número de proyectos en su solución lo más bajo posible. Solo divídalos si necesita hacerlo para implementar una o más historias de usuario específicas, o si tener un solo ensamblaje está causando problemas de rendimiento medibles (por lo general, una vez que alcanzan varios megabytes de tamaño).

jammycakes
fuente
2
+1 estos principios son buenas pautas. La separación de clases en diferentes ensamblajes introduce consideraciones de diseño adicionales (por ejemplo, qué versiones de cada ensamblaje pueden trabajar juntas), lo que aumenta la base de código general, lo que aumenta el costo de mantenimiento.
Ben
1
Sin embargo, además de estos principios, agregaría que a menudo es una buena idea empaquetar el código que probablemente sea reemplazado o intercambiado en un ensamblaje separado. Este código se beneficiará de las consideraciones adicionales necesarias para acoplar conjuntos separados y tenerlo separado hará que sea más fácil probar y comparar las diferentes versiones.
Ben
44
Sí, pero asegúrese de que exista un requisito genuino para que sea reemplazado o intercambiado. Por ejemplo, los marcos y las bibliotecas de terceros (paquetes NuGet y similares) por lo general necesitan soportar múltiples contenedores IOC y múltiples marcos de registro. Por otro lado, para el código de usuario, tales abstracciones suelen ser puramente especulativas, innecesarias y obstructivas, y al final nunca funcionan en las raras ocasiones en que realmente se necesitan.
jammycakes
"Solo divídalos si necesita hacerlo para implementar una o más historias de usuario específicas, o si tener un solo ensamblaje está causando problemas de rendimiento medibles (generalmente una vez que alcanzan varios megabytes de tamaño)". - totalmente aleatorio y no basado en consejos de realidad
hyankov
1
Lo siento, pero ¿qué es exactamente "totalmente aleatorio y no basado en la realidad"? Publiqué esta respuesta después de años de trabajar en soluciones que se dividieron en muchos más proyectos de los necesarios sin ninguna razón más que Así es como se supone que debes hacerlo. ¿El resultado? Tiempos de compilación glaciales y un infierno de dependencia (especialmente en los días previos a NuGet). Dividir las cosas en conjuntos separados tiene un costo, y si no hay ningún beneficio para compensar ese costo, entonces es solo un caso de robo del negocio.
jammycakes
4

Algunos otros principios rectores con los que trabajo:

  • ¿Crees que volverás a usar este código en otros proyectos? Para un grupo de aplicaciones web relacionadas, teníamos un módulo relacionado con la cuenta de usuario que todas las aplicaciones usaban, ya que todas usaban el mismo modelo para cuentas de usuario e inicios de sesión. He hecho cosas similares con las bibliotecas de geometría y matemáticas y las reutilicé en múltiples aplicaciones, simplemente al incluir la DLL.

  • ¿Desea poder modificar / implementar este código sin volver a implementar / recompilar todo el proyecto? A veces ha sido útil simplemente reconstruir el módulo, implementar y reiniciar la aplicación web.

Parece que en su caso un Repositorio básico y genérico podría ser útil nuevamente en el futuro, podría valer la pena separarlo en una nueva DLL si es posible.

FrustratedWithFormsDesigner
fuente
Creo que tu segundo punto es realmente razonable. La división en módulos debería ayudar a los tiempos de desarrollo / prueba y compilación.
WM