Saltar al contenido

Etiqueta: development

Web API NetCore 3.0 con EF

Esta semana me toco estar en una reunión donde entre otras cosas se discutía el desarrollo de un nuevo microservicio que permita suplir alguna necesidad del negocio, y mientras se exponían puntos de vista sobre que deberíamos considerar, se tuvo un debate ameno sobre el uso de un ORM para tener acceso a datos en una base de datos relacional. Esto me llevo a querer hacer una demo sobre como funciona entity framework con NetCore 3.0, como se debería configurar para conectar a una base de datos y poder obtener información.

Así nace este post, tenemos un proyecto básico que nos permite conectarnos a una base de datos en MySQL, y que con EF podemos tener acceso a su información. Aproveche para configurar algunas cosas adicionales en el proyecto que también voy a explicar.

Primero quisiera comentar que mi historia con EF, no siempre fue la mejor. Cuando inicie en mi primer trabajo como practicante recuerdo que el sistema que tenían usaba ADO.Net con la ejecución de procedimientos almacenados, y en ese tiempo el lider técnico era un evangelizador de los beneficios que brindaba poder modificar los procedimientos sin necesidad de tener que realizar un deploy de la aplicación. Y asi es como yo inicialmente no tuve mucho contacto con EF, en trabajos venideros empece a aprender más sobre bases de datos, sobre distintos tipos de ORM, y poco a poco me fui interesando sobre el potencial que tenía EF, hasta que llego un proyecto en el que pude verlo en extenso y ser testigo de todo el potencial que esconde, claro con los problemas que también puede acarrear.

Digo problemas, por que si es mal usado puede devenir en un ineficiente manejo de la base de datos, todo depende de como lo usen los desarrolladores y que prácticas se sigan. Ahora si, veamos el proyecto.

Partimos ejecutando el siguiente script de la base de datos, que pueden encontrar en la carpeta scripts del repositorio.

Scripts

Veamos un poco el diagrama entidad relación de esta base de datos de demo.

Diagrama E-R

Como podemos ver, tenemos un diagrama con 12 tablas, entre las cuales de modo básico tenemos el manejo de las direcciones de una persona, y los estudiantes. Veremos más a detalle como se vincula la información en el video. Revisemos, ahora el proyecto.

Configuración del proyecto

Repasemos cada una de las carpetas y como se han dividido.

  • Configuration: Acá he puesto algunos archivos de para el correcto funcionamiento del proyecto, el archivo “ContainerProvider.cs” es para la inyección de dependencia, “ExceptionMiddleware.cs” nos permite capturar alguna excepción no controlada que se pueda generar en la ejecución de la aplicación y centralizar las acciones que tomen y finalmente “SwaggerServiceExtensions.cs” que nos permite realizar la configuración de swagger para generar documentación en nuestra API.
  • Context: En esta carpeta, se tiene el archivo “EFCoreContext.cs” que nos permite configurar entity framework, las relaciones entre las distintas tablas entre otras cosas.
  • Controller: Acá irán todos los controladores que necesitemos exponer en nuestra API.
  • Entity: La entidad que obtendremos de la base de datos.
  • Mapper: Estamos usando automapper en el proyecto, en esta carpeta pondremos los distintos profiles que requiramos crear para realizar el mapeo de nuestra información.
  • Model: En esta carpeta pondremos todas los DTO, que se expondrán como información por nuestro API.
  • Repository: Esta carpeta será nuestro repository, para el cual tenemos la interfaz, y su implementación.
  • Script: Acá estoy dejando ciertos archivos importantes para poder levantar la demo, el script de base de datos, diagrama E-R, etc.
  • Service: Acá pondremos la lógica de negocio que consideremos necesaria. También tiene la interfaz y su implementación.
  • Transversal: Pondremos acá distintas utilidades que podemos usar en el proyecto.

Para no entrar en detalle de la configuración en EF de todas las entidades, tomemos como modelo a la entidad “person”.

Configuración de la entidad person

Como podemos ver, nos valemos de las “DataAnnotations” que nos permitan indicar el nombre de la tabla que se va a mapear a una entidad, del mismo modo con las columnas, en caso posean un nombre distinto en la base de datos. Mediante estas anotaciones es posible especificar más información, como las relaciones entre las tablas o las llaves primarias y foraneas que se tienen. Sin embargo, particularmente prefiero indicar estas relaciones en el context.

Veamos que configuración se tiene en este archivo.

Configuración contexto

Como podemos ver, es necesario realizar un override al método “OnModelCreating” y ahí especificar las relaciones entre las distintas tablas, así como también las llaves foraneas. Entre otro tipo de configuración con la base de datos que sea necesario realizar. Revisemos ahora el repositorio y como estamos obteniendo la información.

Llamada al contexto

Tenemos una interfaz que declara un método asyncrono que permite obtener el listado de estudiantes y en su implementación hacemos uso de EF para obtener esta información con los joins que son necesarios para nuestra demo. En la capa de servicio, tenemos los siguiente.

Servicio

Tenemos la implementación de la interfaz para la capa de servicio, como se puede ver, se obtiene del repositorio la información y luego se realiza un mapping de estos campos para el DTO que terminaremos devolviendo en el API. Notemos que no tenemos ningún try-catch hasta ahora en nuestro código, esto dado que se a implementado un middleware que nos permita centralizar los errores. Veamos como se implemento esto.

ExceptionMiddleware

Si ocurriera algún error en la ejecución de nuestra solicitud, se terminará entrando en nuestro método “HandleExceptionAsync” acá podríamos tomar las acciones necesarias, para la demo estoy devolviendo un error genérico que un GUID autogenerado por cada error.

El proyecto no es nada del otro mundo, con respecto a lo que ya hayamos visto con net core, pero me parecío interesante hacer una guía sobre como usar EF. Pueden revisar el video en donde seguro hablo a más detalle sobre algunas cosas de la implementación del proyecto, el proyecto demo también esta en github por si quieres revisarlo y si hubiera alguna duda adicional pueden dejarme una pregunta.

REPO: GITHUB

Saludos.

3 comentarios

Logging en Cloudwatch (Amazon Web Services)

Hace un tiempo en el trabajo tuve la oportunidad de ser parte de un proyecto que buscaba centralizar el logging de múltiples aplicaciones en una sola y persistir esta información no en una base de datos, sino en uno de los servició que ofrece Amazon Web Services (AWS) para este fin que es CloudWatch. Esto me llevo a todo un proceso de investigación y recabar información sobre la mejor manera de realizar este grabado de log. De ese proceso de investigación nace este post.

Primero, hay que decir que AWS es todo un mundo por si solo, se tienen muchos servicios orientados a necesidades especificas en el proceso de desarrollo de software, asi como también de cara al despliegue de aplicaciones, seguridad, networking, almacenamiento, entre otros. Esto me lleva a comentarles que en adelante intentaré realizar más post sobre los servicios que ofrecen y como se pueden usar, esto obviamente lo hago para compartir la información y también afianzar lo poco o mucho que puedo ir aprendiendo. Vamos al tema en específico, cuando me planteo realizar esta aplicación, que por lo demás tenía claro que era un API en net core, busque que alternativas tengo para comunicarme con AWS y específicamente con cloudwatch, encontrando las siguientes:

  • SDK de Amazon
  • Diversas librerias que se integran con aplicaciones de loggers (Nlog, Log4net, Serilog, etc).

Debido al tiempo que tenía para realizar la aplicación y a la experiencia que tenía con las herramientas que tenía a la mano, termine decantandome por usar la librería para Nlog. Esto, por que ya tenía el conocimiento de Nlog, te instala un módulo que le permite tener acceso a cierta información para grabar un log. Sin embargo, esto me trajo algunas complicaciones para el caso de net core que ya más adelante veremos.

Proyecto

La demo, consta de una aplicación web api generada de 0, con lo mímimo necesario apra que pueda funcionar nuestro log en AWS. Las librerías a usar, son las siguientes:

  • AWS.Logger.NLog (v1.5.1)
  • NLog (v4.6.8)

Me interesa explicar como se lleva a cabo esta configuración, dado que lo demás es lo típico con cualquier aplicación en net core. En nuestro archivo “Startup.cs” tenemos que realizar las siguientes configuraciones:

Configuración del target en nlog para AWS

Config target

Como se puede ver, se crea una nueva clase del target (que esta disponible luego de instalar la librería) y tenemos que proporciarle cierta información:

  • Credentials: Se le indica el tipo de autenticación que se va a usar, para la demo he creado en AWS un IAM (manejo de indentidad) que sólo tenga permisos para poder comunicarse con cloudwatch (en otro post voy a ahondar en como AWS maneja la seguridad), existen otras tipos de credenciales, por ejemplo en el trabajo dado que nuestro aplicativo está en un EC2 se puede obtener la autenticación de la máquina.
  • LogGroup: Cada uno de los log que se graben están asociados a un log group, que digamos que es la instancia que se crea para almacenar todos los logs que provengan de ese cliente.
  • Region: Es la región en la cual está nuestra cuenta de AWS. Entendamos que esta plataforma esta mundial, y por lo tanto tiene servidores en distintos lugares del mundo, esto debido a buscar que sus servicios sean escalables y redundantes ante cualquier eventualidad y a ti como usuario te permite elegir consumir los servicios que ofrecen en distintos lugares del mundo. De esto también se puede ahondar más adelante.
  • LogStreamNameSuffix y LogStreamNamePrefix : Cuando se crea una nueva instancia en un log group esta se crea con un identificador único la cual es un GUID más un timestamp, esto es así si no se le proporciona ningún parámetro. En cambio si le pasamos un sufijo o un prefijo ya no se genera el GUID, sino se le concatena a lo establecido (prefijo y sufijo) la marca de tiempo.
  • Layout: Este punto lo deje al final por que es donde probablemente me deba explayar más. como ven es un texto, pero me estoy valiendo de los layouts renders proporcionados por nlog para obtener cierta información y evitarme tener que obtenerla por otros medios.

Ampliando el último punto, pueden encontrar más información sobre esto en este link.

Una ves que se configura el target de AWS, es necesario indicarle a nlog que debe de usarlo, asociarle los eventos del log y la configuración a logmanager. Veamos como se hace esto en código.

Configuración de nlog

Como se puede ver, se crea el un nuevo “LoggingConfiguration”, a este se le asocia el target a aws creado y además las reglas a las cuales estará vinculado nuestro target, finalmente también estoy indicando donde estará el log interno de nlog y le paso toda está configuración a logmanager.

Para cierta información que en teoría debería ser proporcionada por los layout render, pero que parece que en net core no funcionan correctamente, me valí de un Middleware, acá pueden ver que información de obtiene de ahí.

Configuración del middleware

Se está obteniendo la URL que se está consumiendo, el username que consume la aplicación, si se tuviera como header, el método http (PUT, POST, GET, etc). Finalmente, cuando un evento de log (para nuestra demo excepción) se lleva a cabo.

Evento de error disparándose
Disparando el log

Finalmente, veamos nuestro appsettings para ver que configuración tenemos ahí.

Configuración del appsetting

El accesskey y secretkey son las que cree para la demo, este usuario ya ha sido eliminado de mi cuenta de aws.

Realicemos la prueba de nuestra demo para ver si efectivamente tenemos nuestro log en AWS ahora. Primero tomemos algunas capturas del ambiente antes de ejecutar nuestra demo.

Log Groups en AWS

Listado de log groups

Postman

Get request

Levantamos la solución (con lo que nuestro log group ya debería haberse creado en AWS) y realizamos la solicitud al api.

Entro a la excepción
Esta por enviarse el log a AWS
Respuesta en postman
Validamos que se creo nuestro log group
Validamos nuestro log stream
El log grabado

Como podemos ver (si hacemos zoom) nuestro log se ha grabado correctamente en AWS.

Con esto llegamos al fin del post, dejo el link al repositorio donde está el código fuente por si quieres revisarlo y un video de la demo.


REPO: GITHUB

Saludos.

2 comentarios

Buenas Practicas Logging

En este post, me dedicaré a explicar que buenas practicas en logging se deben seguir cuando desarrollamos una aplicación web, que quede claro que este es un enfoque personal, que he podido formular con cierta experiencia.

Por otro lado, es necesario comentar que si bien es cierto el ejemplo en cuestión, lo voy a realizar usando Spring boot, la información que se debe obtener para enriquecer el log, puede variar de acuerdo a la tecnología que se va a usar (Net Core, Framework, Node, etc).

Empecemos primero, explicando un poco sobre los niveles de logging que se tienen y en qué caso es necesario usar uno u otro.

Niveles de Logging

Como se puede ver en la imagen, el logging no solo sirve para capturar errores (lo cual es fundamental en el proceso de desarrollo y para el posterior soporte a las aplicaciones), sino también para determinar que feature es el más usado, información de configuración de la aplicación, entre otras.

Por otro lado, el logging se puede desarrollar en múltiples appenders (este nombre es usado en log4j o log4net), como una buena práctica es necesario que como mínimo se realice el grabado del log en dos appenders, siendo una de estos la base de datos y el otro un archivo de texto, en una ruta en específico (pudiendo ser dentro de la solución desplegada). Basicamente en este post me enfocaré en explicar la información que se debe guardar en un log de error (FATAL y ERROR). La información que vamos a considerar es la siguiente:

  • Fecha y hora del log
  • Nivel del log (FATAL, WARNING, etc)
  • Mensaje
  • Stack de la excepción
  • Payload (en caso que la consulta sea un GET, DELETE se debe obtener la información enviada en el url, de ser un POST, UPDATE, PATCH El body)
  • Callsite (el endpoint que es consumido que en el cual se termina generando el error)
  • La acción bajo la cual se produce el error (nombre del método en el controlador)
  • El usuario con el cual se produjo el error.
  • El nombre del método en el cual se generó el error (de la capa de lógica de negocio)
  • El nombre de la aplicación en la cual se generar el error (varía de solución en solución)

Claro, que de acuerdo a qué librería se este usando para realizar este grabado del log es posible tener todo tipo de appenders, uno que me llama mucho la atención es el que nos permite enviar esta información a herramientas como elastic search y luego al integrarlo con otras herramientas como grafana para generar reportes.

En nuestra aplicación demo, nos valdremos que ciertas características del framework para poder tener acceso a la información antes detallada.

HandlerInterceptorAdapter

Al implementar esta clase abstracta y hacer un override al método preHandle tenemos acceso en el pipeline del framework, además de anotarla como “Component”. Como podemos ver en la siguiente imagen.

Implementación del override del preHandle

Como podemos ver, nos valemos del HttpServletRequest para tener acceso a mucha información necesaria para enriquecer nuestro logging, detallemos cada uno de los puntos que nos servirán:

  • La url a la cual se está consultando la obtenemos con el método “getRequestURI”.
  • Para obtener el nombre del método, nos valemos de una pequeña lógica que obtiene el indice del último luego del “/” y con esto hace un substring para poder obtener el último texto.
  • Para el hostname de la máquina, nos valemos de la librería “java.net.InetAddress”.
  • Es necesario validar si la petición es del tipo “GET” para de este modo obtener los query params que se esten enviando. Si este fuera el caso usamos “getQueryString” para este fin.
  • Obtenemos un parámetro denominado “username” que se está enviando en cada una de las peticiones, sea cual sea el método http, esto con la finalidad de guardar el usuario al cual está vinculado el log. Para esto tenemos opción de guardarlo en sesión en el caso nuestra solución sea del tipo MVC, pero al tener un Web API no es posible. Podrían usar un interceptos en angular (de ser este su caso) para que a cada una de las peticiones le adicione este parámetro.

Con esto, cubrimos en cierto aspecto la información que necesitamos para nuestro logging. Sin embargo, tenemos que considerar también las solicitudes http del tipo POST, y que por lo tanto tienen un body.

RequestBodyAdviceAdapter

Nos valdremos de esta clase abstracta, y especificamente en nuestra implementación haremos el override de “supports” y de “affterBodyRead” como podemos ver en la siguiente imagen:

Override de métodos en la implementación de HandlerInterceptorAdapter

Además, es necesario decorar la clase con la etiqueta “ControllerAdvice”. Veamos que información obtenemos de este método:

  • El payload del body, el cual obtenemos del Object que se recibe de “afterBodyRead”, en mi caso se hace un “toString” dado que en las clases implementadas se está haciendo un override a esté metodo para darle la forma de json, como podemos ver en la siguiente imágen.
Override del “toString” en la clase

Además, es muy importante en nuestro logging tener grabadas en nombre del método en el cual se realizo la caída del sistema. Este método puede estar en el stack de la excepción, pero para tenerlo directamente me estoy valiendo de una característica propia del framework, la cual se denomina “aspects” en spring, en algún otro post me dedicaré a explicar los tipos que hay y como se ejecutan en detalle, en este post sólo presentaré como se están usando.

Aspects

Spring nos brinda una herramienta muy potente, la cual es denominada programación orientada aspectos, lo cual en pocas palabras, puede facilitarnos la vida en múltiples escenarios y evitarnos repetir código innecesariamente. Veamos la siguiente imagen:

Implementación de un aspecto

Se creo un aspecto y se decoro con las etiquetas “Aspect” y “Component” para que se registre por el framework, lo que nos permite un aspecto es que esta sección de código de ejecute en ciertas condiciones. Para el caso, se llevará a cabo en la siguiente sintaxis:

“* com.project.loggingdoitright.service.implementation.*.*(..)”

Lo cual nos indica que el aspecto coberturará todos los métodos que se encuentren en el paquete “implementation” que tengan cualquier nombre (veamos los *.*), que tengan cualquier tipo de entrada (veamos los dos puntos dentro de los paréntesis) y el asterisco inicial, indica que coberturará cualquier tipo de devolución. Como variable de entrada de nuestros aspectos tenemos a la clase “JoinPoint”, la cual posee varia información interesante, para lo que necesitamos usaremos “getSignature().getName()” que nos brinda el nombre del método en el cual se registra el error y el cual llama nuestro aspecto.

Finalmente, para realizar el grabado del log y generar un identificador único del log, el cual debe ser proporcionado al usuario del aplicativo. Para esto, usamos algo que ya se explicado antes. Un “ControllerAdvice”, el cual actua como interceptor de los errores.

Finalmente, veamos como queda nuestro registro del log.

Divido la fila asi, para que se pueda visualizar mejor.

Finalmente, puede visualizar el video correspondiente a este post, donde se detalla paso a paso el funcionamiento de la aplicación, así como su prueba consumiento las APIs desde postman.

Espero que les haya sido de ayuda, como siempre dejo el código del proyecto en el repositorio.

REPO: GITHUB

Saludos.

Deja un comentario

JWT en Spring Boot y Spring Security

Hace poco tiempo participe en una hackaton con unos amigos, y cuando empezamos a desarrollar ya la aplicación luego de haber analizado lo que se buscaba solucionar, yo me valí de una plantilla en spring que había estado trabajando en mi tiempo libre, era la primera ves que la ponía a prueba en un escenario real y el sentido era que ahorraramos tiempo en configurar cosas que siempre son necesarias en todos los aplicativos. Para resumir un poco esta parte, la plantilla se porto como debía. Esta experiencia me lleva a realizar este post, en el que voy a explicar como se configura spring security y la autenticación en base de datos, además de que configurar para poder usarlo con una aplicación SPA del lado del frontend, como puede ser Angular, React, Vue.js entre otras.

Como deben de saber spring security es la librería de spring que nos brinda una visión dogmática de como se debería de implementar la seguridad en una aplicativo. Esto, claro siguiente todo un marco de buenas prácticas que muy probablemente si quisiéramos implementarla de 0, tranquilamente puede ser un proyecto entero sólo para esto. Gracias a esto, es que nos permite ahorrarnos mucho tiempo y nos brinda la seguridad de que estamos siguiendo un marco de buenas prácticas. Para nuestro caso, usaremos json web token (jwt) el cual es una de las alternativas que tenemos para implementar la autenticación.

Nuestra aplicación demo consta de distintas capas, ampliamente explicadas en post anteriores, tenemos un archivo de configuración “application.yml”, en la sección “security” hemos definido cierta información importante, entre las cuales está:

  • La url del enpoint de autenticación.
  • El tiempo máximo de expiración de un token.
  • La llave secreta que se usará para poder generar el JWT.

Se ha definido un paquete de configuración, y dentro de este un paquete de jwt, en el cual se tienen distintas clases necesarias para configurar, como es obvio en el pom del archivo se están añadiendo las dependencias de spring security.

Dependencia en el POM
Clases de configuración

Tenemos un controlador llamada “SecurityController”, en el cual tenemos los diferentes endpoint:

  • Authenticate [POST]: Nos permite iniciar sesión en el sistema, retorna el token.
  • Refresh [GET]: Permite refrescar el token que se ha generado, antes de que expire.
  • Create [POST]: Permite crear un usuario.

Tenemos en la capa service de nuestro aplicativo un “SecurityService” que es donde se realiza la lógica necesaria para autenticarse, refrescar el token y el crear un nuevo usuario, Por otro lado, en el repository, respetando el principio de single responsability tenemos tres clases (Person, Session y User) para el acceso a la base de datos.

El esquema de base de datos que vamos a usar es el siguiente:

Modelo E-R usado

Vamos a usar una clase a modo de helper, donde tendremos varios métodos utilitarios que nos van a permitir obtener información del token que generemos e incluso generarlo. Esta clase se llamará “JwtUtil”.

Métodos en JwtUtil

Luego es necesario crear una clase que extienda de “OncePerRequestFilter”, en el cual podamos hacer un override al método “doFilterInternal”, en le cual haremos un filtro a las peticiones que recibamos para determinar si poseen token o no y que acciones se deberían tomar. Esta clase la llamaremos “JwtTokenAuthorizationOncePerRequestFilter” y luego veremos donde debemos configurarla para que este filtro se lleve a cabo.

Clase JwtTokenAuthorizationOncePerRequestFilter

También es necesario crear un clase que extienda de “AuthenticationEntryPoint” para poder hacer un override del método “commence”, el cual será llamado cuando se determine que ser esta intentando acceder a un endpoint que requiere que se le pase un header de authorization. Esta clase, llevará el nombre de “JwtUnAuthorizedResponseAuthenticationEntryPoint”.

Clase JwtUnAuthorizedResponseAuthenticationEntryPoint

Finalmente, toda la magia ocurre en nuestra clase SecurityConfiguration, la cual extiende de “WebSecurityConfigurerAdapter”, en esta realizamos múltiples configuraciones que son propias de spring security, Entraremos al detalle de algunas en el post, pero en el video me daré más tiempo para explicar la configuración de esta clase. Uno de los override es al método “configure”, considerando que este método en la clase de la que estamos extiendo tiene varios overloads, el que explicaré en el post es el que posee como parametro de entrada un “HttpSecurity”, en debemos realizar las siguientes configuraciones:

  • Deshabilitar el CSRF
  • Indicar que authentication entrypoint (acá usamos nuestra entrypoint personalizado).
  • Indicamos que no usaremos sesiones, esto cambiando la configuración de “sessionCreationPolicy”.
  • Configuramos el CORS, para que permita todos los tipos de peticiones (GET, POST, DELETE, UPDATE, etc).
  • Añadimos el filtro que personalizamos anteriormente, esto con el “addFilterBefore”.
Clase SecurityConfiguration

Finalmente, revisemos nuestro “SecurityController”:

SecurityController

Realizemos algunas pruebas en postman de los distintos métodos.

  • Usuario: wgutierrez
  • Contraseña: $2a$10$3zHzb.Npv1hfZbLEU5qsdOju/tk2je6W6PnNnY.c1ujWPcZh4PL6e

Petición:

Solicitud al Authenticate endpoint

Respuesta:

Respuesta el Authenticate endpoint

Como se puede ver, se nos ha generado un token, si vamos a una página como jwt.io y ponemos el token que ha sido generado, tenemos la siguiente información:

Información del token generado

Los métodos de refresh y creación, pueden verlos a más detalle en el video que como de costumbre cuelgo con los post. Espero que les haya sido de ayuda, si tienen alguna duda les dejo el correspondiente repositorio en github, y los medios de contacto correspondientes.

REPO: GITHUB

Saludos.

Deja un comentario

Interface Segregation Principle

El penúltimo principio del que nos toca escribir hoy es el de la segregación de interfaces. El corolario de este principio nos indica lo siguiente:

Los clientes no deberían ser forzados a depender de métodos que no vayan a usar.

El problema que se tiene con las grandes interfaces podemos verlo claramente en la clase MembershipProvider que usaba microsoft hace algún tiempo.

MembershipProvider de Microsoft

Hace un tiempo, microsoft sólo soportaba SQL Server como provider, por lo que si se necesitaba usar algún otro provider, era necesario implementar todos los métodos que pueden ver en la imagen.

El resultado de violar este principio, nos lleva a no implementar completamente las interfaces, dado que no es necesario usar todos métodos, esto hace que poco a poco empecemos a adquirir en nuestro código más dependencias, lo cual es muy probable que nos termine ocasionando las siguientes complicaciones:

  • Más acoplamiento.
  • Código más frágil.
  • Más dificultas de probar el código.
  • Más dificultas en los pases a producción.

Básicamente, lo que nos indica es que debemos evitar crear interfaces con demasiados métodos. Esto lo conseguimos separando las responsabilidades de nuestra interfaces. Para poder determinar si en nuestros proyectos estamos violando este principio, es necesario buscar grandes interfaces, una muestra clara la podemos ver si en nuestro código encontramos algún método que implementa una interfaz y una de estas implementaciones tienen un “NotImplementedException”.

Respetar Interface Segregation Principle

Si se diera el caso en que tenemos refactorizar un proyecto para que se respete este principio, podemos considerar las siguientes estrategias, sea cual sea el caso en el que nos encontremos.

  • Romper una interfaz grande en pequeñas (siempre y cuando nosotros tengamos control de las interfaces a refactorizar).
  • Si se diera el caso en el que dependemos de una gran interfaz (entiéndase un caso como la implementación del provider indicado lineas arriba), lo que se debe hacer es mediante el patrón adapter segregar las interfaces que necesitemos, y sólo este adapter conocerá todos los métodos de la interfaz que queremos disminuir.

La demo que desarrollare para demostrar lo mencionado en este post, consta de una clase que llamaremos IMachine, que es el simil de una impresora multifuncional.

Interfaz IMachine

Si queremos implementar esta interfaz en un método que sólo imprima, nos veremos en la siguiente situación.

Implementación de IMachine, sólo para imprimir

Con métodos que no necesitamos, por esto se crea una interfaz para cada una de las acciones “IFax”, “IPrint” y “IScann”. Del mismo modo, si se requiriera que nuestro equipo escanee y también imprima, podríamos crear una interfaz que herede a su ves de estas dos interfaces, del siguiente modo:

Herencia entre interfaces

Como ven, no se esta generando ningún método dentro, dado que el IScan y el IPrint, ya tiene sus implementaciones propias. Así mismo, cuando hacer la implementación de nuestra interfaz IMultiFunction, podemos valernos del patrón decorator para no implementar nuevamente los métodos de imprimir y de scan, sino usar los que ya existen en estas interfaces.

Aplicación del patrón decorator

Espero que con este post se tenga un poco más claro el concepto que nos brinda este principio. El último principio que compone SOLID es el ded la inyección de dependencia, y este lo estaremos revisando probablemente en el siguiente post. Como siempre, dejo el video del desarrollo del post y el link al repositorio en github.

REPO: GITHUB

Listado de POST sobre SOLID anteriores:

Deja un comentario

Upload File Input Component con Angular Material

Desde hace algún tiempo que vengo trabajando con Angular, recientemente en un freelo me vi en la necesidad de hacer la carga de un archivo a un controllador en net core. Actualmente en este proyecto se esta usando Angular Material y me di con la sorpresa que en esta librería no se tienen ninguna alternativa a un input type file.

Luego de buscar un poco y no encontrar una alternativa que me llamara la atención, decidí ver la forma de crear una. Les comparto esta demo por si a alguien le puede resultar útil.

Primero, necesitamos crear nuestro nuevo proyecto en angular, instalar la librería de angular material, crear el componente para cargar el archivo y finalmente, crear un modulo adicional para que concentre todos los módulos correspondientes de angular material, para esto ejecutamos en la consola del vscode los típicos comandos:

ng new UploadFileAngularMaterial
cd UploadFileAngularMaterial
npm install --save @angular/material @angular/cdk @angular/animations
ng g m /modules/material
ng g c file-upload

Del lado de la configuración del angular material, no me explayaré mucho dado que hay muchos tutoriales en la red que pueden dar más ejemplo y también puede verlo en el repositorio al final del post.

Para usar los material icons, estoy en el index de la aplicación poniendo el link a la url:

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

Veamos ahora si lo importante de esta demo:

File-Upload

El html del componente tendrá la siguiente estructura:

HTML del componente File-Upload

Como se puede observar, tenemos un botón para hacerla carga (una imagen), y debajo de este una lista de los archivos que queramos cargar, en la cual se muestra el nombre del archivo, una barra de progreso, y dos iconos que actúan a modo de botones para reintentar en caso la carga no se haya podido efectuar y para cancelar la carga. Finalmente, nuestro input type file.

Sólo explicaré los métodos más importantes del componente dado que se tienen creado varios:

Variables del Componente

Para nuestro componente, tenemos algunas variables que pueden ser input del componente, y un output del tipo string. Además, la información que se selecciones los ‘files’ so del tipo ‘FileUploadModel’, paso a detallar lo que contiene este model.

Model FileUploadModel

Este modelo posee cierta información que usaremos para manejar la carga del archivo, los estamos que tiene, si se puede re-intentar o cancelar así como las subscripciones que se realizan cuando se hacen las peticiones http.

onClick al botón

Cada ves que se realice un click al botón, se está obteniendo el input type file y se busca el evento ‘onchange’, mediante el cual se obtiene los items seleccionados (archivos de texto para el demo). Dado que el evento ‘onchange’, se va activar cuando este cambie, se llama al evento ‘click’ para que abra el buscar de archivos desde el navegador, de manera que cuando estos sean seleccionados se carguen los archivos, y recién se llama al método ‘uploadFiles’.

uploadFiles

Se empieza a iterar sobre los archivos seleccionados que se llenaron en el evento ‘onchange’ y cada uno de estos estos serán enviados al método ‘uploadFile’.

uploadFile

Este es el método que se encarga de hacer la carga del archivo, para la demo estoy usando un api pública “http://file.io” que te permite realizar la carga de archivos. También se podría llamar a algún service de ser necesario o el modo en le cual deseen consumir servicios desde su aplicativo. Acá, estamos inicializando un HttpRequest del tipo post, y asignado el request a nuestro ‘sub’ que es parte de nuestra entidad. Adicionalmente, vamos a mapear un evento para poder determinar el avance del progress conforme se va cargando el archivo, tenemos una suscripción para manejar lo que nos retorne la suscripción en caso de que la respuesta HTPP sea del tipo 200, si fuera alguna respuesta HTTP de error, entrará a nuestro ‘catchError’. De ser correcto, limpiamos el archivo y le enviamos la respuesta al output del tipo eventEmitter, al componente que este llamandonos. Veamos una demo de como se está dando todo este proceso:

Presionamos el botón
Luego de seleccionarlo, se carga al array de archivos y se envia al uploadfiles
Esta por llevarse a cabo la solicitud al endpoint
Mientras el archivo carga, se ve la barra de carga, nombre del archivo y un botón de cancelar
Cuando cargo correctamente, se elimina el archivo del listado y se envía el output
Se envia a un console.log el resultado del envio del archivo

Si quisieramos revisar si la carga fue correcta, podemos abrir el link que se adjunta en el response, teniendo el siguiente resultado.

Respuesta del endpoint

Aún cuando el dedsarrollo del componente me tomo mas tiempo del que hubiera deseado, es una buena idea tenerlo como una herramienta más en el repertorio. Dejo un video de la creación del proyecto y las pruebas que se han realizado, además del link al repositorio.

REPO: GITHUB

13 comentarios

Liskov Substitution Principle

El tercer principio que compone SOLID es el Liskov Substitution Principle, y este nos plantea

Plantea una guía de cuando debemos usar herencia en POO.

Let Φ (x) be a property provable about objects x of type T. Then Φ (y) should be true for objects y of type S where S is a subtype of T.

Barbara Liskov, 1988

Este principio, podemos entenderlo en su modo más simple de la siguiente forma “Las clases derivadas, deben poder ser sustituidas por sus clases base”.

Para ejemplificar este principio, usaremos el típico ejemplo del rectángulo y el cuadrado. Veamos el proyecto que tenemos definido:

Nuestra clase base es el rectángulo:

Clase base rectángulo

Queremos calcular el área de nuestro rectángulo o las clases que lo implementen, como un cuadrado. Para esto, creamos el siguiente método:

Método para hallar el área

Digamos que ahora necesitamos implementar un cuadrado, y con esta finalidad creamos la siguiente clase:

Clase del cuadrado

Como ven, el cuadrado implementa nuestro rectángulo y lo que hacemos es definir en sus propiedades que las propiedades base (las que esta implementado) son iguales en su ancho y altura, que como sabemos corresponden a los lados de un cuadrado. Si probamos obtener el cuadrado de ambas clases, tendremos el siguiente resultado:

Resultado al obtener el área sin probar el principio Liskov

Notese que se esta creando una nueva instancia del Square y por esto obtenemos los resultados correctos. Sin embargo, si buscamos poner en práctica lo definido por el principio liskov, veamos que resultado obtenemos.

Resultado poniendo a prueba el principio Liskov

Lo que sucede es que nuestra clase Rectangle, es la que es inicializada, por lo tanto sólo posee el Width igual a 4, y no el Height. Cuando se busca obtener el área realiza la multiplicación del ancho por la altura y tenemos como resultado 0. Para poder respetar este principio, es necesario que realicemos el siguiente cambio:

Cambios para respetar el principio Liskov

Lo que hacemos es declarar como virtuales las propiedades de nuestra clase base, por lo que cuando se incialice y el compilador determine que es virtual, va a buscar algún override que es justo lo que hacemos en la clase derivada. De este modo, sabe que debe usar la propiedad de Square e iguala los lados a 4 que es lo que definimos en el Width. Si probamos ahora nuestro método, tendremos el siguiente resultado:

Prueba final del cálculo del área

Donde como se ve, ya se respeta el principio que hemos tocado ampliamente en este post. Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el tercer post de los principios que componen SOLID. Como siempre, dejo el video del desarrollo de esta post y el link al repositorio en github.

REPO: GITHUB

Listado de POST sobre SOLID anteriores:

Deja un comentario

Open Close Principle

Siguiendo con la linea de post que presentan los principios que componen marco de buenas prácticas SOLID. El que vamos a desarrollar en este post es el “Open Close Principle”, esto hace referencia a que el código que desarrollamos debería estar abierto a ser extendido y cerrado a las modificaciones.

Sin más, veamos el ejemplo que vamos a desarrollar en este post:

Supongamos que estamos desarrollando un sistema para un almacen, en el cual se manejan productos. Estos productos, poseen características, las cuales pueden ser:

Clase de Productos

En donde los “Enums” mostrados poseen los siguientes elementos:

Clase de Enums

Digamos que por necesidad del negocio, nos vemos obligados a filtrar los productos que vamos almacenando por su color, teniendo esto en mente, creamos una nueva clase que llamaremos “ProductFilter”.

De este modo, si queremos filtrar un grupo de colores, por un color en particular deberíamos hacer algo muy similar a esto:

Filtro por color los productos

De este modo, tenemos 3 productos, los cuales se agregan a una lista y esta lista se proporciona al método de filtrado que hemos creado. Si ejecutamos esta aplicación, tenemos el siguiente resultado:

Filtrado por color a los productos
Filtrado Correcto

Como podemos ver, se ejecuta sin problemas. El problema viene cuando requerimos realizar más filtros. Digamos que ahora nos indican que el filtrado también sea por tamaño, o por color y tamaño, terminariamos llenando nuestra clase “ProductFilter” de métodos. Algo muy similar a esto:

Llenado de métodos para diversos tipos de filtros

Como ven, esto rompe claramente el principio de Open Close Principle. Deberíamos de poder extender nuestros filtros, sin afectar la clase que ya se tiene, esto se puede conseguir a través de interfaces. En este caso, para respetar este principio voy a usar el “specification pattern”. Veamos como podemos implementarlo y respetar este principio.

Es necesario creas dos interfaces para poder llevar a cabo la implementación de este patrón de diseño, siendo la primera una que nos permitirá definir la especificación, cuando esta va a estar satisfecha y el tipo de elemento que recibe como tipo de entrada y la segunda espara implementar el filtro y como este se lleva a cabo.

Interfaz IFilter
Interfaz ISpecification

Ahora, implementemos el primero de los filtros, este es es el “ColorSpecification”:

ColorSpecification

Como pueden ver, implementamos la interfaz definida en el paso anterior, tenemos una variable que se inicializa en el constructor, que para el filtro es el color y en el método “IsSatisfied” solo se retorna true si el color del producto que nos proporcionan es igual al que se inicializa en el constructor.

Para poder usar este filtro es necesario implementar también la interfaz “IFilter”, lo cual realizamos del siguiente modo:

Implementación del IFilter

Del mismo modo que en la implementación anterior, se se le indica el tipo de elemento con el cual se desea filtrar, iteramos sobre la lista de estos elementos y solo solo cuando la especificación haya sido satifecha retornamos, el “yield” lo usamos por la eficiencia en el recorrido de nuestra lista.

Veamos como usamos este filtro en nuestra clase principal y que resultado obtenemos:

Uso del filtro
Resultado del nuevo filtrado

Finalmente, mostrare como se puede realizar el más de un filtro a la vez, para ejemplificar esto, creare un filtro por tamaño, siguiendo el mismo esquema ejemplificado anteriormente. Para poder adicionar más de un filtro es necesario crear una nueva clase pero ahora su tipo también será genérico, del siguiente modo:

AndSpecification para realizar más de un filtro

Inicializamos dos variables en el constructor, ambas del tipo “ISpecification”, y sólo se satisface cuando ambas variables lo están. Para consumir este método, debemos de hacerlo del siguiente modo:

Consumiendo más de un filtro a la vez

Se le tienen que indicar los filtros que se quieren realizar y al consumir este filtro en el cual buscamos un producto del color verde y de tamaño pequeño, nuestro resultado es el siguiente:

Resultado de ambos filtros

Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el segundo post de los principios que componen SOLID. Como siempre, dejo el video del desarrollo de esta post y el link al repositorio en github.

REPO: GITHUB

Listado de POST sobre SOLID anteriores:

Deja un comentario

Crear web con Thymeleaf y Spring boot

Esta semana, me consultaron por las alternativas que se tienen en Spring para implementar el patrón MVC del lado del frontend, de las múltiples formas que se tienen, la que más me llama la atención es thymeleaf dado que es lo más cerca a código html puro. En este post quiero explicar como podemos crear un proyecto con spring, en el que useamos thymeleaft para el desarrollo de las vistas, añadiendo contenido estático (js, css) a nuestra web y consumiendo una base de datos como postgres usando jdbc.

Como indico en el parrafo anterior, en este post usaremos las siguientes herramientas:

  • Spring initializr
  • Spring JDBC
  • Spring Web Started
  • Thymeleaf
  • Lombok
  • PostgreSQL

Nuestro proyecto en cuestión, consiste en la realización de una página web donde podemos visualizar los logs que se generen en una aplicativo, siendo estos de los distintos tipos que existen. El proyecto en cuestión es uno personal que tengo en mente y pienso desarrollarlo en conjunto con una serie de post, se me hace interesante por que me permitirá agrupar el logging de diversar aplicaciones. Soy consciente que actualmente existen herramientas que brindan esta información con dashboards muy completos entre otras ventajas. Pero, no me negarán que existe una mistica especial en crear algo uno mismo desde cero. Además quiero que en esta aplicación también se pueda visualizar las consultas que se efectuan al motor de postgres, siendo este un simil al SQL Profiler que tenemos en SQL Server, he buscado exhaustivamente alguna alternativa que me permita conseguir esto y no la he encontrado, motivo por el cual me plantee generar una por mi mismo, siendo consciente que muy probablemente no tenga todas las características inicialmente que podemos encontrar en la herramientas de microsoft. Volviendo al post que nos reclama, iniciaremos creando un proyecto de la siguiente manera:

Creación del proyecto con Intellij IDEA
Selección de las dependencias

Con el proyecto creado, replicamos la estructura ya antes mencionada para el repository, service y controller. Me dedicaré a explicar como se configuran los archivos estáticos y la demás configuración correspondiente a la integración con thymeleaft.

Debemos copiar los archivos que vamos a usar en la siguiente ruta de nuestro proyecto:

Ubicación de los archivos estáticos

Para nuestros archivos html, creamos una carpeta shared en la que pondremos archivos como el header o el footer de nuestra aplicación, esto será usado como fragment en nuestras demás vistas. Para este primer post, sólo creare el “index” de nuestra aplicación en donde desarrollare una solicitud para llenar un combo con información de base de datos y la realización de un post.

Ubicación de los HTML del proyecto

En el header, importaremos las dependencias css y js. Además de realizar ciertas configuraciones para poder usarlo como fragment en nuestras demás vistas.

Importar estilos con thymeleaft:

<link rel="stylesheet" 
          th:href="@{/lib/plugins/daterangepicker/daterangepicker.css}">

Importar javascript con thymeleaft:

<script th:src="@{/lib/plugins/jquery/jquery.min.js}"></script>

Configurar una sección HTML en el header para ser llamada como fragment:

Configuración como fragment

Como se puede ver, se indica que es un fragment y se le asigna un nombre por el cual será llamada, el html en cuestión es un loader a ser usado en las demás páginas html cuando carguen.

En el controlador, cuando trabajemos con MVC, es necesario usar la notación “@Controller” en lugar de “RestController” que usabamos cuando trabajamos con APIS. el primer controller a crear es el index y posee la siguiente estructura.

Creación del controller que dirije al index

Como se ve, es un controller del tipo string, y debe retornar el nombre del html, para nuestro caso “logger”. Es necesario que nuestro index tenga el tipo de entrada “Model” que es una clase propia del framework que nos permite indicarle a la vista los atributos con los cuales trabajará. Como podemos ver, estoy declarando un atributo con el nombre “search” que es del tipo “SearchLog” que es una entidad que posee los siguientes campos:

Entidad SearchLog

Los campos que estén dentro de nuestro formulario html, deben tener los mismos valores definidos para nuestra entidad “SearchLog”. Además, se añaden otros dos atributos, que contienen las listas de nombres de las máquinas en las cuales se generaron los logs y los nombres de las aplicación de las cuales se tiene log registrados. En la vista “logger”, se tiene la siguiente configuración:

Llamada al fragment:

Llamada al fragment head desde el logger

Como se puede observar, hago un insert del fragment, indicando la ubicación del archivo que lo contiene y el nombre que le indicamos.

Configuración del form:

Configuración del form

En este “form” es importante notas que se está indicando la acción a la cual debe dirigir “/search”, el objecto que contiene que lleva el nombre “search” y si ven el post lineas arriba es el nombre que le indicamos en el model e indicar que es del tipo post. Por otro lado los campos contenidos en el form, poseen los nombres exactos que se definieron en la entidad y finalmente, para la lista que se devuelve, se itera sobre esta y se esta indicando en el valor y el texto a desplegar el mismo valor que es el nombre.

En el controlador tenemos que definir un método post con nombre “search”, quedando de la siguiente manera:

Controlador “search”

Es necesario definir como entrada de nuestro controlador un “@ModelAttribute” que debe ser del mismo tipo que definimos en nuesto model y del que posee nuestro form, para que pueda ser mapeado correctamente. Finalmente, llenamos los datos en nuestra entidad.

Llenado de datos en nuestro formulario

Presionamos el botón “search” y deberíamos llegar a nuestro método post con variable de entrada “SearchLog” y los campos que hemos ingresado.

Llegada de la información al controlador correctamente

Como podemos ver, es relativamente sencillo configurar y empezar a trabajar con thymeleaft, en otros post explicare la integración que podemos realizar con spring secutiry y sessions usando esta herramienta. Espero que les haya servido, como en los otros post dejo el link al repositorio de github del proyecto y el video.

REPO: GITHUB

Saludos.

Deja un comentario

Grabar log de errores en db y archivo con Spring Boot

Llevo ya algunas semanas revisando cursos sobre Spring, como he mencionado en publicaciones anteriores, el trabajo me lleva a desarrollar más en tecnologías de Microsoft. Sin embargo, conforme voy aprendido más de spring, me interesa más por lo pontente que es y el beneficio que brinda al evitar la repetición de código que no le suma nada a la lógica de negocio o ”  boilerplate code “.

El post de hoy, lo centrare en el grabado de logging cuando tenemos una aplicación hecha con spring boot, como saben el log es fundamental dado que un buen log nos permite resolver los problemas de nuestro aplicativo sin mucha demora, en un post posterior explicare lineamientos para generar un buen log y buenas practicas en el mismo. Sin embargo me interesa hacer un post de esto acá porque he tenido que revisar mucho la documentación para poder configurar el log como lo necesitaba en spring y no pude encontrar ningún lugar donde expliquen detenidamente como poder realizar esto.

El log que grabaremos se realizará en base de datos (postgres) y un archivo de texto (.txt), se pueden tener multiples tipos (niveles) de log, que nos permiten medir la usabilidad de los features, los errores y las caidas fatales del sistema, para la demo me enfocare en los errores y explicare como en spring podemos capturar todos los errores que se generen, grabarlos en base de datos y el archivo de texto y al usuario mostrarle un mensaje de texto genérico y un código vinculado a nuestro registro de error.

APLICATIVO

Estoy usando Intellij IDEA como IDE para el desarrollo de esta demo, y la aplicación se divide en repository, service y controller. Considerando que el repository y service tenemos una implementación para las interfaces que definimos. Además, en repository tenemos una capa para hacer el mapping a entidades de lo recibido por la base de datos.

Project files de Spring boot

Para grabar los logs, usar log4j2 que es una librería de amplia trayectoria y bien documentada, para configurarla en la sección de resources de nuestra aplicación es necesario crear un archivo de configuración, este puede ser un xml, json o un properties. En mi caso cree un “log4j2.properties” .

Archivo de configuración del log

En el properties de spring es necesario indicarle que tenemos un archivo de configuración de log con las siguientes sentencias:

Configuración en el application.properties para el log

En el log4j2.properties que hemos creado es necesario indicar la siguiente configuración.

Configuración del log en log4j2.properties

En esta estamos indicando el indicando al configuración de la base de datos, es necesario indicar cada columna de la tabla que hemos destinado para los logs en base de datos con los valores que se van a considerar para cada columna. También está la configuración para el archvo de texto, finalmente se indica que ambos appender deben ser utilizados y el nivel mínimo del log.

También es posible indicar en el log variables configuradas por nosotros, para mi caso es necesario saber el endpoint que se está consumiendo, el nombre del método del endpoint, el nombre del método donde el error ocurrio, un guid para vincular el error a un identificador único entre otros valores. Esto podemos conseguirlo seteando variables como propiedades del sistema y luego leyendo en nuestro log4j2.propierties.

Seteo de variables como propiedades del sistema
Obtenemos la propiedad del sistema en log4j2.properties

Una explicación didáctica de esta configuración así como algunas cosas que no pongo en el post para no hacerlo muy extenso lo encuentran en el video. Quedan muchos puntos por detallar más que desarrolloraré en otros posts, espero que les haya sido de utilidad y como siempre dejo los links al repositorio para que revisen ustedes el código.

REPO: GITHUB

Saludos.

1 comentario