Separando proyectos java

12

Tengo un gran proyecto de Java, y usamos Maven para nuestro ciclo de construcción. Este proyecto se usa ampliamente, en otros proyectos, en varias aplicaciones, algunas de las cuales están contenidas en él y otras en otros lugares ... Para ser honesto, es un poco desordenado (se agregaron varios bits en diferentes momentos para momentos específicos propósitos), y me gustaría limpiarlo un poco. Además, no está completamente probado (se han agregado muchos bits sin la unidad adecuada y las pruebas de integración), y hay algunas pruebas que tardan mucho tiempo en ejecutarse o en realidad no pasan ... (uh-oh), así que Las pruebas se desactivan en el ciclo de construcción de Maven (de nuevo, uh-oh).

Estoy pensando en separar este gran proyecto en proyectos específicos más pequeños, de modo que el subproyecto 'final' (o varios subproyectos) recoja los diversos subproyectos que necesitaría.

Mi pensamiento es el siguiente:

  • Si separo el gran proyecto en varios subproyectos, esto deja en claro cuál es la responsabilidad de cada proyecto.
  • Al separarme en subproyectos, puedo limpiar las pruebas de cada subproyecto individualmente y activar las pruebas para ese subproyecto en el ciclo de construcción de Maven.

Estoy un poco preocupado por el impacto que esto podría tener en el tiempo de construcción.

  • ¿Imponer una estructura en el proyecto grande (es decir, en subproyectos más pequeños) ralentizaría el compilador?

Además, tengo una ligera preocupación sobre el impacto que esto podría tener el tiempo de edición en IDEs (principalmente utilizamos Intellij). Intellij parece construir cada proyecto a su vez a través del árbol de dependencias, es decir, si C depende de B depende de A y yo cambio A, no intentará construir B a menos que A compile, y así sucesivamente. Podría decirse que es ventajoso, pero he descubierto que si, por ejemplo, cambio una interfaz en A que se usa ampliamente en B y C, lleva algún tiempo corregir todos los errores de ese cambio ...

Otra pregunta es cómo usar las clases de fábrica. Algunos aspectos del proyecto dependen de frascos externos. Ocasionalmente (afortunadamente no con frecuencia) estos se actualizan y tenemos que migrar. Tendemos a manejar esto mediante el uso de una clase Factory que apunta a las versiones correctas del código externo (por lo que no tenemos que cambiar todas las implementaciones en toda la base de código).

Por el momento, todo esto está en el gran proyecto, pero estoy pensando que al cambiar a subproyectos, podría desarrollar un nuevo proyecto para la implementación de un nuevo código externo, asegurarme de que el subproyecto sea completamente funcional y probado, y luego cambie las dependencias / clase de fábrica en el proyecto del usuario. Sin embargo, esto se hace más complicado por el uso extensivo de interfaces en todo el gran proyecto. Por ejemplo

  • subproyecto A - contiene interfaces
  • subproyecto B: depende de A para las interfaces y el antiguo jar externo
  • subproyecto C: depende de B (y, por lo tanto, A y jar externo antiguo), y contiene una clase Factory que utiliza las implementaciones de interfaces de B

Si necesito cambiar el frasco externo de B, puedo:

  • crear subproyecto B_ii - nuevamente depende de A, y ahora el nuevo jar externo
  • una vez completamente funcional, puedo agregar la dependencia de C a B_ii y cambiar la clase Factory para usar las nuevas implementaciones de interfaces.
  • cuando todo está funcionando, puedo eliminar la dependencia de C del B original y, si lo deseo, eliminar el subproyecto B.

  • ¿Es esa una forma sensata de hacer esto?

Entonces, en general, mis preguntas son:

  • ¿Alguien tiene alguna experiencia en la ruptura de grandes proyectos? ¿Hay algún consejo / truco que estaría dispuesto a compartir?
  • ¿Qué impacto tuvo esto en su desarrollo y tiempos de construcción?
  • ¿Qué consejo podría ofrecer para estructurar una ruptura de tal proyecto?
asombro
fuente
Tenga en cuenta que puede crear un proyecto principal con A, B y C desprotegidos uno al lado del otro. Esto permite, por ejemplo, que IntelliJ tenga los tres en la memoria al mismo tiempo y realice refactorizaciones a través de los límites del módulo.
Thorbjørn Ravn Andersen

Respuestas:

10

Voy a tomar un primer corte rápido en esto (¡excelente Q, por cierto!):

¿Imponer una estructura en el proyecto grande (es decir, en subproyectos más pequeños) ralentizaría el compilador?

No lo suficiente como para que importe, la sobrecarga está realmente en las invocaciones de Maven.

Además, tengo una ligera preocupación sobre el impacto que esto podría tener el tiempo de edición en IDEs (principalmente utilizamos Intellij). Intellij parece construir cada proyecto a su vez a través del árbol de dependencias, es decir, si C depende de B depende de A y yo cambio A, no intentará construir B a menos que A compile, y así sucesivamente. Podría decirse que es ventajoso, pero he descubierto que si, por ejemplo, cambio una interfaz en A que se usa ampliamente en B y C, lleva algún tiempo corregir todos los errores de ese cambio ...

Los diferentes IDE tienen sus puntos fuertes con respecto a los enlaces Maven y la gestión de dependencias. El estado actual del juego parece ser que en su mayoría solo funciona en los últimos Eclipse, Netbeans e IntelliJ, pero tendrá que enseñar a sus desarrolladores el doble golpe de emergencia de "Actualizar archivos fuente desde el disco y reconstruir todos los proyectos relacionados con Maven".

Sin embargo, creo que tengo que hacer eso cada vez menos en estos días. Tener una unidad SSD hace una gran diferencia aquí, por cierto.

cortar los párrafos de las clases de fábrica

La administración de dependencias es increíblemente importante, independientemente de qué tecnología (Maven / Ivy / lo que sea) use use para ayudarlo a implementarla.

Comenzaría por obtener informes exhaustivos del complemento de dependencia de Maven y hacer un balance de lo que tienes. En términos generales, establece la dependencia en la gestión de dependencias del POM lo más arriba posible en la cadena alimentaria, pero no más. Entonces, si dos de sus submódulos usan una dependencia externa, entonces llévela a su POM principal y así sucesivamente.

La actualización de JAR externos siempre debe realizarse como un mini proyecto adecuado. Evalúe por qué se está actualizando, modifique el código fuente para aprovechar las nuevas funciones / correcciones de errores, etc. Simplemente topar la versión sin este análisis y trabajar lo meterá en problemas.

Entonces, en general, mis preguntas son:

¿Alguien tiene alguna experiencia en la ruptura de grandes proyectos? ¿Hay algún consejo / truco que estaría dispuesto a compartir?

  • Las interfaces y la inyección de dependencia son su amigo.
  • El libro de Michael Feather sobre cómo lidiar efectivamente con el código heredado es una lectura obligada.
  • Las pruebas de integración son tu amigo
  • Dividir los subproyectos en foo-api (¡solo interfaces!) Y foo-core y tener módulos que solo dependen de foo-api ayuda mucho y obliga a la separación
  • Los módulos Jboss y / o OSGi pueden ayudar a forzar una separación limpia

¿Qué impacto tuvo esto en su desarrollo y tiempos de construcción?

Impacto muy pequeño en los tiempos de desarrollo y desarrollo: una ganancia masiva de tiempo para nuestra línea de entrega continua general

¿Qué consejo podría ofrecer para estructurar una ruptura de tal proyecto?

Haga las cosas pequeñas bien y las cosas grandes tienden a caerse limpiamente después. Así que divida las cosas poco a poco: no haga una reestructuración masiva de todo el lote a menos que tenga un alto porcentaje de cobertura con sus pruebas de integración.

Escriba pruebas de integración antes de la división: debería obtener más o menos los mismos resultados después de la división.

Dibuje diagramas de la modularidad que tiene ahora y hacia dónde desea llegar. Diseño de pasos intermedios.

No te rindas: un poco de afeitado Yak ahora crea la base para poder "construir y refactorizar rápidamente sin temor" Shameless plug -> de Ben and I, The Well-Grounded Java Developer .

¡La mejor de las suertes!

Martijn Verburg
fuente
0

Mi opinión sobre esto solo está separada cuando es necesario. Sin embargo, eso plantea la siguiente pregunta: ¿Cómo determino si es necesario?

  • Cuando el artefacto es diferente (es decir, no construya EAR, WAR, JAR, integración-testing-jar en el mismo proyecto).
  • Cuando durante la separación observa que es necesario que exista algún código en más de un artefacto, refactorícelos en uno nuevo.
  • Cuando algún otro proyecto fuera de tu equipo quiere usar algo que tienes en tu proyecto.

Una razón es que los desarrolladores tienen que lidiar con múltiples proyectos e intentar descubrir, aparte de qué paquete Java es a qué proyecto debería pertenecer. He estado en proyectos donde se volvió realmente anémico y tuve un proyecto que solo tenía cuatro clases implementando una funcionalidad solo porque esa es la dirección arquitectónica. Esto también fue antes de que Maven fuera popular, por lo que las rutas de clase y lo que no es necesario configurar en los scripts IDE y Ant.

La otra razón es que tendría más partes móviles con las que lidiar en términos de plomería de los archivos y probablemente tendría espagueti de dependencia antes de darse cuenta.

La refactorización también es más fácil dentro de un proyecto que en todos los proyectos.

Si el tiempo de compilación es un problema, simplemente proporcione un mejor hardware.

Arquímedes Trajano
fuente