Otra forma de preguntar esto es; ¿Por qué los programas tienden a ser monolíticos?
Estoy pensando en algo como un paquete de animación como Maya, que la gente usa para varios flujos de trabajo diferentes.
Si las capacidades de animación y modelado se dividieran en su propia aplicación separada y se desarrollaran por separado, con archivos que se pasan entre ellos, ¿no serían más fáciles de mantener?
If the animation and modelling capabilities were split into their own separate application and developed separately, with files being passed between them, would they not be easier to maintain?
No mezcle más fácil de extender con un módulo más fácil de mantener , por sí mismo, no está libre de complicaciones o diseños dudosos. Maya puede ser el infierno en la tierra para mantener, mientras que sus complementos no lo son. O viceversa.Respuestas:
Si. En general, dos aplicaciones más pequeñas y menos complejas son mucho más fáciles de mantener que una sola grande.
Sin embargo, obtienes un nuevo tipo de error cuando todas las aplicaciones trabajan juntas para lograr un objetivo. Para que trabajen juntos, tienen que intercambiar mensajes y esta orquestación puede salir mal de varias maneras, aunque cada aplicación pueda funcionar perfectamente. Tener un millón de pequeñas aplicaciones tiene sus propios problemas especiales.
Una aplicación monolítica es realmente la opción predeterminada con la que termina cuando agrega más y más funciones a una sola aplicación. Es el enfoque más fácil cuando considera cada característica por sí sola. Solo una vez que ha crecido puede ver el conjunto y decir "sabes qué, esto funcionaría mejor si separáramos X e Y".
fuente
Las cosas rara vez son tan simples en la realidad.
Separarse definitivamente no ayuda a prevenir esos errores en primer lugar. A veces puede ayudar encontrar errores más rápido. Una aplicación que consta de componentes pequeños y aislados puede permitir pruebas más individuales (tipo de "unidad") para esos componentes, lo que puede hacer que a veces sea más fácil detectar la causa raíz de ciertos errores, y así permitir que los repare más rápido.
Sin embargo,
Incluso una aplicación que parece ser monolítica desde el exterior puede consistir en un montón de componentes comprobables por unidad en el interior, por lo que las pruebas unitarias no son necesariamente más difíciles para una aplicación monolítica.
Como ya mencionó Ewan, la interacción de varios componentes introduce riesgos y errores adicionales. Y depurar un sistema de aplicación con comunicación compleja entre procesos puede ser significativamente más difícil que depurar una aplicación de proceso único
Esto también depende en gran medida de qué tan bien se puede dividir una aplicación más grande en componentes, y qué tan amplias son las interfaces entre los componentes y cómo se usan esas interfaces.
En resumen, esto es a menudo una compensación, y nada donde una respuesta de "sí" o "no" es correcta en general.
¿Ellos? Mire a su alrededor, hay miles de millones de aplicaciones web en el mundo que no me parecen muy monolíticas, todo lo contrario. También hay muchos programas disponibles que proporcionan un modelo de complemento (AFAIK, incluso el software Maya que mencionó lo hace).
El "mantenimiento más fácil" aquí a menudo proviene del hecho de que diferentes partes de una aplicación pueden ser desarrolladas más fácilmente por diferentes equipos, por lo que una carga de trabajo mejor distribuida, equipos especializados con un enfoque más claro y más.
fuente
Tendré que estar en desacuerdo con la mayoría en este caso. Dividir una aplicación en dos por separado no hace que el código sea más fácil de mantener o razonar.
Separar el código en dos ejecutables solo cambia la estructura física del código, pero eso no es lo importante. Lo que decide qué tan compleja es una aplicación es cuán estrechamente acopladas están las diferentes partes que la componen. Esta no es una propiedad física, sino lógica .
Puede tener una aplicación monolítica que tenga una separación clara de diferentes preocupaciones e interfaces simples. Puede tener una arquitectura de microservicio que se base en los detalles de implementación de otros microservicios y esté estrechamente relacionada con todos los demás.
Lo cierto es que el proceso de cómo dividir una aplicación grande en otras más pequeñas es muy útil cuando se intenta establecer interfaces y requisitos claros para cada parte. En DDD hablar, eso vendría con sus contextos limitados. Pero si crea muchas aplicaciones pequeñas o una grande que tenga la misma estructura lógica es más una decisión técnica.
fuente
Más fácil de mantener una vez que haya terminado de dividirlos, sí. Pero dividirlos no siempre es fácil. Intentar dividir una parte de un programa en una biblioteca reutilizable revela dónde los desarrolladores originales no pudieron pensar dónde deberían estar las costuras . Si una parte de la aplicación está llegando a otra parte de la aplicación, puede ser difícil de solucionar. Rasgar las costuras te obliga a definir las API internas más claramente, y esto es lo que finalmente hace que la base del código sea más fácil de mantener. La reutilización y el mantenimiento son productos de costuras bien definidas.
fuente
Es importante recordar que la correlación no es causalidad.
Construir un monolito grande y luego dividirlo en varias partes pequeñas puede o no conducir a un buen diseño. ( Puede mejorar el diseño, pero no está garantizado).
Pero un buen diseño a menudo conduce a la construcción de un sistema como varias partes pequeñas en lugar de un gran monolito. (Un monolito puede ser el mejor diseño, es mucho menos probable que lo sea).
¿Por qué son mejores las piezas pequeñas? Porque son más fáciles de razonar. Y si es fácil razonar sobre la corrección, es más probable que obtenga un resultado correcto.
Para citar CAR Hoare:
Si ese es el caso, ¿por qué alguien construiría una solución innecesariamente complicada o monolítica? Hoare proporciona la respuesta en la siguiente oración:
Y más tarde en la misma fuente (la Conferencia del Premio Turing de 1980):
fuente
Esta no es una pregunta con un sí o un no. La pregunta no es solo la facilidad de mantenimiento, sino también el uso eficiente de las habilidades.
En general, una aplicación monolítica bien escrita es eficiente. La comunicación entre procesos y entre dispositivos no es barata. Romper un solo proceso disminuye la eficiencia. Sin embargo, ejecutar todo en un solo procesador puede sobrecargar el procesador y disminuir el rendimiento. Este es el problema básico de escalabilidad. Cuando la red entra en escena, el problema se vuelve más complicado.
Una aplicación monolítica bien escrita que puede operar eficientemente como un solo proceso en un solo servidor puede ser fácil de mantener y estar libre de defectos, pero aún así no puede ser un uso eficiente de las habilidades de codificación y arquitectura. El primer paso es dividir el proceso en bibliotecas que todavía se ejecutan como el mismo proceso, pero que están codificadas de forma independiente, siguiendo disciplinas de cohesión y acoplamiento flexible. Un buen trabajo en este nivel mejora la capacidad de mantenimiento y rara vez afecta el rendimiento.
La siguiente etapa es dividir el monolito en procesos separados. Esto es más difícil porque entras en territorio complicado. Es fácil introducir errores de condición de carrera. La sobrecarga de la comunicación aumenta y debe tener cuidado con las "interfaces comunicativas". Las recompensas son excelentes porque rompe una barrera de escalabilidad, pero también aumenta el potencial de defectos. Las aplicaciones multiproceso son más fáciles de mantener a nivel de módulo, pero el sistema general es más complicado y más difícil de solucionar. Las soluciones pueden ser endiabladamente complicadas.
Cuando los procesos se distribuyen a servidores separados oa una implementación de estilo de nube, los problemas se vuelven más difíciles y las recompensas son mayores. La escalabilidad se dispara. (Si está considerando una implementación en la nube que no ofrece escalabilidad, piense bien). Pero los problemas que entran en esta etapa pueden ser increíblemente difíciles de identificar y pensar.
fuente
No se . No hace que sea más fácil de mantener. Si algo bienvenido a más problemas.
¿Por qué?
Ahora tiene dos productos que necesitan:
Ahora tiene tres mercados de consumo: modeladores, animadores y animadores de modelistas.
Dicho esto, las bases de código más pequeñas son más fáciles de mantener a nivel de aplicación, simplemente no obtendrá un almuerzo gratis. Este es el mismo problema en el corazón de Micro-Service / Any-Modular-Architecture. No es una panacea, la dificultad de mantenimiento a nivel de aplicación se cambia por dificultades de mantenimiento a nivel de orquestación. Esos problemas siguen siendo problemas, simplemente ya no están en la base del código, deberán evitarse o resolverse.
Si resolver el problema a nivel de orquestación es más sencillo que resolverlo en cada nivel de aplicación, entonces tiene sentido dividirlo en dos bases de código y tratar los problemas de orquestación.
De lo contrario, no, simplemente no lo hagas, sería mejor para ti mejorar la modularidad interna de la aplicación en sí. Inserte secciones de código en bibliotecas coherentes y más fáciles de mantener para las que la aplicación actúa como complemento. Después de todo, un monolito es solo la capa de orquestación de un paisaje de biblioteca.
fuente
Hubo muchas buenas respuestas, pero como hay una división casi mortal, también arrojaré mi sombrero al ring.
En mi experiencia como ingeniero de software, he encontrado que esto no es un problema simple. Realmente depende del tamaño , la escala y el propósito de la aplicación. Las aplicaciones más antiguas en virtud de la inercia requerida para cambiarlas, generalmente son monolíticas, ya que fue una práctica común durante mucho tiempo (Maya calificaría en esta categoría). Supongo que estás hablando de nuevas aplicaciones en general.
En aplicaciones lo suficientemente pequeñas que son más o menos simples, la sobrecarga requerida para mantener muchas partes separadas generalmente excede la utilidad de tener la separación. Si puede ser mantenido por una persona, probablemente se puede hacer monolítico sin causar demasiados problemas. La excepción a esta regla es cuando tiene muchas partes diferentes (un frontend, backend, quizás algunas capas de datos intermedias) que están convenientemente separadas (lógicamente).
En aplicaciones muy grandes, incluso individuales, dividirlo tiene sentido en mi experiencia. Tiene la ventaja de reducir un subconjunto de la clase de errores posibles a cambio de otros errores (a veces más fáciles de resolver). En general, también puede tener equipos de personas trabajando de forma aislada, lo que mejora la productividad. Sin embargo, muchas aplicaciones en estos días se dividen con bastante precisión, a veces en detrimento propio. También he estado en equipos donde la aplicación se dividió en tantos microservicios innecesariamente que introdujo una sobrecarga cuando las cosas dejan de hablar entre sí. Además, tener que tener todo el conocimiento de cómo cada parte habla con las otras partes se vuelve mucho más difícil con cada división sucesiva. Hay un equilibrio, y como puede ver por las respuestas aquí, la forma de hacerlo no está muy clara,
fuente
Para las aplicaciones de IU es poco probable que disminuya la cantidad general de errores, pero cambiará el equilibrio de la mezcla de errores hacia los problemas causados por la comunicación.
Hablando de aplicaciones / sitios de IU que enfrentan los usuarios, los usuarios no son pacientes y exigen un tiempo de respuesta bajo. Esto hace que cualquier retraso en la comunicación se convierta en errores. Como resultado, se cambiará la posible disminución de errores debido a la menor complejidad de un solo componente con errores muy difíciles y el requisito de tiempo de comunicación entre procesos y máquinas.
Si las unidades de datos con las que se ocupa el programa son grandes (es decir, imágenes), cualquier retraso entre procesos sería más largo y más difícil de eliminar; algo como "aplicar transformación a imagen de 10 mb" ganará instantáneamente + 20 mb de disco / red IO además a 2 conversión de formato en memoria a formato serializado y viceversa. Realmente no hay mucho que puedas hacer para ocultarle al usuario el tiempo necesario para hacerlo.
Además, cualquier comunicación y especialmente la E / S del disco está sujeta a las comprobaciones de antivirus / cortafuegos; esto inevitablemente agrega otra capa de errores difíciles de reproducir e incluso más demoras.
La división del "programa" monolítico brilla cuando los retrasos en la comunicación no son críticos o ya son inevitables
Tenga en cuenta que esto se aplica tanto a las aplicaciones de escritorio como a los sitios web (la parte del programa orientada al usuario tiende a ser "monolítica"); todo el código de interacción del usuario vinculado a una sola pieza de datos generalmente se ejecuta en un solo proceso (no es inusual dividirlo) procesa por pieza de datos, como una página HTML o una imagen, pero es ortogonal a esta pregunta). Incluso para el sitio más básico con entrada del usuario, verá que la lógica de validación se ejecuta en el lado del cliente, incluso si hacerla en el lado del servidor sería más modular y reduciría la complejidad / duplicación de código.
fuente
¿Evitar? Bueno, no, en realidad no.
Es decir, todos los errores que ni siquiera sabía que tenía, que solo descubrió cuando intentó dividir todo ese desastre en partes más pequeñas. Entonces, en cierto modo, evitó que esos errores aparecieran en producción, pero los errores ya estaban allí.
Los errores en las aplicaciones monolíticas tienen el potencial de derribar todo el sistema y evitar que el usuario interactúe con su aplicación. Si divide esa aplicación en componentes, la mayoría de los errores, por diseño, solo afectarán a uno de los componentes.
Si desea mantener la experiencia del usuario igual, deberá incluir una nueva lógica para que todos esos componentes se comuniquen (a través de los servicios REST, a través de las llamadas del sistema operativo, lo que tenga) para que puedan interactuar sin problemas desde el punto de vista del usuario.
Como un simple ejemplo: su aplicación monolítica permite a los usuarios crear un modelo y animarlo sin salir de la aplicación. Divide la aplicación en dos componentes: modelado y animación. Ahora sus usuarios tienen que exportar el modelo de la aplicación de modelado a un archivo, luego encontrar el archivo y luego abrirlo con la aplicación de animación ... Seamos sinceros, a algunos usuarios no les va a gustar eso, por lo que debe incluir una nueva lógica para el archivo aplicación de modelado para exportar el archivo yInicie automáticamente la aplicación de animación y haga que abra el archivo. Y esta nueva lógica, tan simple como puede ser, puede tener una serie de errores relacionados con la serialización de datos, el acceso a los archivos y los permisos, los usuarios que cambian la ruta de instalación de las aplicaciones, etc.
Cuando decide dividir una aplicación monolítica en componentes más pequeños, (con suerte) lo hace con mucho más conocimiento y experiencia sobre el sistema que cuando se diseñó por primera vez, y gracias a eso puede aplicar una serie de refactores para hacer el código más limpio, más simple, más eficiente, más resistente, más seguro. Y esta refactorización puede, de alguna manera, ayudar a prevenir errores. Por supuesto, también puede aplicar la misma refactorización a la aplicación monolítica para evitar los mismos errores, pero no lo hace porque es tan monolítico que tiene miedo de tocar algo en la interfaz de usuario y romper la lógica empresarial ¯ \ _ (ツ) _ / ¯
Por lo tanto, no diría que está evitando errores simplemente dividiendo una aplicación monolítica en componentes más pequeños, pero de hecho está haciendo que sea más fácil llegar a un punto en el que los errores se pueden prevenir más fácilmente.
fuente