Saltar al contenido

Autor: Walberth

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

Single Responsibility Principle

Ya hace un tiempo llevo trabajando en una empresa en el sector financiero, en la cual como toda empresa de su envergadura el stack de tecnologías y aplicaciones que poseen es diverso. Y como comprenderan eso tarde o temprano nos llevaba tener que trabajar con legacy code, y las serie de retos que estos involucran. Más de una vez, he tenido que atajar métodos de más de 1000 líneas de código que tenían como objetivo extornar algún monto, grabar log, etc. Esto se vuelve un dolor se cabeza al querer realizar pruebas unitarias para que sean parte del pipeline que se ejecuta en un ambiente de integración y despliegue continuo.

Este problema pone en evidencia la importancia de desarrollar teniendo siempre en mente la utilización de patrones de diseño. Esto me lleva a tratar en este post el primero de los principios de SOLID, siendo los principios que lo componen no los únicos que existen, pero si un marco de buenas prácticas en el desarrollo de software que nos permite asegurar un nivel de calidad en lo que desarrollamos.

Single Responsability Principle representa la «S» en SOLID, este principio entre otras cosas nos indica que los métodos que desarrollamos deben poseer una sola reponsabilidad y por lo tanto un sólo motivo para cambiar. Cuando nos referimos a responsabilidad, esta puede tomar la forma de las siguiente maneras:

Tipos de responsabilidad
  • Grabar en una base de datos, archivo de texto o cualquier otro medio de persistencia que se desee.
  • Realizar un log del método que se está consumiendo y/o también de los errores que puedan ocurrir.
  • Validar la información ingresada al método.
  • Lógica de negocio.

En lineas generales, debemos buscar que el código que desarrollemos tenga el menor acoplamiento posible y la única manera de conseguirlo es definiendo adecuadamente las responsabilidades. Una buena manera de probar que tanto acoplamiento posee nuestro código es cuantos test unitarios es necesario desarrollar para poder coberturarlo al 100%, es muy usual que si nuestro método desarrollado tiene muchas responsabilidades, el poder realizarle pruebas unitarios se va a volver algo extremadamente completo y tedioso. Para poder probar este patrón de diseño, vamos a reformular código teniendo en cuenta las consideraciones antes mencionadas.

La aplicación que vamos a refactorizar nos permite obtener el cálculo de ratio para la realización de la contratación de una poliza, tenemos un método de más de 100 lineas de código que posee distintas responsabilidades, tal como podemos ver en la siguiente imagen:

Código a refactorizar
  1. Grabación del log cuando inicia la llamada al método.
  2. Lectura de un archivo de texto y conversión a un objeto.
  3. Validación de la data obtenida.
  4. Lógica de negocio.
  5. Persistencia de la información obtenida en un archivo de texto.
  6. Grabación del log cuando finaliza la llamada al método.

Como podemos ver, es necesario reformular el método para que cumpla con los requerimientos descritos por este patrón. Esto lo realizaremos extrayendo ciertas responsabilidades del método principal. El primero nos permitirá realizar el grabado de logs:

Método para grabar logs

Luego, desarrollaremos un método para leer el archivo de texto, el cual debe poder retornar la entidad que le pasemos, por lo tanto es debe ser genérico.

Método para leer de un archivo de texto y retornar una entidad

Además, es necesario extraer el grabado de la persistencia en un método, este también debe aceptar como variables de entradas genericos.

Grabado de la persistencia

Finalmente, quedando el método del siguiente modo:

Método refactorizado

Notese que también he separado en un método distinto las validaciones de las polizas de vida.

Espero que les ayude a entender un poco más este principio, así mismo comentarles que este es el primer 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

2 comentarios

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

Llamar un Mock Object dos veces con distintos resultados

Recientemente en el trabajo nos enfrentamos a un escenario cuando estabamos creando los test unitarios de un proyecto. Usando Moq como librería para el mocking queriamos que al llamar a un mismo método en el repositorio obtengamos distintos resultados por cada llamada. Ya en un post anterior he tratado un tema similiar a este con test unitarios, pero en los escenarios más simples es sólo necesario realizar un llamado obtener el resultado desarrollar cierta lógica de negocio con este resultado y evaluar los aserciones en el test.

Me parecio interesante desarrollar un poco más este tema y por eso hago este post, en el les explicaré un poco sobre las distintas opciones que tenemos para poder obtener distintos resultados al llamar a mock object con Moq, siendo una de estas opciones «SetupSequence» que es un overload al método setup que existe en esta librería, lo que podría generar un problema es que este overload es relativamente reciente, dado que recién se a incorporado como parte de la librería el 20 de junio del 2019. Y la otra opción es el uso de una estructura de datos como las queue o colas.

Para el desarrollo del post, me valdré del mismo proyecto que trabajamos en el post pasado, simplemente he añadido nuevos método en la capa de aplicación y en el repositorio y nuevos test unitarios.

APLICACION

Nuevo método en la capa de aplicación

Este método, como ven simplemente llama dos veces al mismo método del repositorio, con distintos parametros de entrada y retorna la concatenación de ambos resultados.

REPOSITORIO

Nuevo método en la capa de repositorio

Como ven, en el repositorio también se ha creado un nuevo método acá se está intentando simular que se llega a una base de datos, pero por fines prácticos se está devolviendo el nombre completo para todas las llamadas. Veamos los nuevos test unitarios desarrollados.

TEST

Uso de SetupSequence

Como pueden ver, con el uso de «SetupSequence» es posible realizar más de «Returns» los cuales, se van devolviendo conforme se van llamando, de manera tal que en el primer llamado desde la capa de aplicación, se devuelve «walberth», y en el segundo «angela» y finalmente, se retorna la concatenación.

Uso de queue (Colas)

La otra forma de conseguir el mismo resultado, es valiendonos del uso de colas, como saben esta estructura de datos es del tipo FIFO (first in, first out), de modo que cada ves que llamemos a nuetro moq, y su objeto de retorno es del tipo Queue va a devolver los objetos de este modo.

Espero que les sirva de ayuda como hemos podido resolver este escenario, tal ves con un caso tan común, pero cuando ocurre puede que nos tome un par de horas ver la forma de darle solución. Como siempre, dejo el video del desarrollo de esta solución y los links a github.

REPO: GITHUB

Saludos.

1 comentario

Unit Test y Mocking en NetCore

Cuando empezamos a desarrollar, sea el lenguaje que nos adopte incialmente no solemos tener en consideración los test unitarios y mucho menos el mocking. Si nos dedicamos a este mundo del desarrollo, tarde o temprano vamos a tener que llegar a saber para que sirven y como implementarlos, y de esto de lo que busco escribir un poco hoy.

TEST UNITARIOS

Los test unitarios son secciones de código de escribimos, cuyo objetivo es probar el funcionamiento y funcionalidad de otros métodos que hayamos desarrollado. Es importante tener claro el concepto de atomicidad al querer realizar un test unitarios, dado que el método a probar que se ha desarrollado debemos intentar que posea una única responsabilidad, de manera tal que nuestros test unitarios puedan ser atómico.

MOCKING

Cuando nos referimos a mocking, debemos tener claro que se tiene que llevar a cabo con una prueba unitaria, y lo que nos permite es abstraer el método que queremos probar de otras dependencías que no se están probando. De tal manera, podemos aislar nuestro test unitario y conseguir que sean realmente atómicos.

En el ejemplo que llevaremos a cabo, explicare como implementar un test unitario en Net Core 2.2, usando una librería para poder aplicar mocking en nuestro test. Para conseguir esto, usaremos XUnit para el test unitaio y Moq para el mocking. Es importante recalcar que existen otras opciones y que los conceptos que se aplicarán en la demo, puedes tranquilamente extrapolarse a alguna otra librería que se quiera usar.

Para empezara crear nuestro proyecto, se tiene la siguiente estructura en un proyecto demo:

Distribución del proyecto demo
Nuget utilizados en el test unitario

La arquitectura no es lo importante en esta demo, pero basicamente tenemos una capa de presentación (aplicación consola), una capa de aplicación y una de repositorio, en esta última se debe tener acceso a la base de datos. Las pruebas se dividirán de la siguiente manera:

  1. Para explicar los test unitarios se tiene un método en la capa de aplicación un método que recibe el nombre y apellido de una persona y lo regresa concatenado, teniendo varientes si se ha ingreso el nombre o el apellido en vacio.
  2. Probaremos el mocking simulando que estamos accediendo al repositorio y desde este a una base de datos en donde obtenemos un listado de nombres de personas.
Capa de aplicación
Capa de repositorio

En el proyecto de test unitarios, se crearon los siguientes test unitarios:

  • El primero prueba el escenario ideal del método que desarrollamos, notemos que en la primera parte de nuestro test, estamos preparando la información, liego consumimos el método desarrollado y finalmente evaluamos el resultado con los assert que nos brinda xunit.
  • En el segundo test unitario, probamos el escenario en el que a nuestro método no se le proporcione la información necesaria. Se sigue el mismo formato con la organización de los pasos para las pruebas (preparar la información, llamar al método y finalmente evaluar los resultados).
  • Para realizar la prueba del mocking, es importante realizar la inyección de dependencia del repositorio en nuestra aplicación, dado que lo que buscamos con el mocking es poder simular el acceso en este repositorio, Luego, en el test creamos el mock con la interfaz de nuestro repositorio, le indicamos en «Setup» con una expresión lambda el método que vamos a probar, y en este punto podríamos proporcionale la información que sea necesaria para consumir este método de ser esto necesario y finalmente le indicamos lo que va a retornar, para conseguir esto hemos debido crear un objeto del mismo tipo que retorna nuestro método y indicarlo dentro de «Returns». Finalmente, le pasamos nuestro mock al construtor de nuestra aplicación y llamamos al método a probar.
Inyección de dependencia
Mocking usando moq en NetCore

Finalmente, corremos nuestros test unitarios y tenemos el siguiente resultado:

Como podemos ver, lo importante del test unitario es poder realizar pruebas atómicas del código que escribimos y con esta finalidad quitarle la responsabilidad como obtener objetos que escapan a lo que buscamos probar (como conexiones a base de datos o otros métodos). Y con esta finalidad nos apoyamos del mocking, existen muchas cosas más que se pueden llevar a cabo con Xunit, e incluso más con Moq, ambas librerías usadas para esta demo, Más adelante realizaré más post en los cuales detallo un poco las características de ambas, espero que encuentren útil el post, les dejo el video y el link de github del proyecto realizado.

REPO: GITHUB

Saludos.

Deja un comentario

Caching en Web API Net Core 2.2

Para nadie es sorpresa que la tendencia en el desarrollo con tencologias Backend de Microsoft se orienta cada vez más a NetCore, estando como release candidate (a la fecha de hoy) la versión 3.0.

Hace un tiempo que vengo trabajando con NetCore, inicialmente en proyectos personales y luego en el trabajo. El tema que hoy nos reclama es como aplicar caching y concurrency en las web api cuando se trabaja con Net Core. Para ello, primero recordemos un poco que implica caching.

Cuando nos referimos a caching es válido entender que brindamos un determinado tiempo de vida a una solicitud http, lo cual nos permite no volver a consultar el mismo recurso por el tiempo que se ha determinado, esto nos permite optimizar los tiempos de respuesta y el consumo de recursos en el servidor.

Existen tipos de caching, para entender mejor esta idea, revisemos la siguiente imagen:

caching types

  • Client Cache: Usualmente podemos encontrarlos en el navegador, almacenan las respuestas de las peticiones http individualmente si estas así lo indican, es privado porque los recursos que se están almacenando no se comparten con nadie más.
  • Gateway Cache: Es un cache compartido, los recursos son usados por multiples aplicaciones.
  • Proxy Cache: Tambien es un cache compartido, sin embargo su embergadura es mayor, es usualmente usado por corporaciones grandes o provedores de internet.

Del mismo modo, cuando se trabaja con caching, tenemos que tener un poco más claro que significa el «Modelo de expiración», esta es una forma de indicarle a los clientes, cuanto tiempo va a durar una solicitud, lo cual permite indicarle a las aplicaciones clientes por cuanto tiempo se va a entender que una respuesta http se considera activa. Para conseguir este objetivo se usan dos header comunmente:

  • Expires Hader: Es el más simple, contiene una marca de tiempo de la expiración, pero se asume que las horas entre el cliente y el servidor están sincronizadas.
  • Cache Control Header: Permite indicar el tiempo como «max-age» lo cual indica estará activo por 60 segundos, y public indica quienes pueden realizar el caching de esta respuesta http (client, gateway o proxy).

Es necesario hablar también sobre los modelos de validación o «Validation Model» , estos nos permiten validar si la solicitud http ha sido modificada y de este modo mantenemos la integridad de nuestra solicitud.

  • Strong Validators: Usan los «ETag» para validar si la respuesta ha sido modificada.
  • Weak Validators: No siempre cambian cuando la respuesta lo hace, sólo cambian en cambios considerables, se basan en una etiqueta de «Last-Modified», recordemos que los horarios deben estar sincronizados. También poseen weak Etag.

Finalmente, para culminar con la teoría, es necesario hablar sobre «Cache-control directives», las cuales son las directivas que se pueden enviar en el request y el response http:

  • Response: Tenemos elementos de control que nos indican el tiempo de duración máximo (en segundos) de la solicitud, si es posible almacenarla en el navegador (private) o en el servidor (public) entre otras.
  • Request: Del mismo modo, tenemos elementos que nos valida el tiempo de duración de la respuesta mínimo y máximo entre otras.

Para la demo que vamos a realizar, usaremos un nugget llamado «Marvin.Cache.Headers», luego de instalarlo nos dirigimos al archivo de configuración «Startup» de nuestro web api.

Es necesario agregar en el «ConfigurationService» y en el «Configure» que se debe añadir el uso de cacheheader al pipeline de ejecución, adicionalmente estoy sobreescribiendo el timepo de expiración de la solicitud e indicandole que se debe revalidar.

Sólo con está configuración, ya tenemos habilitado el uso de caching, en su modo más básico. Probemos en postman el uso de lo implementado, primero debemos habilitar el envío de «cache-control» en el header, esto se hace de la siguiente manera:

La petición que llevaremos a cabo, es la siguiente:

Como pueden ver es un GET a un endpoint que en el query esta buscando los libros de un determinado autor. Pongamos un punto de interrupción para validar que la petición a llegado, y veamos la respuesta que recibimos.

Como podemos ver, el tiempo de vida de la petición es 30 segundos, de modo que si se volviera a hacer otra petición, esta no llegaria a nuestro endpoint, en cambio nos indicaria el timepo de vida que aún tiene la solicitud que hemos realizado.

Les dejo link al repositorio en github en el cual pueden encontrar el proyecto completo y el video de youtube donde pueden visualizar las pruebas.

REPO: GITHUB

Saludos.

Deja un comentario