lunes, 13 de junio de 2022

Patrón de diseño Observer

 Observador (en inglés: Observer) es un patrón de diseño de software que define una dependencia del tipo uno a muchos entre objetos, de manera que cuando uno de los objetos cambia su estado, notifica este cambio a todos los dependientes.

Patrón de diseño basado en eventos

La arquitectura dirigida por eventos es un tipo de arquitectura software que se basa en la producción, detención y procesamiento de eventos y, si se considera oportuno, la reacción ante ellos. En la mayoría de los casos, el dispositivo que genera un evento no es el mismo que el que lo recibe y procesa.

Esta arquitectura está compuesta por productores y consumidores de eventos. El primero detecta los eventos y los representa como mensajes. No conoce al consumidor del evento ni el resultado que generará este último.

Una vez que se detecta un evento, este se transmite del productor a los consumidores a través de canales de eventos, donde se procesa de manera asíncrona con una plataforma para este fin. Cuando se produce un evento, se debe informar a los consumidores, quienes podrían procesarlo o simplemente recibirlo.

Procesamientos de eventos
Una arquitectura de este tipo puede se puede procesar 2 formas:
  • Publicación/suscripción-->la infraestructura de mensajería mantiene el seguimiento de las suscripciones. Cuando se publica un evento, se envía el evento a cada suscriptor. Después de que se recibe un evento, no se puede reproducir, y los nuevos suscriptores no ven el evento. (Rabbit)
  • Flujo de eventos-->los eventos se escriben en un registro. Los eventos siguen un orden estricto (dentro de una partición) y son duraderos. Los clientes no se suscriben al flujo, sino que un cliente puede leer desde cualquiera de sus partes. El cliente es responsable de avanzar su posición en el flujo. Esto significa que un cliente puede unirse en cualquier momento y puede reproducir los eventos. (Kakfka). Hay varios tipos de flujo de eventos:
    • El procesamiento de flujos de eventos utiliza una plataforma de transmisión de datos, como Apache Kafka, para incorporar los eventos y procesar o transformar su flujo. Este procesamiento se puede utilizar para detectar patrones significativos en los flujos.
    • El procesamiento de eventos simple surge cuando un evento desencadena inmediatamente una acción en el consumidor.
    • El procesamiento de eventos complejo requiere que un consumidor de eventos procese una serie de ellos para detectar patrones.
Estructura del evento
Un evento puede estar hecho de dos partes, el encabezado evento y el cuerpo evento. El encabezado de evento puede incluir información como el nombre del evento, fecha y hora para el evento, y el tipo de evento. El texto del evento es la parte que describe lo que ha ocurrido en realidad.

Capas del flujo del evento
Una arquitectura de evento disparado se basa en cuatro capas lógicas:
  1. Generador evento
  2. Canal de evento
  3. Motor de procesamiento de eventos
  4. Actividad de descarga dirigida por evento

Diferencias entre patrón DAO y Repository

 Una de las principales diferencias entre DAO y Repository se halla en el nivel en el que se ubican. El primero se ubica en un nivel mas bajo, mucho mas cerca a la fuente de datos, mientras que el segundo se ubica al mismo nivel de la capa de modelo de dominio, un poco más arriba que el primero.


Patrón de diseño repositorio

Patrón de diseño DAO

 DAO es el patrón mas usado para realizar la persistencia de los objetos de dominio, la forma mas común y básica de este patron es una clase que contiene las operaciones CRUD (Create, Read, Update, Delete).

Este patrón nos permite separar la lógica del negocio con respecto a la lógica de persistencia, de esta forma se pueden crear variedad de implementaciones de persistencia (usando ORM o ADO.Net) sin que lleguen a afectar al dominio. El problema es que mayormente este patrón no tiene una responsabilidad bien definida y muchos lo toman como una pasarela para hablar con la base de datos, creando un método para cada posible consulta que se realice o por cada actualización parcial

miércoles, 27 de abril de 2022

Usando Docker y Jenkins

Vamos a instalar un jenkins dentro de un contenedor docker para ellos vamos a utilizar la imagen oficial de jenkins jenkins/jenkins.

Podemos hacerlo de dos formas:
  • Comandos:
           Ejecutamos un contendor a partir de una imagen jenkins

                        docker run -p 8081:8080 -p 50000:50000 jenkins/jenkins

Abrimos un navegador web y accedemos a http://localhost:8081 para entrar en la consola de administración de Jenkins.
La contraseña que se solicita la podemos obtener en la salida de la  ejecución del comando anterior o ejecutando el siguiente comando:
                
                        docker ps | grep jenkins/jenkins

                        3183b39448bd   jenkins/jenkins:lts   "/sbin/tini -- /usr/…"   16 hours ago   Up 16 hours                           50000/tcp, 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp   jenkins1

Con el identificador del contenedor (la primera cadena de caracteres) lanzamos el siguiente comando y obtendremos la contraseña que nos solicitan al acceder a localhost:8081 :

docker exec -it 3183b39448bd cat /var/jenkins_home/secrets/initialAdminP

Si queremos que los cambios que realicemos en la configuración de Jenkins persistan incluso tras la destrucción del contenedor, debemos arrancar el contenedor con un volumen:

docker run -p 8081:8080 -p 50000:50000 -v jenkins_home:/var/jenkins_home jenkins/jenkins

  • Crear una imagen Docker con Jenkins a medida: para no tener que repetir las configuraciones manuales en cada nueva instalación podemos construir una imagen Docker con Jenkins a medida con todo lo que necesitemos. Lo podemos hacer con el DockerFile, añadiendo las instrucciones que necesitemos para crear la imagen o con Docker Compose (docker-compose.yml) con el que podemos ejecutar una serie de servicios como diferentes contenedores donde tienen que interactuar entre sí.
Usar Compose es básicamente un proceso de tres pasos.
    1. Defina el entorno de su aplicación con un Dockerfile para que pueda reproducirse en cualquier lugar.
    2. Defina los servicios que conforman su aplicación en docker-compose.yml para que puedan ejecutarse juntos en un entorno aislado.
    3. Por último, ejecute docker-compose up y Compose se iniciará y ejecutará toda la aplicación.
Estructura del DockerFile (vamos a utilizar lo mínimo)
FROM jenkins/jenkins:lts

LABEL MAINTAINER = PruebasDocker

Un ejemplo de utilización de Compose:

version: '3.8' -->

services:
  pruebas-jenkins-master:
    container_name: jenkins1
    build: .
    image: pruebas/jenkins:1.0.0
    ports:
      - "8081:8080"
    networks:
      - pruebas-ci-network  
    volumes:
      - pruebas-jenkins-master:/var/jenkins_home
      - /usr/bin/docker:/usr/bin/docker
      - /var/run/docker.sock:/var/run/docker.sock
    restart: always  

  pruebas-sonarqube:
    container_name: sonarqube
    image: sonarqube:community
    ports:
      - "9002:9000"
    restart: always          

volumes:
  pruebas-jenkins-master:
    name: pruebas-jenkins-master

networks:
  pruebas-ci-network:
    name: pruebas-ci-network
    driver: bridge
    ipam:
      driver: default
      config:
      - subnet: 172.16.239.0/24   

Veamos que significa algunos parámetro del docker-compose:

Puedes encontrar todos los parámetros que podemos definir en la documentación oficial.

version: Los archivos docker-compose.yml son versionados, lo que significa que es muy importante indicar la versión de las instrucciones que queremos darle. A medida de que Docker evoluciona, habrá nuevas versiones, pero de todos modos, siempre hay compatibilidad hacia atrás.

services: contiene una configuración que se aplica a cada contenedor iniciado para ese servicio.

image: imagen desde la que iniciar el contenedor. Puede ser un repositorio/etiqueta o una identificación de imagen parcial.

ports publicamos los puertos al host y podemos especificar el mapeo (HOST:CONTENEDOR).

networks: Son las redes a las que se unirán los contenedores, referenciando las entradas debajo de las redes de nivel superior.

volumes:  Montamos rutas o volúmenes, especificamos una ruta en el host (HOST:CONTENEDOR), Igual podemos especificar modo de acceso (HOST:CONTENEDOR:rw)

container-name: especifica un nombre de contenedor personalizado, en lugar de un nombre predeterminado generado.

restart: always: Indicamos la política de reinicio del contenedor si por cualquier condición se para.

martes, 19 de abril de 2022

Archivo jboss-web.xml

El jboss-web.xml es un archivo XML que contiene el comportamiento específico de JBossWeb de una aplicación web.

Por ejemplo he necesitado cambiar el nombre del contexto de la aplicación y he añadido entre las etiquetas jboss-web  <context-root>nombre</context-root>   


lunes, 11 de abril de 2022

Composición

La composición significa utilizar objetos dentro de otros objetos, es una técnica de diseño en Java para implementar una relación has-a.
La herencia de Java se utiliza con fines de reutilización de código y lo mismo podemos hacer mediante la composición. la herencia se usa normalmente para categorizar es decir por ejemplo Persona y Deportista, un Deportista es una Persona.

La composición se logra mediante el uso de una variable de instancia que hace referencia a otros objetos.Un ejemplo podría ser el diseñar las Clases Persona , Empresa y Dirección, tanto la Persona como la Empresa contienen ellas mismas la información de la dirección como propiedades individuales (calle, número..)

viernes, 25 de marzo de 2022

Patrones de diseño en capas múltiples

 Se basa en la separación de los procesos en unidades llamadas capas y es la arquitectura por defecto usados en aplicaciones JAVA.

Cada capa cumple una función especifica, como por ejemplo , podemos tener una capa para la parte visual de la aplicación y sólo tenemos en esta capa lo relacionado con ello.

Existen muchas implementaciones de esta arquitectura con diferentes cantidades de capas , según las necesidades del proyecto.



Patrones de diseño microkernels

 También conocido como arquitectura de plugin es muy popular para aplicaciones que necesitar utilizar software de terceros o agregar funcionalidades adicionales.

En esta arquitectura, los componentes se dividen en dos tipo:

  • Núcleo central, donde se encuentran todas las funciones criticas de la aplicación, es independiente y funcional, no requiere de ningún otro elemento para trabajar, sin embargo su funcionalidad se restringe a los procesos básicos, como gestión de hardware, interfaz gráfica o paquetes de uso común.
  • Plug-ins son módulos autocontenidos con una funcionalidad especializada y dependen del entorno que provee el núcleo central para extender o modificar su funcionalidad.

Entre las ventajas que ofrece esta arquitectura está su enorme flexibilidad, y la facilidad en realizar pruebas, lo que reduce el índice de errores.

Y por otro lado, una desventaja de está arquitectura es que al estar todo centralizado en un núcleo central, no son fáciles de escalar para  uso masivo.

Patrones de diseño microservicios

 Sí una aplicación monolítica concentra toda su funcionalidad en un solo punto, los microservicios son todos los puestos, distribuyendo os procesos en múltiples `partes más pequeñas e independientes.

Existen distintas maneras de implementar el patrón de diseño de microservicios,  podemos encontrar algunos elementos comunes entre ellos, como la distribución de tareas en bloques separados.

Estos bloques se llaman componentes de servicios y agrupan uno o varios componentes enfocados en realizar una tarea o un área de negocio de la aplicación. Esto componentes de servicios están completamente desacoplados entre sí y funcionan de forma completamente independiente lo que permite que cualquiera de ellos puedan ser reemplazados. sin afectar al resto de la aplicación.

Se aconseja en este tipo de patrón de diseño que las bbdd también estén desacopladas y se realice a través de la APIs Rest. Esto permite a los arquitectos tener puntos de entradas y salidas de datos que pueden estar a su vez encapsulados en sus propios componentes de servicios creando APIs consistentes y a la vez flexibles que pueden interactuar con diferentes bases de datos.


viernes, 18 de marzo de 2022

Patrones de diseño frente a Principios de diseño

  • Los principios de diseño son pautas generales que pueden guiar la estructura y las relaciones de clase. Todo desarrollo, antes o después, se ve sometido a cambios sobre la funcionalidad inicial o simplemente se requiere funcionalidad nueva. Estos principios nos proporcionan unas bases para la construcción de aplicaciones mucho más sólidas y robustas. Permiten que los cambios y la evolución del software tenga un mínimo impacto en el código ya desarrollado tratando de evitar que lo que funciona deje de funcionar y por ello que el coste del mantenimiento sea mucho menor.
  • Los patrones de diseño son soluciones probadas que resuelven problemas recurrentes. Dicho esto, la mayoría de las implementaciones prácticas de estos principios de diseño se realizan principalmente utilizando uno o más patrones de diseño.

Principios de diseño de software

 Algunos de estos Principios de Diseño son:

  • Encapsular lo que varía --> Considerado como los principios fundacionales del diseño, este principio se encuentra en funcionamiento en casi todos los patrones de diseño.                             Este principio sugiere, identificar los aspectos de sus aplicaciones que varían y separarlos de lo que permanece igual. Si un componente o módulo de tu aplicación está destinado a cambiar con frecuencia, entonces es una buena práctica separar esta parte del código de los estables para que después podamos ampliar o alterar la parte que varía sin afectar a los que no varían. La mayoría de los patrones de diseño como Strategy, Adapter, Facade, Decorator, Observer, etc siguen este principio. 
  • Favorecer la composición sobre la herencia-->La programación orientada a objetos proporciona 2 tipos de relaciones entre las clases y sus instancias: has-a (composición) e is-a (herencia). Este principio de diseño nos sugiere esencialmente que "la relación has-a debe ser preferida sobre la relación is-a.". A veces la herencia cuando se usa en exceso hace que nuestro código sea más rígido y no extensible.
  • Programa a interfaces-->Este principio de diseño nos guía para hacer uso de los tipos abstractos y no de los concretos. "Programar para interfaces" significa en realidad programar para un supertipo como una interfaz o una clase abstracta en java.
  • Acoplamiento flexible-->Es un principio que sugiere que "los componentes que interactúan entre sí deben ser independientes unos de otros, confiando lo menos posible en el conocimiento de otro componente". Este es un principio que seguimos en una arquitectura de microservicios. Los servicios que interactúan entre sí son independientes unos de otros. La interacción se basa estrictamente en los contratos de datos. La implementación del acoplamiento flexible variará de un escenario a otro en función del problema que tratemos de resolver.
  • Principios SÓLIDOS-->Es un conjunto de 5 principios que forman el acrónimo:
      • Principio de responsabilidad única (Single responsibility principle): Una clase debe tener una sola razón para cambiar. Este principio sugiere que las responsabilidades de una clase deben ser limitadas. No es una directriz clara porque no tenemos una instrucción específica en la que basarnos para juzgar esto. Sin embargo, la idea básica cuando se diseña usando este principio debería ser comprobar si los métodos dentro de una clase están cohesionados, es decir, ¿realmente necesitan estar juntos?
      • Principio de apertura y cierre (Open closed principle): Este principio establece que nuestro diseño debe estar abierto a la ampliación pero cerrado a las modificaciones. ¿Pero qué pasa si queremos añadir una nueva implementación en nuestro diseño? Podemos abordar esto de dos maneras:
        • Extendemos la funcionalidad existente a una nueva clase y añadimos las implementaciones allí
        • Utilizar la composición para aceptar nuevos comportamientos
      • Principio de sustitución de Liskov (Liskov’s substitution principle):A veces la herencia puede romper el sistema cuando sustituimos nuestra clase derivada por la clase padre. El principio de Liskov sugiere esencialmente que: "Siempre deberías poder sustituir los subtipos por su clase base".
      • Principio de segregación de la interfaz (Interface segregation principle): Este principio sugiere que una "interfaz debe estar siempre cohesionada". Es decir, los componentes de la interfaz deben estar muy relacionados entre sí. Cualquier componente que no esté relacionado entre sí debe ser separado y segregado.
      • Principio de inversión de la dependencia (Dependency inversion principle): El principio de inversión de la dependencia se utiliza para desacoplar los módulos de software. Establece que "los módulos de alto nivel no deben depender de los de bajo nivel. En cambio, ambos deben depender de la abstracción".
Los principios de diseño que se han tratado en este artículo son los más fundamentales. Hay algunos principios más como
  • DRY significa "Don't Repeat Yourself" (no te repitas), que establece que "cada pieza de conocimiento debe tener una representación única, inequívoca y autorizada dentro de un sistema". En otras palabras, significa que cada módulo debe ser único en sí mismo.
  • AHA significa Avoid Hasty Abstractions, que básicamente nos sugiere que a veces las abstracciones pueden llevar a una base de código rígida. Y cuando lo hacen, es mejor evitarlas.
  • KISS son las siglas de Keep It Simple Stupid (Manténgalo Simple y Estúpido), que sugiere que nuestra implementación debe ser lo más simple posible. Cualquier cosa que introduzca complicaciones en la implementación debe ser desacoplada utilizando cualquiera de los principios anteriores.