"¿De dónde crecen las piernas" o qué precede a la programación?

¡Hola a todos! El otro día, como parte de la plataforma educativa OTUS, se lanza un nuevo curso: "Arquitectura y patrones de diseño" . En relación con el comienzo, celebramos una lección abierta tradicional . Estudió las características de una aplicación monolítica, arquitecturas multinivel y sin servidor. Examinamos en detalle el sistema dirigido por eventos, el sistema orientado a servicios y la arquitectura de microservicios.



El profesor es Matvey Kalinin , especialista con más de 20 años de experiencia en programación y autor del curso "Arquitectura y patrones de diseño".

Un poco de historia


Inicialmente, los programas realmente resolvieron la tarea establecida por sí mismos y estaban bastante aislados. Pero con el tiempo, los programas crecieron y las personas comenzaron a comprender que la complejidad emergente de lo funcional comienza a afectar la velocidad de las mejoras, la confiabilidad y la resistencia a varios tipos de complicaciones.

De hecho, cuando tenemos uno o dos programas que son iguales y no cambian, escribir estos programas y garantizar su interacción no es difícil. Pero cuando hay más y más de ellos, los problemas no se pueden evitar, y no importa qué tipo de paquete de software esté involucrado.

Hoy, las aplicaciones, en su mayor parte, se distribuyen. Consisten en varios módulos y están interconectados por mensajes del sistema. Es decir, se obtiene un conglomerado bastante grande de programas que interactúan entre sí. Y para que interactúen con éxito, debemos considerar:

  • respuesta rápida;
  • banda ancha
  • desempeño en términos de una unidad de recursos;
  • capacidad de escalar;
  • capacidades de integración;
  • características de las plataformas utilizadas;
  • características de los procesos comerciales existentes;
  • y mucho más…

Al mismo tiempo, uno no puede evitar recordar la siguiente cita:

“El código de programa correcto no requiere grandes costos laborales para su creación y mantenimiento. Los cambios se realizan rápida y fácilmente. Los errores son pocos. Los costos de mano de obra son mínimos, mientras que la funcionalidad y la flexibilidad son máximas " .

Robert Cecil Martin

Es decir, escribir el programa idealmente una vez (si está bien escrito), las mejoras serán mínimas. ¿Cómo lograr esto y vincular uno y otro? Para responder a esta pregunta, pasamos a la historia.


?


Entonces, nos dimos cuenta de que necesitamos estructurar adecuadamente el producto de software. Pero, ¿cuál es el propósito de la arquitectura de software? Y de todos modos, ¿por qué usamos la frase "las piernas crecen" en el título de este artículo?

La cuestión es que cuando comienzas a programar, ya sabes lo que quieres construir. Inicialmente, todo comienza con un proceso comercial. Hay un pedido y un cliente que describe (al menos en palabras) cómo debe funcionar su sistema. Y al principio este sistema existe solo en la forma descrita por él. Luego, este proceso se formaliza y se describe, pero todavía es la mitad de la batalla, porque comienza un mayor desarrollo. Y el desarrollador necesita convertir este proceso en un producto de software, que debería ...

Entonces es hora de recordar otra cita:

"El objetivo de la arquitectura de software es reducir el trabajo humano requerido para crear y mantener un sistema".

Robert Cecil Martin


No debería sorprender que el objetivo esté formado por frases tan generales. El hecho es que la arquitectura vive en ideas abstractas. ¿Por qué? Porque la persona que se dedica a la arquitectura de software transforma la visión de un cliente comercial en la visión de un desarrollador . Y si hablamos del arquitecto del equipo de desarrollo y del arquitecto de la empresa, cada uno de ellos tiene un objetivo diferente, pero ambos se esfuerzan por una cosa : reducir los costos de mano de obra humana.

En este contexto, es interesante observar los valores del software:

  • El código cumple con los requisitos del proceso comercial;
  • Existe la posibilidad de un cambio rápido en el comportamiento.

Y aquí surge la pregunta: ¿qué es más importante, el funcionamiento del sistema o la simplicidad de cambiarlo ? Para responderlo, echemos un vistazo al programa desde el punto de vista de la utilidad:

si hay un programa que funciona correctamente que no permite cambios, dicho programa eventualmente se volverá irrelevante, cuando los requisitos cambien.

Si el programa no funciona correctamente, pero se pueden hacer cambios en él, entonces se puede hacer que funcione correctamente en el marco de los requisitos cambiantes. Este programa seguirá siendo útil siempre.

Paradigmas (modelos) de programación


¿Cuál crees que es el modelo de programación más famoso (el primero)? Por supuesto, un monolito . Es apropiado aquí nuevamente por un momento regresar a 1968 y recordar a Edsger Dijkstra, quien demostró que el uso incontrolado de las transiciones (ir a las instrucciones) es perjudicial para la estructura del programa. Sugirió reemplazar las transiciones con construcciones más comprensibles if / then / else y do / while / until.

Ahora las instrucciones de goto se pueden ver con menos frecuencia. Pero antes, las instrucciones de ir a Goto eran muy comunes. En general, esta es una forma de maldad, porque cuando ve el código en el que hay una instrucción goto, tiene la sensación de que es posible que no encuentre el punto exacto al que va. Y cuanto más goto, más complicado, es decir, obtenemos el código de espagueti. Ahora llamamos a ese código "spaghetti", donde, por ejemplo, 20 ifs anidados y, posiblemente, un goto. Este tampoco es un código muy claro. Imagine que tiene que pasar del 10 al 15 y está tratando de comprender cómo funciona el ciclo; eso es lo que Dijkstra tenía en mente.

El código Spaghetti es un programa mal estructurado, confuso y difícil de entender que contiene muchas declaraciones de goto (especialmente saltos), excepciones y otras construcciones que afectan la estructura. En general, un antipatrón de programación bien conocido y bastante común. Tal programa requiere mucho tiempo para comprender, apoyar y evaluar.

Programación estructural


Había lenguajes de programación, se dieron cuenta de algunos objetivos. Hasta cierto punto, la estructura de los programas no afectó mucho la implementación. Pero los programas crecieron, entre ellos se formaron numerosos lazos. Y en un momento, una persona sintió que era importante empaquetar el algoritmo en una estructura que fuera fácil de leer, probada. Los cambios han comenzado en el nivel de estructura del programa. Es decir, no solo el programa en sí tenía que satisfacer el resultado, sino que la estructura del programa también tenía que cumplir algún criterio.

Por lo tanto, cambiamos sin problemas a la programación estructural . Según él, el programa se construye sin usar el operador goto y consta de tres estructuras básicas de control:

  • secuencia,
  • derivación
  • ciclo.

Se utilizan subprogramas, el desarrollo en sí se lleva a cabo paso a paso, de arriba a abajo.
Y de nuevo, en 1966 ... Este año, Ole-Johan Dahl y Kristen Nyugor notaron que en el lenguaje ALGOL es posible mover el marco de la pila de llamadas de función a la memoria dinámica (montón), para que las variables locales declaradas dentro de la función puedan guardarse después salir de ella. Como resultado, la función se convirtió en el constructor de la clase, las variables locales en variables de instancia y las funciones anidadas en métodos. Esto condujo al descubrimiento del polimorfismo mediante el uso estricto de punteros de función.

Programación orientada a objetos


Como todos saben, en OOP, los programas se representan como una colección de objetos, cada uno de los cuales es una instancia de una clase particular, y las clases forman una jerarquía de herencia.

Principios básicos de estructuración:

  • abstracción;
  • herencia;
  • polimorfismo.

Puede ver todos estos principios desde otra perspectiva. Robert Martin desarrolló los principios de SOLID, que, por un lado, determinan cómo trabaja un programador con abstracciones y, por otro lado, forman el proceso de polimorfismo, herencia.

Programación imperativa


Un programa imperativo es similar a las órdenes que debe ejecutar una computadora. Dichos programas se caracterizan por:

  • las instrucciones están escritas en el código fuente del programa;
  • las instrucciones deben seguirse secuencialmente;
  • los datos obtenidos durante la ejecución de instrucciones anteriores se pueden leer de la memoria mediante instrucciones posteriores;
  • Los datos obtenidos al ejecutar la instrucción pueden escribirse en la memoria.

También un diseño muy antiguo. Las principales características de los lenguajes imperativos:

  • uso de variables con nombre;
  • uso de operador de asignación;
  • uso de expresiones compuestas;
  • uso de rutinas.

Sin embargo, continuamos el "viaje en el tiempo". Esta vez volveremos por un momento en 1936 (!). Es interesante que este año Alonzo Church inventó el cálculo lambda (o cálculo λ), que más tarde, en 1958, formó la base del lenguaje LISP inventado por John McCarthy. El concepto fundamental del cálculo λ es la inmutabilidad, es decir, la imposibilidad de cambiar los valores de los símbolos.

Programación funcional


La programación funcional implica hacer el cálculo de los resultados de las funciones a partir de los datos de origen y los resultados de otras funciones y no implica el almacenamiento explícito del estado del programa.

De hecho, esto significa que un lenguaje funcional no tiene una declaración de asignación.

Veamos la diferencia entre los estilos imperativos y funcionales usando un ejemplo:

#  
target = []  #   
for item in source_list:  #     
    trans1 = G(item)  #   G()
    trans2 = F(trans1)  #   F()
    target.append(trans2)  #     
#  
compose2 = lambda A, B: lambda x: A(B(x))
target = map(compose2(F, G), source_list)

Entonces, ¿qué es la arquitectura de software?


La arquitectura de software es un conjunto de decisiones sobre la organización de un sistema de software.

Incluye:

  • selección de elementos estructurales y sus interfaces;
  • el comportamiento de los elementos e interfaces seleccionados, su interacción;
  • combinando elementos seleccionados de estructura y comportamiento en sistemas más grandes;
  • Estilo arquitectónico que guía a toda la organización.

Tenga en cuenta: primero llegamos a la conclusión de que ir a goto no nos convenía, luego vimos que hay ciertas reglas (encapsulación, herencia, polimorfismo), luego nos dimos cuenta de que estas reglas no solo funcionan, sino de acuerdo con ciertos principios. El cuarto punto es el estilo arquitectónico, y hablaremos de ello más adelante.

El objetivo principal de la arquitectura es soportar el ciclo de vida del sistema. La buena arquitectura hace que el sistema sea fácil de aprender, fácil de desarrollar, mantener e implementar. El objetivo final es minimizar los costos durante la vida útil del sistema y maximizar la productividad del programador y, más precisamente, del equipo de desarrollo.

Al principio hablamos sobre las reglas que necesitábamos para escribir programas. Pero, además de escribir programas, también hay soporte, desarrollo e implementación. Es decir, la arquitectura no captura un área determinada de programación, sino todo el ciclo de desarrollo.

Una buena arquitectura debe proporcionar:

  1. Una variedad de casos de uso y operación efectiva del sistema.
  2. Simplicidad de mantenimiento del sistema.
  3. Facilidad de diseño del sistema.
  4. Fácil implementación del sistema.

Estilos arquitectonicos


Monolito


Antes que nada, hablemos del conocido monolito . Este estilo todavía se encuentra, sin embargo, en sistemas pequeños. La arquitectura monolítica significa que su aplicación es un módulo grande y conectado. Todos los componentes están diseñados para trabajar juntos, compartir memoria y recursos. Todas las funciones o su parte principal se concentran en un proceso o contenedor, que se divide en capas internas o bibliotecas.



Ventajas:

  1. Fácil de implementar. No es necesario perder el tiempo pensando en la comunicación entre procesos.
  2. Fácil de desarrollar pruebas de extremo a extremo.
  3. Fácil de implementar.
  4. Escale fácilmente con Loadbalancer en varias instancias de su aplicación.
  5. Fácil de operar.



Pero ahora tiene más deficiencias :

  1. Una fuerte cohesión conduce a un enredo con la evolución de la aplicación.
  2. El escalado independiente de componentes conduce a complicaciones y una nueva prueba completa de funcionalidad.
  3. Más difícil de entender.
  4. A medida que aumenta la complejidad, crece el tiempo de desarrollo.
  5. Falta de aislamiento de componentes.

Por un lado, el monolito es bueno, pero tan pronto como comienzas a desarrollarlo, surgen dificultades.

¿Qué es un servicio?


Ahora todos saben lo que es un servicio. Se puede definir como un recurso visible que realiza una tarea repetitiva y se describe mediante una instrucción externa.

Los servicios modernos tienen las siguientes características :

  • los servicios están orientados no a las capacidades de TI, sino a las necesidades del negocio;
  • los servicios son autosuficientes y se describen en términos de interfaces, operaciones, semántica, características dinámicas, políticas y propiedades del servicio;
  • la reutilización de los servicios es proporcionada por su planificación modular;
  • los acuerdos de servicio se celebran entre entidades llamadas proveedores y usuarios, y no afectan la implementación de los servicios mismos;
  • durante su ciclo de vida, los servicios se alojan y se hacen visibles a través de metadatos, registros y repositorios de servicios;
  • agregación: la combinación de procesos de negocios y aplicaciones complejas para una o varias empresas se basa en servicios poco acoplados.

Como resultado de las características anteriores, surgió el concepto de arquitectura orientada a servicios (SOA).

Arquitectura Orientada a Servicios (SOA)


SOA es un estilo arquitectónico para crear una arquitectura de TI empresarial, que utiliza los principios de orientación del servicio para lograr una conexión cercana entre el negocio y sus sistemas de información de soporte.



SOA tiene las siguientes características :

  1. Mejora la relación entre la arquitectura empresarial y la empresa.
  2. Le permite crear aplicaciones complejas a partir de conjuntos de servicios integrados.
  3. Crea procesos comerciales flexibles.
  4. No depende de un conjunto de tecnologías.
  5. Autónomo en el sentido de evolución e implementación independientes.

Un modelo de implementación SOA consiste en inteligencia y desarrollo de negocios e inteligencia y desarrollo de TI. El ensamblaje consiste en servicios de programación y construcción de aplicaciones complejas. El hospedaje consiste en hospedar aplicaciones y herramientas de tiempo de ejecución, como Enterprise Service Buses (ESB). En cuanto al manual , consiste en soportar el entorno operativo, monitorear el desempeño de los servicios y monitorear el cumplimiento de las políticas de servicio.



Arquitectura de microservicios


Es hora de hablar sobre la arquitectura de microservicios . En él, la aplicación consta de pequeñas aplicaciones de servicios independientes, cada una con sus propios recursos. Los servicios interactúan entre sí para realizar tareas relacionadas con sus oportunidades comerciales. Hay varias unidades de despliegue. Cada servicio se implementa de forma independiente.



Ventajas:

  1. Admite la modularidad de todo el sistema.
  2. Los servicios no relacionados son más fáciles de modificar para servir diferentes aplicaciones.
  3. Diferentes servicios pueden pertenecer a diferentes equipos.
  4. Los servicios de servicio se pueden reutilizar en toda la empresa.
  5. Más fácil de entender y probar.
  6. No está vinculado a la tecnología utilizada en otros servicios.
  7. El aislamiento del servicio aumenta la fiabilidad general de toda la funcionalidad.



Desventajas

  1. Dificultades con la implementación de la funcionalidad general (registro, derechos de acceso, etc.).
  2. Es difícil realizar pruebas de sistema de extremo a extremo.
  3. Operación y soporte más difíciles.
  4. Se necesita más equipo que para un monolito.
  5. El apoyo de varios equipos conduce a la coordinación de la interacción entre ellos.

Vale la pena señalar que en esta arquitectura es muy difícil hacer algo sin DevOps.

Arquitectura en capas


La arquitectura en capas es el patrón de arquitectura más común. También se llama arquitectura de n niveles, donde n es el número de niveles.

El sistema se divide en niveles, cada uno de los cuales interactúa con solo dos vecinos.

La arquitectura no implica ningún número obligatorio de niveles: puede haber tres, cuatro, cinco o más. Con mayor frecuencia, se utilizan sistemas de tres niveles: con un nivel de presentación (cliente), un nivel lógico y un nivel de datos.
Las capas más comunes son :

  • capa de presentación (para trabajar con usuarios);
  • capa de aplicación (servicio - seguridad, acceso);
  • capa de lógica de negocios (implementación de dominio);
  • capa de acceso a datos (representación de la interfaz a la base de datos).

Capas cerradas


El concepto de aislamiento de niveles separa estrictamente un nivel de otro: solo puede pasar de un nivel al siguiente y no puede saltar a través de varios a la vez.



Capas abiertas


El sistema le permite saltar sobre niveles abiertos y caer sobre los que se encuentran debajo.



MVC


El concepto de MVC fue descrito en 1978. La versión final del concepto MVC se publicó solo en 1988 en la revista Technology Object.

El objetivo principal es separar la lógica de negocios (modelo) de su visualización (presentación, vista). ¿Qué da?

  • Se aumenta la posibilidad de reutilización del código.
  • El usuario puede ver los mismos datos simultáneamente en diferentes contextos.



El modelo proporciona datos y responde a los comandos del controlador, cambiando su estado. La vista es responsable de mostrar los datos del modelo al usuario, respondiendo a los cambios del modelo. El controlador interpreta las acciones del usuario, notificando al modelo la necesidad de cambios.



Arquitectura dirigida por eventos


Otra arquitectura interesante. Se utiliza en el desarrollo e implementación de sistemas que transmiten eventos entre elementos de software débilmente acoplados.

Consiste en componentes dispares de procesamiento de eventos de un solo propósito que reciben y procesan eventos de forma asincrónica.

La plantilla consta de dos topologías principales: un intermediario y un intermediario.

Topología de revendedor


Hay procesos en los que se necesita control sobre la secuencia de pasos. Aquí somos intermediarios útiles.

Los principales tipos de componentes de arquitectura:

  • colas de eventos;
  • mediador de eventos;
  • canales de eventos;
  • controladores de eventos.

Evento Un

evento se puede definir como un "cambio significativo de estado". Un evento puede constar de dos partes:

  • encabezado (nombre del evento, marca de tiempo para el evento y tipo de evento);
  • cuerpo (describe lo que realmente sucedió).

Topología del mediador

El cliente envía un evento a la cola de eventos, que se utiliza para enviar el evento al mediador.

El mediador recibe el evento inicial y envía eventos asíncronos adicionales a los canales de eventos para cada paso del proceso.

Los procesadores de eventos, que escuchan los canales de eventos, reciben un evento de un intermediario y ejecutan cierta lógica comercial al procesar el evento.



Topología de corredor

La topología del corredor difiere de la topología intermedia en que no existe un mediador de eventos central. El flujo de mensajes se distribuye entre los componentes del procesador de eventos en una cadena a través de un intermediario de mensajes ligero (por ejemplo, ActiveMQ, HornetQ, etc.). Esta topología es útil cuando hay un flujo relativamente simple de procesamiento de eventos y no hay necesidad de una orquestación de eventos centralizada.

Cada componente de un procesador de eventos es responsable de procesar un evento y publicar un nuevo evento que indique la acción que se acaba de completar.



Si la primera situación era asíncrona "en algún lugar debajo", entonces la segunda situación es asíncrona, se podría decir, completamente. Un evento genera varios eventos, y pueden aumentar y aumentar.

Ventajas arquitectura dirigida por eventos:

  • los componentes están aislados y permiten que cada uno se finalice sin afectar al resto del sistema;
  • facilidad de despliegue;
  • alto rendimiento. Permite operaciones asincrónicas paralelas;
  • escala bien.

Desventajas

  • difícil de probar;
  • difícil de desarrollar debido a la asincronía pronunciada.

Arquitectura sin servidor


Esta es una forma de crear y ejecutar aplicaciones y servicios sin la necesidad de administrar la infraestructura. La aplicación aún se ejecuta en los servidores, pero la plataforma toma el control por completo de estos servidores.

Toda la infraestructura es compatible con proveedores externos, y la funcionalidad necesaria se ofrece en forma de servicios responsables de los procesos de autenticación, transmisión de mensajes, etc.

Se distingue la siguiente terminología:

  1. (Function-as-a-Service) — , .
  2. (Backend as a Service) — , - API, . , , , .

Si consideramos la arquitectura de "Cliente-servidor", la aplicación del servidor implementa la mayor parte de la lógica del sistema (autenticación, navegación de página, búsqueda, transacciones).



En la arquitectura sin servidor, todo es algo diferente. La autenticación se reemplaza por un servicio BaaS de terceros (servicio en la nube listo para usar), el acceso a la base de datos también se reemplaza por otro servicio BaaS. La lógica de la aplicación ya está parcialmente dentro del cliente, por ejemplo, rastreando la sesión de un usuario.

El cliente ya está en camino de convertirse en una aplicación de una página. La búsqueda se puede realizar a través del servicio de búsqueda (FaaS - funciona como un servicio). La función de compra también está aislada del cliente como un servicio separado (FaaS).



Bueno, eso es todo, si estás interesado en los detalles, mira el video completo. En cuanto al nuevo curso, puede familiarizarse con su programa aquí..

All Articles