Jekyll2022-02-19T09:21:28+00:00https://pandemoniodigital.es/feed.xmlPandemonio DigitalBlog sobre ingeniería de software, programación, bases de datos y otras novedades tecnológicas.Arquitectura Orientada a Eventos2022-02-19T09:00:00+00:002022-02-19T09:00:00+00:00https://pandemoniodigital.es/arquitectura/2022/02/19/aoe<p><img src="https://pandemoniodigital.es/assets/images/aoe/cover.png" alt="" class="page.image" /></p>
<h1 id="introducción">Introducción</h1>
<p>En esta entrada vamos a hablar sobre arquitectura orientada a eventos y async api.</p>
<p>Recalcar que este artículo se trata de una aproximación mía personal basada en diversos artículos, libros y conferencias que he estado consultando estos meses.</p>
<p>Partamos desde el principio ¿Qué es la arquitectura orientada a eventos?. Si partimos de nuestra querida amiga <strong>wikipedia</strong> tenemos que:</p>
<blockquote>
<p>Es es un patrón de <a href="https://es.wikipedia.org/wiki/Arquitectura_software">arquitectura software</a> que promueve la producción, detección, consumo de, y reacción a <a href="https://es.wikipedia.org/wiki/Evento_estad%C3%ADstico">eventos</a></p>
</blockquote>
<p>De esta definición nos quedamos con varios conceptos:</p>
<h2 id="evento">Evento</h2>
<p>Vamos a entender como evento cualquier cambio significativo e interesante que se ha producido en el sistema. El evento representa algo que ya ha sucedido en el mundo real, no incluye cosas que van a pasar en un futuro. Los eventos viajan en una sola dirección, es decir no esperan una respuesta lo que se denomina “<em>fire and forget</em>”.</p>
<p>Los eventos son inmutables, no cambian ni se alteran. Un estado puede corresponder a la creación, cambio del estado de una entidad, etc.</p>
<p>Un evento generalmente contiene toda la información necesaria para describir el evento, y suele estar en un formato JSON, aunque podría estar en otros formatos.</p>
<p>Un evento puede llevar información adicional que sea posteriormente proporcionada a aquel que la consuma, a esta información adicional se la denomina <em>payload</em>.</p>
<h2 id="productores-o-publicadores">Productores o publicadores</h2>
<p>Vamos a distinguir en esta arquitectura los productores, cuando se produce un evento en una aplicación productora publicará un evento o comando, este será publicado en un <em>broker</em> de mensajes. Una vez publicado el evento, el productor se desentiende de qué ocurra con el mensaje.</p>
<h2 id="consumidores-o-suscriptores">Consumidores o suscriptores</h2>
<p>En el <em>broker</em> en el que son publicados los eventos pueden estar suscritas diversas aplicaciones, estas aplicaciones estarán a la escucha de cuando se produzca un evento, recibir el evento con su <em>payload</em> y con este mensaje recibido realizar la lógica para la que se hayan programada.</p>
<h2 id="comandos">Comandos</h2>
<p>Aunque no aparecen en la definición también quiero hablar de los comandos. Los comandos son acciones, peticiones para operaciones futuras que deben realizar cambios en el sistema. Estos comandos pueden pasar por diversos estados así como producir error. Suelen ser acciones que se desea que ocurra en el sistema.</p>
<h2 id="broker-de-mensajería">Broker de mensajería</h2>
<p>Se trata de una tecnología dedicada a procesar e intercambiar mensajes de datos entre aplicaciones. Son un mediador entre sistemas que no tienen por qué conocerse mutuamente. Esta característica permite reducir el acoplamiento y escalar de una forma simple y eficiente.</p>
<p>Para ello hace uso un patrón de mensajería en el cuál tenemos por un lado como hemos detallado mas arriba publicadores y suscriptores.</p>
<p>Las principales funciones de un <em>broker</em> de mensajería son:</p>
<ol>
<li>Enrutar los mensajes.</li>
<li>Desacoplar productores de consumidores.</li>
<li>organizar y realizar comprobaciones de los mensajes.</li>
<li>Almacenar los mensajes.</li>
</ol>
<h1 id="idea-principal">Idea principal</h1>
<p>La idea de esta arquitectura está muy orientada a comunicaciones asíncronas entre distintas aplicaciones. Hasta ahora en una arquitectura orientada a microservicios cuando una aplicación quería comunicarse con otro realizaba una petición mediante un <em>webclient</em> de forma síncrona. También mediante la programación reactiva disponemos de la posibilidad de realizar peticiones mediante un cliente web de forma asíncrona. La arquitectura orientada a eventos nos posibilita establecer comunicaciones entre servicios mediante el patrón de publicadores & suscriptores. Un productor produce un evento o comando, otra aplicación o varias aplicaciones están a la escucha suscritos a este <em>broker</em> y reaccionan como si hubiesen recibido una llamando, realizando a su vez nuevos eventos y desencadenando la lógica deseada.</p>
<p>En esta arquitectura se observa también que además de permitirnos una comunicación entre microservicios, nos permite por otro lado disponer de un log, de conocer qué eventos se han producido, cuando, quien los ha producido, quien los ha recibido, etc. Lo que viene siendo una completa auditoría.</p>
<p>Y finalmente por lo que el hecho de que un evento desencadene una lógica, nos permite mediante una publicación volver a reproducir una lógica que conlleve varias aplicaciones que reaccionen ante dicho evento.</p>
<h2 id="cuando-utilizar-arquitectura-orientada-a-eventos">Cuando utilizar Arquitectura Orientada a Eventos</h2>
<ol>
<li>Cuando se desconoce quienes van a ser los consumidores de un evento.</li>
<li>Cuando se requiere que un evento sea consumido por muchos consumidores.</li>
<li>Cuando unos eventos deben desencadenar otros eventos más complejos</li>
<li>Cuando se desea utilizar un patrón CQRS.</li>
</ol>
<h2 id="ventajas">Ventajas</h2>
<ol>
<li><em>Escalabilidad</em>: es posible disponer de varios consumidores de forma que se puedan escalar el consumo de eventos.</li>
<li><em>Desacoplamiento</em> entre productores y consumidores</li>
<li><em>Tolerancia a fallos</em>, si se produce una caída de los consumidores, estos pueden recuperar los eventos que tras volver a estar en activo.</li>
</ol>
<h2 id="desventajas">Desventajas</h2>
<ol>
<li>Limitado a procesos asíncronos, aunque la arquitectura orientada a eventos está muy enfocado a desacoplar sistemas, las aplicaciones se limitan casi en su totalidad a comunicaciones asíncronas. Aunque es posible realizar comunicaciones síncronas mediante comando, esta arquitectura no está muy enfocado en peticiones y respuestas, ya que son mas complejas de definir y menos eficientes en tiempo de respuesta.</li>
<li>Introducen mayor complejidad: las comunicaciones convencionales de cliente servidor o petición respuesta involucraban dos partes. Ahora tenemos tres partes: productores, broker y consumidores lo cual introduce mayor complejidad a la hora de interaccionar entre todas las partes involucradas.</li>
<li>Enmascaramiento de fallos: cuando los sistemas están fuertemente acoplados un error se propaga de un sistema a otro, llamando la atención y haciéndose notar. En este caso al haber un acoplamiento menor puede ser mas complicado detectar fallos en nuestro sistema. Esto se puede solventar monitorizando y haciendo logs de cada componente orientado a objeto, pero como los anteriores punto, añaden complejidad.</li>
</ol>
<h2 id="comunicaciones-síncronas">Comunicaciones síncronas</h2>
<p>Como hemos indicado más arriba, esta arquitectura está muy orientada a comunicaciones asíncronas entre aplicaciones mediante eventos/comandos. No obstante, aunque los eventos no esperan respuesta es posible establecer comunicación síncrona mediante comandos síncronos.</p>
<p>Esto se realizaría tal y como mostramos en la siguiente imagen:</p>
<p><img src="https://pandemoniodigital.es/assets/images/aoe/diagram-1.png" alt="Captura" /></p>
<p>La comunicación entre ambas aplicaciones se realiza mediante dos <em>topics</em> o colas de mensajería en una el productor hace una petición que es consumida por el Servicio B y permanece a la espera de una respuesta una vez haya sido recibido el comando y realizado la lógica pertinente. Una vez el Servicio B ha finalizado publica en otro tópico una respuesta.</p>
<p>Como podemos observar es bastante mas complejo que una petición respuesta convencional, y salta a la vista que en tiempo será más elevado al tener que encolarse con otros eventos, y generarse el comando con el correspondiente payload.</p>
<h1 id="async-api">Async API</h1>
<p>Uno de los problemas que tiene esta arquitectura, al igual que otras es que a medida que crece el número de eventos, productores, consumidores se hace difícil documentar y llevar un seguimiento de qué eventos son producidos y generados. Es por ello que surgen iniciativas como <a href="https://www.asyncapi.com/">Async AP</a>I.</p>
<p>Al igual que ocurrió en su momento con los servicios REST y el <a href="https://swagger.io">swagger</a>. Async Api nos propone definir en un formato similar todos los eventos, <em>topics</em>, <em>payloads</em>, consumidores, productores que van a ser participes de nuestra arquitectura. Nos posibilita incluir descripción, ejemplo de los mensajes generados, versionado, seguridad, etc.</p>
<p><img src="https://pandemoniodigital.es/assets/images/aoe/asyncapi.png" alt="Captura" /></p>
<h3 id="async-topic-definition">Async Topic Definition</h3>
<p><a href="https://github.com/fmvilas/topic-definition">GitHub - fmvilas/topic-definition: AsyncAPI topic structure definition</a></p>
<p>Una propuestas muy interesante que podemos ver en más detalle en el anterior enlace son unas sencillas reglas a la hora de definir los nombres de los topics en los que publicaremos los mensajes. Estas reglas nos harán mas sencilla tanto la documentación como el mantenimiento de nuestra arquitectura de eventos.</p>
<p>La idea a la hora de poner un nombre de un topic es que siga la siguiente estructura:</p>
<p><img src="https://pandemoniodigital.es/assets/images/aoe/async-api-2.png" alt="Captura" /></p>
<p>La idea es que a la hora de definir el <em>topic</em> tengamos la siguiente estructura:</p>
<ul>
<li>dominio</li>
<li>subdominio</li>
<li>versión del <em>topic</em>: inicialmente uno</li>
<li>entidad a la que pertenece el evento o comando
<ul>
<li>Si se trata de un evento el nombre del evento que se aplica sobre la entidad que suele ser un verbo en pasado, o sería lo deseable</li>
<li>Si se trata de un comando el nombre del comando, que puede ser un verbo en infinito o futuro. Opcionalmente se puede incluir en que estado se encuentra el comando.</li>
</ul>
</li>
</ul>Diego Rubio AbujasArch Unit: testear la arquitectura2021-09-27T21:00:00+00:002021-09-27T21:00:00+00:00https://pandemoniodigital.es/arquitectura/2021/09/27/arch-unit<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/arch-unit.png" alt="" /></p>
<p>En este post vamos a hablar sobre un tipo de pruebas del cual llevo oyendo hablar desde hace un año, las pruebas unitarias de arquitectura.</p>
<h1 id="introducción">Introducción</h1>
<p>Una organización quiere aplicar determinadas reglas o estilos de arquitectura a todos los proyectos desarrollados en java. Estas reglas podrían incluir desde la forma en la que se estructura el proyecto, a las librerías que se utilizan el naming que se aplica, anotaciones que se deben o no se deben utilizar, etc.</p>
<p>Supongamos que hay muchos equipos distintos, con mayor o menor experiencia, y queremos que todos apliquen las mismas reglas de arquitectura en sus proyectos. Esto podría suponer un seguimiento regular de sus desarrollos, con el problema de revisar grandes cantidades de código.</p>
<p>Corremos el riesgo de que la arquitectura no se aplique correctamente o como deseamos en la totalidad de los desarrollos.</p>
<p>Bien, en este artículo vamos a hablar de <strong>Arch Unit</strong>. Se trata de una librería para comprobar arquitecturas en código Java mediante un framework de test unitarios.</p>
<h1 id="archunit">ArchUnit</h1>
<blockquote>
<p>ArchUnit is a free, simple and extensible library for checking the architecture of your Java code using any plain Java unit test framework. That is, ArchUnit can check dependencies between packages and classes, layers and slices, check for cyclic dependencies and more. It does so by analyzing given Java bytecode, importing all classes into a Java code structure. You can find examples for the current release at ArchUnit Examples and the sources on GitHub.</p>
</blockquote>
<p><a href="https://www.archunit.org/">https://www.archunit.org/</a></p>
<p>La idea es definir una batería de pruebas y reglas sobre cómo debe ir implementada nuestra arquitectura que deberán pasar todos los proyectos. De esta forma “<em>forzamos</em>” a todos los proyectos a cumplir dichas reglas de arquitectura y no pasar por encima o depender de una revisión de código para que dichas reglas de arquitectura se cumplan.</p>
<p>El autor de esta librería es <strong>Peter Gafert</strong> y otros contribuidores. Aquí podemos ver una conferencia muy interesante del autor sobre ArchTest: <a href="https://www.youtube.com/watch?v=dDQk-YI8N-c">https://www.youtube.com/watch?v=dDQk-YI8N-c</a></p>
<p>En la web del proyecto se ilustran varias aplicaciones posibles de esta librería:</p>
<h2 id="package-dependency-checks"><strong>Package Dependency Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-1.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">noClasses</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"..source.."</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">dependOnClassesThat</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"..foo.."</span><span class="o">)</span>
</code></pre></div></div>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-2.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"..foo.."</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyHaveDependentClassesThat</span><span class="o">().</span><span class="na">resideInAnyPackage</span><span class="o">(</span><span class="s">"..source.one.."</span><span class="o">,</span> <span class="s">"..foo.."</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="class-dependency-checks"><strong>Class Dependency Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-3.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">haveNameMatching</span><span class="o">(</span><span class="s">".*Bar"</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyHaveDependentClassesThat</span><span class="o">().</span><span class="na">haveSimpleName</span><span class="o">(</span><span class="s">"Bar"</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="class-and-package-containment-checks"><strong>Class and Package Containment Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-4.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">haveSimpleNameStartingWith</span><span class="o">(</span><span class="s">"Foo"</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">resideInAPackage</span><span class="o">(</span><span class="s">"com.foo"</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="inheritance-checks"><strong>Inheritance Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-5.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">implement</span><span class="o">(</span><span class="nc">Connection</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">haveSimpleNameEndingWith</span><span class="o">(</span><span class="s">"Connection"</span><span class="o">)</span>
</code></pre></div></div>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-6.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">areAssignableTo</span><span class="o">(</span><span class="nc">EntityManager</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyHaveDependentClassesThat</span><span class="o">().</span><span class="na">resideInAnyPackage</span><span class="o">(</span><span class="s">"..persistence.."</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="annotation-checks"><strong>Annotation Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-7.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">classes</span><span class="o">().</span><span class="na">that</span><span class="o">().</span><span class="na">areAssignableTo</span><span class="o">(</span><span class="nc">EntityManager</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
<span class="o">.</span><span class="na">should</span><span class="o">().</span><span class="na">onlyHaveDependentClassesThat</span><span class="o">().</span><span class="na">areAnnotatedWith</span><span class="o">(</span><span class="nc">Transactional</span><span class="o">.</span><span class="na">class</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="layer-checks"><strong>Layer Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-8.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">layeredArchitecture</span><span class="o">()</span>
<span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"Controller"</span><span class="o">).</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"..controller.."</span><span class="o">)</span>
<span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"Service"</span><span class="o">).</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"..service.."</span><span class="o">)</span>
<span class="o">.</span><span class="na">layer</span><span class="o">(</span><span class="s">"Persistence"</span><span class="o">).</span><span class="na">definedBy</span><span class="o">(</span><span class="s">"..persistence.."</span><span class="o">)</span>
<span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"Controller"</span><span class="o">).</span><span class="na">mayNotBeAccessedByAnyLayer</span><span class="o">()</span>
<span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"Service"</span><span class="o">).</span><span class="na">mayOnlyBeAccessedByLayers</span><span class="o">(</span><span class="s">"Controller"</span><span class="o">)</span>
<span class="o">.</span><span class="na">whereLayer</span><span class="o">(</span><span class="s">"Persistence"</span><span class="o">).</span><span class="na">mayOnlyBeAccessedByLayers</span><span class="o">(</span><span class="s">"Service"</span><span class="o">)</span>
</code></pre></div></div>
<h2 id="cycle-checks"><strong>Cycle Checks</strong></h2>
<p><img src="https://pandemoniodigital.es/assets/images/arch-unit/diagram-9.png" alt="" /></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">slices</span><span class="o">().</span><span class="na">matching</span><span class="o">(</span><span class="s">"com.myapp.(*).."</span><span class="o">).</span><span class="na">should</span><span class="o">().</span><span class="na">beFreeOfCycles</span><span class="o">()</span>
</code></pre></div></div>
<p>Además de definir nuestras propias reglas, existen reglas genéricas que se pueden aplicar. Por lo que he estado viendo es muy configurable y dispone de plugins tanto para maven como para gradle. La documentación muestra todas las posibles aplicaciones de esta libería.</p>
<h1 id="un-ejemplo-de-baeldung">Un ejemplo de Baeldung</h1>
<p>En Baeldung podemos encontrar una poc de esta libería: <a href="https://www.baeldung.com/java-archunit-intro">https://www.baeldung.com/java-archunit-intro</a></p>Diego Rubio AbujasPatrón Sidecar2021-08-06T21:00:00+00:002021-08-06T21:00:00+00:00https://pandemoniodigital.es/arquitectura/2021/08/06/sidecar<p><img src="https://pandemoniodigital.es/assets/images/sidecar/cover.png" alt="Imagen portada" class="page.image" /></p>
<p>En esta entrada os voy a hablar de un patrón que veo cada vez con más en muchos de los proyectos por lo que he pasado, hablamos del patrón sidecar.</p>
<h1 id="qué-es">¿Qué es?</h1>
<p>Cada vez es más frecuente que diversos proyectos requieran de servicios similares relacionadas con por ejemplo la seguridad, la supervisión, registro o configuración. Y que dispongamos de componentes que se repiten en diversas aplicaciones.</p>
<p>Si estas funcionalidades están muy relacionadas con la aplicación se podrían incluir como parte de esta. No obstante hacer esto nos impediría aislar la funcionalidad, además de no poder reutilizarla en otros servicios.</p>
<p>La idea es extender a nuestra aplicación una nueva funcionalidad mediante un proceso que se va a ejecutar en paralelo a esta. Es decir será una aplicación que se ejecutará en paralelo a nuestra aplicación principal. Ya vamos viendo un poco la analogía de sidecar</p>
<p>Al oír todo esto piensas, y por qué no incluir esa nueva funcionalidad como una biblioteca o librería en nuestro proyecto. Y de hecho es lo que se ha venido haciendo tradicionalmente. Con la llegada de los contenedores se abre estas nueva posibilidad, una aplicación con otra aplicación anexa.</p>
<h2 id="service-mesh-o-malla-de-servicios">Service Mesh o Malla de Servicios</h2>
<p>Al indagar sobre Sidecar surge mucho este curioso término que este va bastante ligado. Qué es service mesh o malla de servicios. En la web <a href="https://www.aplyca.com/es/blog/service-mesh">https://www.aplyca.com/es/blog/service-mesh</a> encontramos la siguiente definición</p>
<blockquote>
<p>En términos generales, un service mesh puede ser considerado como una infraestructura de software dedicada a manejar la comunicación entre microservicios. Proporciona y permite aplicaciones basadas en contenedores y microservicios, los cuales se integran directamente desde el interior del clúster.
Un service mesh proporciona servicios de vigilancia, escalabilidad y alta disponibilidad a través de una API de servicios, en lugar de obligar su implementación en cada microservicio. El beneficio está en reducir la complejidad operativa asociada con las aplicaciones modernas de microservicios.</p>
</blockquote>
<p>Y bien el <strong>patrón sidecar</strong> nos permite que junto a nuestro contenedor de la aplicación principal dispongamos de otro contenedor más pequeño que vaya enganchado como un sidecar a una moto a nuestra aplicación y nos permite que nuestra aplicación interactúe con esta malla de servicios.</p>
<h1 id="sidecar-vs-librearía-vs-servicios-externos">Sidecar vs Librearía vs Servicios externos</h1>
<p>@<a href="https://www.bbva.com/es/patron-sidecar-en-seguridad/">https://www.bbva.com/es/patron-sidecar-en-seguridad</a></p>
<p>Extraído de esta web tenemos una tabla comparativa con estas tres alternativas, aparte de un artículo muy interesante sobre Sidecar.</p>
<h2 id="intraproceso-librerías">Intraproceso (librerías)</h2>
<p>VENTAJAS:</p>
<ul>
<li>Uso eficiente de los recursos, al estar dentro de la propia aplicación.</li>
<li>Sin latencia</li>
</ul>
<p>DESVENTAJAS:</p>
<ul>
<li>Sin aislamiento, un problema en un componente puede afectar a los demás componentes de la aplicación</li>
<li>Una versión por cada lenguaje</li>
<li>Gestión de dependencias, problemas de integración…</li>
</ul>
<h2 id="servicios-externos">Servicios externos</h2>
<p>VENTAJAS</p>
<ul>
<li>Uso del lenguaje/tecnología más adecuado</li>
<li>Gestión de dependencias</li>
</ul>
<p>DESVENTAJAS</p>
<ul>
<li>Latencia</li>
<li>Distinto ciclo de vida, problemas de integración (interfaces)</li>
<li>Seguridad, Access Control, etc</li>
</ul>
<h2 id="sidecar">Sidecar</h2>
<p>VENTAJAS</p>
<ul>
<li>Poliglota, permite trabajar con otros lenguajes o tecnologías diferentes a la de la aplicación principal</li>
<li>Desacoplado de dependencias y librerías</li>
<li>No compartido, sin problemas de integración</li>
<li>Transparencia (casi siempre), sin problemas de integración</li>
</ul>
<p>DESVENTAJAS</p>
<ul>
<li>Compite por recursos</li>
<li>Complejidad</li>
</ul>
<h2 id="sidecar-y-kubernetes">Sidecar y Kubernetes</h2>
<p>De igual manera, hablar de sidecar es hablar de contenedores, y en concreto de <strong>pods</strong>. Como bien sabemos un <strong>pod</strong> en <strong>kubernetes</strong> se puede definir de la siguiente forma:</p>
<blockquote>
<p>Pods are the smallest deployable units of computing that you can create and manage in Kubernetes.
A Pod (as in a pod of whales or pea pod) is a <strong>group of one or more containers</strong>, with shared storage and network resources, and a specification for how to run the containers. A Pod’s contents are always co-located and co-scheduled, and run in a shared context. A Pod models an application-specific “logical host”: it contains one or more application containers which are relatively tightly coupled. In non-cloud contexts, applications executed on the same physical or virtual machine are analogous to cloud applications executed on the same logical host.
As well as application containers, a Pod can contain init containers that run during Pod startup. You can also inject ephemeral containers for debugging if your cluster offers this.</p>
</blockquote>
<p>Es decir que en un pod podemos tener varios contenedores en ejecución de manera simultanea. Uno podría tratarse de nuestra aplicación principal, y el otro el sidecar interactuando con la malla de servicios; o bien haciendo de poliglota.</p>
<p>Ilustro más abajo un ejemplo:</p>
<p><img src="https://pandemoniodigital.es/assets/images/sidecar/diagram1.png" alt="Diagrama" class="page.image" /></p>
<p>Esto seria un ejemplo con sidecar para un visualizador de logs.</p>
<h2 id="spring-boot-y-sidecar">Spring Boot y Sidecar</h2>
<p>@<a href="https://cloud.spring.io/spring-cloud-netflix/multi/multi__polyglot_support_with_sidecar.html">https://cloud.spring.io/spring-cloud-netflix/multi/multi__polyglot_support_with_sidecar.html</a></p>
<p>Spring Cloud tiene una <strong>funcionalidad poliglota</strong> que permite utilizar aplicaciones que no sean en java dentro de Spring Cloud y Eureka. Esta solución proporcionada por <strong>Spring Cloud Sidecar</strong> está inspirada en <a href="https://github.com/Netflix/Prana">Netflix Parana</a>. Esta solución no solo implementa la función de puerta de enlace proxy Zuul, sino que también proporciona un servicio HTTP. Explicado de otra manera podemos ponerle un sidecar a una aplicación no hecha en Java para que vaya a través de Spring Cloud con Eureka, de forma que proporcionaría un API HTML Por lo tanto, Sidecar no solo puede representar el servicio de aplicaciones heterogéneas, sino también informar a Eureka si la aplicación se inicia o se cierra a través del resultado de la llamada de su servicio de verificación de estado. Incluyo una imagen a ver si lo puedo explicar de otra forma:</p>
<p><img src="https://pandemoniodigital.es/assets/images/sidecar/diagram2.png" alt="Diagrama" class="page.image" /></p>
<p>En esta imagen podemos ver cono mediante Sidecar podemos hacer que una aplicación en node dispone de Zuul Eureka y Hystrix.</p>
<p><img src="https://pandemoniodigital.es/assets/images/sidecar/diagram3.png" alt="Diagrama" class="page.image" /></p>
<p>En esta otra imagen podemos ver otro ejemplo de una aplicación que no es JVM y dispone de de un sidecar para disponer de un api y Eureka.</p>Diego Rubio AbujasGitHub Copilot2021-07-25T05:33:00+00:002021-07-25T05:33:00+00:00https://pandemoniodigital.es/documentaci%C3%B3n/2021/07/25/github-copilot<p><img src="https://pandemoniodigital.es/assets/images/github-copilot/logo.png" alt="" /></p>
<p>En esta entrada vengo a hablar un poco sobre qué es <strong>GitHub Copilot</strong>, que nos puede ofrecer y qué posible futuro nos trae al mundo del desarrollo. El origen del interés de esta tecnología viene de un grupo que mantengo con antiguos excompañeros.</p>
<p>Según podemos observar en su <a href="https://copilot.github.com/">página web</a> se trata de un <strong>asistente al desarrollo</strong>, el cual mediante inteligencia artificial y partiendo de comentarios genera la implementación de código. Hace algo parecido a <em>pair programming</em> pero con una IA.</p>
<ul>
<li>A partir de comentarios implementar funcionalidades como podemos ver en el siguiente ejemplo.</li>
</ul>
<p><img src="https://pandemoniodigital.es/assets/images/github-copilot/snap1.png" alt="" /></p>
<ul>
<li>Puede realizar una predicción del código que hay que repetir y autocompletarlo por nosotros.</li>
</ul>
<p><img src="https://pandemoniodigital.es/assets/images/github-copilot/snap2.png" alt="" /></p>
<ul>
<li>Definir tests a partir del nombre de lo que queremos testear. En el caso de que no se adecue del todo a lo que queremos también puede mostrar alternativas a las soluciones.</li>
</ul>
<p><img src="https://pandemoniodigital.es/assets/images/github-copilot/snap3.png" alt="" /></p>
<p>De hace tiempo atrás he observado como mi IDE Intellij cada vez me muestra sugerencias más acertadas, ya a día de hoy me tiene tan alado que prácticamente es escribir un carácter y ya prácticamente me sugiere la línea entera Esto es quizás un paso más, ya no solo es autocompletar la clase, método o nombre de variable; estamos hablando de que te implemente el método entero a partir de un comentario o un nombre de función.</p>
<p>Por el momento Copilot no se encuentra disponible para todos los lenguajes y está en una fase <strong>experimental</strong>. Me he presentado a ver si me envían una invitación pero por lo que tengo entendido está como un plugin para <strong>Visual Studio Code</strong> y para lenguajes como <em>Python, Go, Javascript y Java</em>.</p>
<p>En la siguiente imagen podemos observar el funcionamiento de esta IA.</p>
<p><img src="https://pandemoniodigital.es/assets/images/github-copilot/diagram.png" alt="" /></p>
<p>Como podemos observar en la imagen este servicio utiliza para proveer la sugerencia <strong>OpenAI Codex Model</strong> del que vamos a hablar a continuación:</p>
<h1 id="openai-codex-model">OpenAI Codex Model</h1>
<p>Se trata de una <strong>modelo de aprendizaje automático</strong> desarrollado por <strong>OpenAI</strong> que impulsa GitHub Copilot. Los creadores de este modelo se inspiraron en <em>GPT-3</em> para su creación. Este modelo de aprendizaje automático se nutre de código publico existente en internet (Entiendo que en los repositorios públicos de GitHub entre otros).</p>
<p>De GPT-3 sabemos que se trata de un modelo de lenguaje, lo cual indica que su objetivo es realizar una predicción de lo que vendrá a continuación basándose en datos previos. Podría decirse que es como un “autocompletar” pero referente a codificación. Las respuestas dadas por este modelo de lenguaje son “posibilidades” pueden ser más o menos acertadas.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Hay que aclarar que esta IA <strong>no sustituye al desarrollador</strong>, la codificación es solo una parte de las muchas tareas que debe realizar un desarrollador. Los autores de OpenAI Codex Model señalan que su estado actual puede reducir costes de producción de software y aumentar la productividad pero no remplazará otras actividades de desarrollo.</p>
<p>Esta IA se nutre de código open souce subido a la plataforma de GitHub por otros desarrolladores. Por lo que he podido leer este nuevo sistema será <strong>de pago</strong> y <strong>no estará bajo licencia copyleft</strong>. La IA de Github utiliza codigo bajo licencia GPL para entrenarse y realizar su funcion de autocompletado: todo esto ha provocado cierto malestar y controversia entre la comunidad de desarrolladores ya que su código se está utilizando para que otra empresa pueda lucrarse con su trabajo aportado y hecho público.</p>Diego Rubio AbujasCheckstyle maven plugin2021-05-17T11:00:00+00:002021-05-17T11:00:00+00:00https://pandemoniodigital.es/testing/2021/05/17/checkstyle<p><img src="https://pandemoniodigital.es/assets/images/checkstyle/checkstyle.png" alt="Logo" class="page.image" /></p>
<p>Una de las cosas que me resultaron muy interesante tras mi breve paso por el mundo de Angular y Typescript era la posibilidad de incluir en la construcción del proyecto unos chequeos de estilo, de forma que podemos incluir reglas de codificación que deben cumplirse por todo el equipo de desarrollo. Con normas de estilo me refiero a que , por ejemplo, se deje un espacio en blanco, los nombres de la variables no empiecen por miyúusculas, o cualquier cosilla que se nos ocurra, por ejemplo que determinadas variables empiecen por “_” o cosas así. De esta forma tras construir nuestra aplicación no solo controlamos testeo, que sea compilable y que cumpla con sonar, sino que podemos definir nuestras propias normas de calidad de código.</p>
<p>Bien, pues para maven existe un plugin que nos permite definir nuestras propias reglas de estilo, y que como parte del proceso de construcción introduce una nueva fase llamada <code class="language-plaintext highlighter-rouge">checkstyle</code> en la que se comprueba que todo el código sigue unas reglas definidas por defecto o bien en un fichero xml.</p>
<p>Además es posible incluir en nuestro pipeline de construcción una comprobación para que como parte del proceso de construcción, testeo y despliegue se compruebe que el código sigue las reglas de estilo que queramos definir.</p>
<h1 id="links-de-interés">Links de interés:</h1>
<p><a href="https://maven.apache.org/plugins/maven-checkstyle-plugin">Apache Maven Checkstyle Plugin</a></p>
<p><a href="https://cckstyle.org">Web de Checkstyle</a></p>
<p><a href="http://maven.apache.org/plugins/maven-checkstyle-plugin/checkstyle.html">Ejemplo de reporte de Checkstyle</a></p>
<p><a href="https://www.baeldung.com/checkstyle-java">Ejemplo de Baeldung</a></p>
<h1 id="ventajas">Ventajas</h1>
<ul>
<li>Facilita le mantenimiento de código</li>
<li>Asegura la calidad</li>
<li>Facilidad de uso</li>
<li>Permite implementar nuestras propias reglas de estilo</li>
</ul>
<h1 id="estándares-de-codificación-en-java">Estándares de codificación en Java</h1>
<p>Aunque podemos definir nuestros propios estándares de codificación. Para desarrollo en Java hay dos convenciones muy utilizadas, una la sugerida por Sun y otra por Google. Incluyo más abajo links a las web donde se especifican todas las normas de codificaciones de cada uno de esto estándadres y que pueden ser configurados en el plugin.</p>
<h2 id="java-code-convention"><a href="https://www.oracle.com/technetwork/java/codeconventions-150003.pdf">Java Code Convention</a></h2>
<p>Esta convención fue desarrollada por Sun. Destaca la importancia de tener una codificación con convenciones similares basándose en que:</p>
<ul>
<li>El 80% del tiempo lo pasamos haciendo correcciones y mantenimiento.</li>
<li>Raramente el mantenimiento es realizado por el propio autor.</li>
<li>Seguir una convención de codificiación hace el código más legible.</li>
<li>Necesidad de limpieza en el código que se entrega.</li>
</ul>
<h3 id="resumen-de-las-conventions-que-se-sugieren"><a href="https://gist.github.com/goldbattle/9283399">Resumen de las conventions que se sugieren</a></h3>
<h2 id="google-java-style-guide"><a href="https://google.github.io/styleguide/javaguide.html">Google Java Style Guide</a></h2>
<p>Otra interesante convención desarrollada por Google para Java.</p>Diego Rubio AbujasKarate2021-03-15T23:00:00+00:002021-03-15T23:00:00+00:00https://pandemoniodigital.es/testing/2021/03/15/karate<p><img src="https://pandemoniodigital.es/assets/images/karate/logo.png" alt="" /></p>
<blockquote>
<p>Karate una herramienta de open source que combina la automatización de pruebas de API, pruebas de rendimiento e incluso la automatización de la interfaz de usuario en un marco único y unificado. Utiliza sintaxis BDD popularizada por Cucumber. Genera informes HTML y puede ejecutar sus pruebas en paralelo para aumentar la velocidad.</p>
</blockquote>
<p>Ya hemos hablado anteriormente sobre <a href="https://pandemoniodigital.es/testing/2021/01/11/cucumber-spring-boot.html">Cucumber</a> lo que viene siendo el TDD, BDD, Gherkin y toda la pesca. En esta ocasión vamos a hablar de <a href="https://github.com/intuit/karate">Karate</a>.</p>
<p>Esta herramienta de testeo llego a mis manos a raíz de un proyecto en el que estuve inmerso durante los últimos meses. El propio nombre y su curioso logo me dejó un poco estupefacto, pero bueno el tema de los nombres de aplicaciones ya es conocido.</p>
<p>Karate es una herramienta de código abierto para realizar pruebas de BDD sobre nuestra API Rest. Utiliza una sintaxis Gherkin. La diferencia principal entre Karate y Cucumber es que este es por así decirlo como un Cucumber mucho mas simplificado, a continuación enumeramos algunas funcionales que se citan en esta <a href="https://www.sngular.com/es/automatizacion-de-pruebas-con-karate-i/">web</a>.</p>
<h2 id="qué-nos-proporciona-karate">¿Qué nos proporciona Karate?</h2>
<ul>
<li>Setup inicial sencillo, rápido y directo.</li>
<li>BDD unificado desde un mismo fichero. No es necesario definir los steps en otras ubicaciones.</li>
<li>Pruebas simples, concisas, legibles y fáciles de mantener.</li>
<li>Posibilidad de utilizar clases Java para utilidades complejas o funciones de ayuda que facilita su depuración y mantenibilidad.</li>
<li>No se requieren conocimientos de programación avanzados. Acciones habituales sobre APIs implementadas mediante lenguaje natural.</li>
<li>Posibilidad de ejecutar pruebas en paralelo.</li>
<li>Reutilización de features pudiendo ser usadas como precondiciones o funciones de otros pruebas.</li>
<li>Implementación sencilla</li>
<li>Soporte nativo para aserciones sobre JSON y XML.</li>
<li>Permite lectura de ficheros CSV (Data Driven Testing)</li>
<li>Motor de JavaScript integrado.</li>
<li>Importación de archivos csv o json con información. En cucumber, las variables están estáticas en una tabla.</li>
<li>Documentación y ejemplos completos.</li>
<li>Soporte websockets.</li>
<li>Informes en HTML con el resultado de las pruebas</li>
</ul>
<p>En resumen es un Cucumber mas simplificado y con la posibilidad de incluir javascript para nuestras pruebas. Además de generar unos informes muy completos en html con el resultado de nuestras pruebas.</p>
<p>Para plasmar esta herramienta lo que haremos será coger el mismo ejemplo que realizamos con cucumber anteriormente y hacerlo con Karate.</p>
<h1 id="prueba-de-concepto">Prueba de concepto</h1>
<p>La prueba que realizaremos será pasar de la anterior prueba de concepto que realizamos en cucumber a realizarla con Karate, haremos una comparativa entre las distintas soluciones para poder mostrar las bondades de Karate.</p>
<p>Recordemos un poco la prueba tenemos dos endpoints simplones, uno devuelve un saludo con el nombre que pasamos por <em>param</em>, y otro recibe dos <em>params</em> y devuelve la suma de ambos.</p>
<ul>
<li><a href="https://github.com/drubioa/demo-cucumber-spring-boot">Repositorio Cucumber</a></li>
<li><a href="https://github.com/drubioa/demo-karate-springboot">Repositorio Karate</a></li>
</ul>
<h2 id="dependencias">Dependencias</h2>
<p>El primer paso será incluir las dependencias que necesitamos para lanzar los test en Karate. Sería necesario incluir las siguientes dependencias en nuestro maven.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><properties></span>
<span class="nt"><karate.version></span>0.9.6<span class="nt"></karate.version></span>
<span class="nt"></properties></span>
...
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.intuit.karate<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>karate-apache<span class="nt"></artifactId></span>
<span class="nt"><version></span>${karate.version}<span class="nt"></version></span>
<span class="nt"><scope></span>test<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="nt"><groupId></span>com.intuit.karate<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>karate-junit5<span class="nt"></artifactId></span>
<span class="nt"><version></span>${karate.version}<span class="nt"></version></span>
<span class="nt"><scope></span>test<span class="nt"></scope></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<h2 id="configuración">Configuración</h2>
<p>Para configurar nuestras pruebas con karate tenemos dos clases por un lado karate-config.js en la que vamos a definir la url (Se podrían definir muchísimas cosas más, os sugiero que consultéis la documentación).</p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">fn</span><span class="p">()</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">config</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">baseUrl</span> <span class="p">:</span> <span class="dl">'</span><span class="s1">http://localhost:</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">karate</span><span class="p">.</span><span class="nx">properties</span><span class="p">[</span><span class="dl">'</span><span class="s1">port</span><span class="dl">'</span><span class="p">]</span>
<span class="p">};</span>
<span class="k">return</span> <span class="nx">config</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Luego en otra clase vamos a configurar un <code class="language-plaintext highlighter-rouge">@SpringBootTest</code> que lanzará nuestra aplicación en un puerto aleatorio, y una vez arrancada se lanzarán los test de Karate que cargarán un fichero feature en el que definiremos las pruebas que queremos hacer.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@SpringBootTest</span><span class="o">(</span><span class="n">classes</span> <span class="o">=</span> <span class="nc">DemoApplication</span><span class="o">.</span><span class="na">class</span><span class="o">,</span> <span class="n">webEnvironment</span> <span class="o">=</span> <span class="nc">SpringBootTest</span><span class="o">.</span><span class="na">WebEnvironment</span><span class="o">.</span><span class="na">RANDOM_PORT</span><span class="o">)</span>
<span class="kd">public</span> <span class="kd">class</span> <span class="nc">KarateTest</span> <span class="o">{</span>
<span class="nd">@LocalServerPort</span>
<span class="kt">int</span> <span class="n">port</span><span class="o">;</span>
<span class="kd">final</span> <span class="nc">String</span> <span class="n">directory</span> <span class="o">=</span> <span class="s">"classpath:features/"</span><span class="o">;</span>
<span class="nd">@BeforeEach</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">setUp</span><span class="o">(){</span>
<span class="nc">System</span><span class="o">.</span><span class="na">setProperty</span><span class="o">(</span><span class="s">"port"</span><span class="o">,</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">port</span><span class="o">));</span>
<span class="o">}</span>
<span class="nd">@Karate</span><span class="o">.</span><span class="na">Test</span>
<span class="nd">@DisplayName</span><span class="o">(</span><span class="s">"Tests for examples"</span><span class="o">)</span>
<span class="nc">Karate</span> <span class="nf">createUserRoleController</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="nc">Karate</span><span class="o">.</span><span class="na">run</span><span class="o">(</span><span class="n">directory</span> <span class="o">+</span> <span class="s">"foo.feature"</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Si tuviéramos mas ficheros de features, habría que incluir mas métodos con la anotación <code class="language-plaintext highlighter-rouge">@Karate.test</code>.</p>
<h2 id="features">Features</h2>
<p>Y ahora vamos con el kit de la cuestión, si recordamos la prueba que teníamos con Cucumber por un lado definíamos el feature en Gherkin y por otro una clase java en la que programábamos cada una de las sentencias Gherkin , por ejemplo dado un <code class="language-plaintext highlighter-rouge">Then</code> teníamos que desarrollar la comprobación como podemos ver más abajo, y así con todo</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">@Then</span><span class="o">(</span><span class="s">"the client receives status code of 200"</span><span class="o">)</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">checkStatus200</span><span class="o">(){</span>
<span class="n">assertEquals</span><span class="o">(</span><span class="n">responseEntity</span><span class="o">.</span><span class="na">getStatusCode</span><span class="o">()</span> <span class="o">,</span> <span class="nc">HttpStatus</span><span class="o">.</span><span class="na">OK</span><span class="o">);</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Bien, pues aquí es donde esta la magia, lo interesante de Karate. Observemos un momento el fichero de feature de Karate para este ejemplo.</p>
<div class="language-gherkin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">Feature</span><span class="p">:</span> features of gherkin test
<span class="kn">Background</span><span class="p">:</span>
<span class="nf">* </span>url baseUrl
<span class="kn">Scenario Outline</span><span class="p">:</span> client makes call to GET hello
<span class="nf">Given </span>path '/examples/hello'
<span class="nf">And </span>param name = '<span class="nv"><name></span>'
<span class="nf">When </span>method GET
<span class="nf">Then </span>status 200
<span class="nf">And </span> match response contains '<span class="nv"><name></span>'
<span class="nn">Examples</span><span class="p">:</span>
<span class="p">|</span> <span class="nv">name</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Diego</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Sofia</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">Pepe</span> <span class="p">|</span>
<span class="kn">Scenario Outline</span><span class="p">:</span> client makes call to GET to sum numbers
<span class="nf">Given </span>path '/examples/sum'
<span class="nf">And </span>param a = '<span class="nv"><a></span>'
<span class="nf">And </span>param b = '<span class="nv"><b></span>'
<span class="nf">When </span>method GET
<span class="nf">Then </span> status 200
<span class="nf">And </span> match response == '<span class="nv"><result></span>'
<span class="nn">Examples</span><span class="p">:</span>
<span class="p">|</span> <span class="nv">a</span> <span class="p">|</span> <span class="nv">b</span> <span class="p">|</span> <span class="nv">result</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">2</span> <span class="p">|</span> <span class="n">3</span> <span class="p">|</span> <span class="n">5</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">2</span> <span class="p">|</span> <span class="n">4</span> <span class="p">|</span> <span class="n">6</span> <span class="p">|</span>
<span class="p">|</span> <span class="n">2</span> <span class="p">|</span> <span class="n">5</span> <span class="p">|</span> <span class="n">7</span> <span class="p">|</span>
</code></pre></div></div>
<p>No hay más que codificar, no hay que implementar comprobaciones, preparar la llamada rest, etc. Karate internamente ya se encarga de traducir de Gherkin a la llamada al endpoint, ya se encarga de comprobar la respuesta, de hacer match al response y si consultamos la documentación vemos que se nos abre un amplio abanico de opciones a la hora de diseñar nuestras pruebas sin tener que tocar nada de código java, sino directamente transformando y realizando las pruebas de manera automática este Gherkin a lo que deseamos testear.</p>
<h2 id="el-informe">El Informe</h2>
<p><img src="https://pandemoniodigital.es/assets/images/karate/screenshoot.png" alt="" /></p>
<p>Y ya para ir cerrando, si accedemos dentro de nuestro proyecto a la path <code class="language-plaintext highlighter-rouge">/target/surefire-reports/</code> podremos observar una web como la que observamos mas arriba con los distintos escenarios de tes que hemos realizado y el resultado de cada uno de los test, así como mediciones de tiempos, etc.</p>Diego Rubio AbujasSdkman2021-03-11T18:00:00+00:002021-03-11T18:00:00+00:00https://pandemoniodigital.es/pildora/2021/03/11/sdkman-nvm<p><img src="https://pandemoniodigital.es/assets/images/sdkman/logo-sdkman.png" alt="" /></p>
<p>En esta ocasión vengo a hablar de algo que llevaba tiempo queriendo montar en mi máquina y que me ha resultando de gran utilidad.</p>
<p>Pongámonos en situación amigos desarrolladores de <strong>java</strong>, cuántas veces nos ha pasado que tenemos un proyecto que debe ir con la JDK 1.8 y otro que queremos correr con la JDK 11, u 15. O digamos que tenemos un proyecto muy viejuno que va con la 1.6, muchas versiones de la maquina de java tediosas de instalar y de borrar. Configurar muchas variables de entorno en caso de windows.</p>
<p>Bien, <a href="https://sdkman.io/">SdkMan</a> nos proporciona una aplicación que nos permite mediante una consola de comandos cambiar entre distintas versiones de Sdk, también nos permite tener distintas versiones de Maven, Groovy, Kotlin, Scala… etc.</p>
<h2 id="instalación-en-win10">Instalación en Win10</h2>
<p>La web nos proporciona una instalación muy sencilla para Linux y Mac, pero en nuestro caso vamos a instalarlo en nuestro Windows 10. La instalación en Windows 10 nos plantea un problema inicial, y es que se realiza con un script bash. Bien para la instalación lo que hemos hecho es utilizar la consola de GitBash que se nos instaló junto al <a href="https://gitforwindows.org/">Git</a>.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-s</span> <span class="s2">"https://get.sdkman.io"</span> | bash
</code></pre></div></div>
<p>Una vez ejecutamos este comando en mi caso, me dio un error y es que no encuentra zip, unzip. Vaya, otro problema derivado de tener que instalarlo en Windows 10. Bien, la solución por lo que vi en StackOverflow es comentar el siguiente código.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#echo "Looking for unzip..."</span>
<span class="c">#if ! command -v unzip > /dev/null; then</span>
<span class="c"># echo "Not found."</span>
<span class="c"># echo "======================================================================================================"</span>
<span class="c"># echo " Please install unzip on your system using your favourite package manager."</span>
<span class="c"># echo ""</span>
<span class="c"># echo " Restart after installing unzip."</span>
<span class="c"># echo "======================================================================================================"</span>
<span class="c"># echo ""</span>
<span class="c"># exit 1</span>
<span class="c">#fi</span>
<span class="c">#echo "Looking for zip..."</span>
<span class="c">#if ! command -v zip > /dev/null; then</span>
<span class="c"># echo "Not found."</span>
<span class="c"># echo "======================================================================================================"</span>
<span class="c"># echo " Please install zip on your system using your favourite package manager."</span>
<span class="c"># echo ""</span>
<span class="c"># echo " Restart after installing zip."</span>
<span class="c"># echo "======================================================================================================"</span>
<span class="c"># echo ""</span>
<span class="c"># exit 1</span>
<span class="c">#fi</span>
</code></pre></div></div>
<p>Una vez comentado estos fragmentos de código ya podremos instalar el sdkman en nuestro windows 10, o al menos yo en mi caso pude.</p>
<h2 id="utilización">Utilización</h2>
<p>Una vez instalado sdkman, desde la terminal de bash podemos realizar el siguiente comando:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>sdk list java
<span class="o">================================================================================</span>
Available Java Versions
<span class="o">================================================================================</span>
Vendor | Use | Version | Dist | Status | Identifier
<span class="nt">--------------------------------------------------------------------------------</span>
AdoptOpenJDK | | 15.0.2.j9 | adpt | | 15.0.2.j9-adpt
| | 15.0.2.hs | adpt | | 15.0.2.hs-adpt
| | 11.0.10.j9 | adpt | | 11.0.10.j9-adpt
| | 11.0.10.hs | adpt | installed | 11.0.10.hs-adpt
| | 11.0.9.open | adpt | | 11.0.9.open-adpt
| | 8.0.282.j9 | adpt | | 8.0.282.j9-adpt
| | 8.0.282.hs | adpt | | 8.0.282.hs-adpt
| | 8.0.275.open | adpt | | 8.0.275.open-adpt
Alibaba | | 11.0.9.4 | albba | | 11.0.9.4-albba
Amazon | | 15.0.2.7.1 | amzn | installed | 15.0.2.7.1-amzn
| | 11.0.10.9.1 | amzn | | 11.0.10.9.1-amzn
| | 8.282.08.1 | amzn | | 8.282.08.1-amzn
Azul Zulu | | 15.0.2 | zulu | | 15.0.2-zulu
| | 15.0.2.fx | zulu | | 15.0.2.fx-zulu
| | 11.0.10 | zulu | | 11.0.10-zulu
| | 11.0.10.fx | zulu | | 11.0.10.fx-zulu
| | 8.0.282 | zulu | installed | 8.0.282-zulu
| | 8.0.282.fx | zulu | | 8.0.282.fx-zulu
| | 6.0.119 | zulu | | 6.0.119-zulu
BellSoft | | 15.0.2.fx | librca | | 15.0.2.fx-librca
| | 15.0.2 | librca | | 15.0.2-librca
| | 11.0.10.fx | librca | | 11.0.10.fx-librca
| | 11.0.10 | librca | | 11.0.10-librca
| | 8.0.282.fx | librca | | 8.0.282.fx-librca
| | 8.0.282 | librca | | 8.0.282-librca
GraalVM | | 21.0.0.2.r11 | grl | | 21.0.0.2.r11-grl
| | 21.0.0.2.r8 | grl | | 21.0.0.2.r8-grl
| | 20.3.1.2.r11 | grl | | 20.3.1.2.r11-grl
| | 20.3.1.2.r8 | grl | | 20.3.1.2.r8-grl
| | 19.3.5.r11 | grl | | 19.3.5.r11-grl
| | 19.3.5.r8 | grl | | 19.3.5.r8-grl
| | 19.1.0 | grl | | 19.1.0-grl
Java.net | | 17.ea.12 | open | | 17.ea.12-open
| | 17.ea.11 | open | | 17.ea.11-open
| | 17.ea.10 | open | | 17.ea.10-open
| | 17.ea.9 | open | | 17.ea.9-open
| | 17.ea.2.pma | open | | 17.ea.2.pma-open
| | 17.ea.2.lm | open | | 17.ea.2.lm-open
| | 16.ea.36 | open | | 16.ea.36-open
| | 15.0.2 | open | | 15.0.2-open
| | 11.0.10 | open | | 11.0.10-open
| | 11.0.2 | open | | 11.0.2-open
| | 8.0.282 | open | installed | 8.0.282-open
| | 8.0.265 | open | installed | 8.0.265-open
Mandrel | | 21.0.0.0 | mandrel | | 21.0.0.0-mandrel
| | 20.3.1.2 | mandrel | | 20.3.1.2-mandrel
SAP | | 15.0.2 | sapmchn | | 15.0.2-sapmchn
| | 11.0.10 | sapmchn | | 11.0.10-sapmchn
TravaOpenJDK | | 11.0.9 | trava | | 11.0.9-trava
<span class="o">================================================================================</span>
Use the Identifier <span class="k">for </span>installation:
<span class="nv">$ </span>sdk <span class="nb">install </span>java 11.0.3.hs-adpt
<span class="o">================================================================================</span>
</code></pre></div></div>
<p>Podemos visualizar un listado con las distintas versiones de java de los distintos proveedores, si deseamos instalar alguna de ellas únicamente tenemos que seleccionar install y se pondrá descargar e instalar esa sdk. Una vez finalizada la instalación tenemos la opción de ponerla como versión por defecto.</p>
<p>Podemos desinstalar, cambiar version por defecto, utilizar una u otra versión. Y esto no solo se ciñe a java, también a maven, groovy, scala… etc.</p>
<p>Podemos observar la siguiente estructura de carpetas:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">cd</span> .sdkman/
<span class="nv">$ </span><span class="nb">ls
</span>archives/ bin/ candidates/ contrib/ etc/ ext/ src/ tmp/ var/
<span class="nv">$ </span><span class="nb">cd </span>candidates/
<span class="nv">$ </span><span class="nb">ls
</span>java/ maven/ sbt/ scala/
<span class="nv">$ </span><span class="nb">cd </span>java/
<span class="nv">$ </span><span class="nb">ls
</span>11.0.10.hs-adpt/ 15.0.2.7.1-amzn/ 8.0.265-open/ 8.0.282-open/ 8.0.282-zulu/ current/
<span class="nv">$ </span><span class="nb">cd </span>current/
<span class="nv">$ </span><span class="nb">ls
</span>ADDITIONAL_LICENSE_INFO ASSEMBLY_EXCEPTION bin/ conf/ include/ jmods/ legal/ lib/ LICENSE release version.txt
</code></pre></div></div>
<p>Como podemos observar en candidatos tenemos instalados las distintas sdks, y de todas las que hay instalado en current tenemos la actual.</p>
<p>Faltaría, en nuestro caso que utilizamos windows 10, irnos a variables del entorno y configurar como</p>
<p>JAVA_HOME, MVN_HOME, SCALA_HOME, Path las rutas correspondientes a la ubicación donde hayamos instalado el sdkman. De esta forma cada vez que cambiemos la versión candidata se cambiará en nuestra terminal.</p>
<p><img src="https://pandemoniodigital.es/assets/images/sdkman/" alt="" /></p>
<h2 id="nvm"><a href="https://github.com/nvm-sh/nvm">NVM</a></h2>
<p>Al igual que sdkman, para desarrollos en node existe nvm (Node version manager) que al igual que sdk nos posibilita hacer switch entre distintas versiones de node.js y cuyo enlace dejo mas arriba.</p>Diego Rubio AbujasMutation testing2021-03-03T14:00:00+00:002021-03-03T14:00:00+00:00https://pandemoniodigital.es/testing/2021/03/03/mutation-testing<p><img src="https://pandemoniodigital.es/assets/images/mutation-testing/cover.png" alt="" /></p>
<blockquote>
<p>“Las pruebas unitarias también son código fuente”</p>
</blockquote>
<blockquote>
<p>“Mi código funciona perfectamente porque hago pruebas unitarias”</p>
</blockquote>
<p>En esta ocasión abordamos el tema de <em>mutation testing</em> o pruebas de mutación, este tema ya surgió hace un par de años hablando con un compañero sobre pruebas unitarias y lo fácil que era engañarlas para cumplir la cobertura que nos pedía Sonar, ha vuelto hace poco a partir de una conversación sobre virtudes y bondades de las pruebas unitarias. Así que me he decidido a recabar un poco de información sobre el tema y dejarlo plasmado en este artículo.</p>
<p>Las pruebas de mutaciones nos permiten evaluar la calidad de nuestros test. Para ello lo que hacen es, partiendo de una prueba unitarias, realizar “pequeñas variaciones” en el la clase que estamos testeando, y generar lo que denominan “un mutante”. Cada versión cambiada es un mutante y las pruebas deben ser capaces de distinguir el programa original del mutante a partir del comportamiento observado. A esto se le llama matar al mutante. Una vez se han evaluado todos los tipos de mutaciones se utilizan los mutantes que hayan sobrevivido para establecer una puntuación de mutaciones, y ver si es factible y necesario realizar nuevos test entre nuestras pruebas. Si lo pensamos, es como testear los test, es decir probar si nuestros test unitarios son realmente útiles o no.</p>
<p>Del resultado de las pruebas de mutación obtenemos una puntuación que cuanto mayor sea mejor serán nuestros test unitarios.</p>
<blockquote>
<p>Mutation Score = (Killed Mutants / Total number of Mutants) * 100</p>
</blockquote>
<h1 id="tipos-de-mutaciones">Tipos de mutaciones</h1>
<p>Básicamente podemos enumerar los siguientes tipos de mutaciones:</p>
<ol>
<li>Mutación de estado: eliminación o duplicación de determinadas líneas de código</li>
<li>Mutación de valor: se cambia un valor</li>
<li>Mutación de operador: se cambia un operador por otro, un + por - por ejemplo</li>
<li>Mutación de decisión: una estructura de control, un if por ejemplo, es alterado.</li>
</ol>
<h1 id="ventajas">Ventajas</h1>
<p>Algunas de las ventajas que podemos enumerar de las pruebas de mutación son las siguientes:</p>
<ol>
<li>Nos permite evaluar la cobertura “real” de nuestro código.</li>
<li>Nos permite identificar nuevas pruebas que realizar.</li>
<li>Mejora la detección de errores en el desarrollo de softwre.</li>
<li>Permite detectar ambigüedades y fallos en el código.</li>
</ol>
<h1 id="desventajas">Desventajas</h1>
<p>Podemos considerar también algunas desventajas:</p>
<ol>
<li>Son muy costosos de generar y ejecutar este tipo de pruebas, ya que implica repetir las pruebas un elevado número de veces.</li>
<li>Requiere tener acceso al código fuente, no se puede aplicar para pruebas de caja negra.</li>
<li>Es necesario evaluar detalladamente los mutantes que hayan sobrevivido para ver cómo podemos mejorar nuestra batería de test.</li>
<li>Requiere de una automatización para poder generar todos los mutantes.</li>
</ol>
<h1 id="herramientas-de-automatización">Herramientas de automatización</h1>
<h2 id="pitest"><a href="http://pitest.org/">Pitest</a></h2>
<p><img src="https://pandemoniodigital.es/assets/images/mutation-testing/pitest.png" alt="pitest-logo" /></p>
<p>Esta herramienta está pensada para pruebas en Java. Se puede configurar por plugin de maven, ant o por linea de comandos. Se puede integrar además con Gradle, Eclipse, Intellij y otros IDEs.</p>
<p>Esta herramienta nos permite configurar muchos operadores de mutación para poder personalizar la generación de mutantes en este ejemplo que nos muestran en su web, si por ejemplo le indicaramos que sobre el código:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">(</span> <span class="n">i</span> <span class="o">>=</span> <span class="mi">0</span> <span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"foo"</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"bar"</span><span class="o">;</span>
</code></pre></div></div>
<p>Generar un mutante siguiendo CONDITIONALS_BOUNDARY_MUTATOR nos generaría por ejemplo un mutante tal y como el que indicamos a continuación:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="o">(</span> <span class="n">i</span> <span class="o">></span> <span class="mi">0</span> <span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"foo"</span><span class="o">;</span>
<span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
<span class="k">return</span> <span class="s">"bar"</span>
</code></pre></div></div>
<p>Si realizamos la automatización de test con el plugin de maven podemos configuar desde que tipo de generacion de mutantes vamos a utilizar, a si lo haremos concurrentemente o en un solo hilo, donde se generará el reporte, que clases testear o no, etc.</p>
<h3 id="resultado-de-los-tests">Resultado de los Tests</h3>
<p>Como resultado de las pruebas de mutación con Pitest en el reporte podemos tener los siguientes resultados:</p>
<ol>
<li>Mutanta eliminado.</li>
<li>No ha sido cubierto por los test y por tanto el mutante ha sobrevivido.</li>
<li>La mutación no es válida y no ha podido ser ejecutada por la JVM.</li>
<li>Un error en la memoria.</li>
<li>Un error de ejecución.</li>
</ol>
<p>Una vez finalizado las pruebas tendrémos un report con el resultado de los test de mutación. También existe un plugin de SonarQube.</p>
<p><img src="https://pandemoniodigital.es/assets/images/mutation-testing/pitest-report.png" alt="pitest-report" /></p>
<h2 id="stryker-mutator"><a href="https://stryker-mutator.io">Stryker Mutator</a></h2>
<p><img src="https://pandemoniodigital.es/assets/images/mutation-testing/stryker.png" alt="pitest-report" /></p>
<p>Esta herramienta para automatizar test de mutaciónes, está disponible actualmente para Js y Ts, C# y Scala. Entre sus características está la de soportar más de 30 tipos de mutaciones, ser Open Source, realizar los test de de una forma rápida y generar un reporte de los test que realiza.</p>
<p>En este <a href="https://stryker-mutator.io/docs/mutation-testing-elements/supported-mutators/">enlace</a> podemos ver en qué consisten esos 30 tipos de mutaciones. Hay que ver también que dependiendo del lenguaje hay algunos tipos de operadores que no se soportan a la hora de generar mutantes.</p>
<p><a href="https://www.notion.so/Mutation-Testing-ba27029528544907a5fa6ee7188c5a0d#1444f50f23db4f90bf5ecefd52a285a7"></a></p>Diego Rubio AbujasSpring Cache + Redis2021-02-24T22:30:00+00:002021-02-24T22:30:00+00:00https://pandemoniodigital.es/demo/2021/02/24/poc-spring-cache-redis<p><img src="https://pandemoniodigital.es/assets/images/poc-spring-cache-redis/superior.png" alt="" /></p>
<p><a href="https://github.com/drubioa/demo-spring-cache-redis">Enlace repositorio de github</a></p>
<p>En esta ocasión nos metemos a hablar un poco sobre <strong>Spring Cache</strong> y los distintos tipos de caches que podemos incluir en nuestra aplicación. Esta problemática ya la he visto en varios proyectos, disponemos de una Servicio que realiza una operación que tarda en realizarse, por ejemplo puede que tenga llamar a varios servicios, hacer consultas pesadas, cálculos, etc… y resulta que es una operación que se realiza mucho por parte de nuestros usuarios y cuyos resultados no cambian en el tiempo. Mi primer encuentro con Spring Cache fue en un proyecto de banca en el cual había que obtener los movimientos de un cliente, y estas solo se actualizaban cada 2 o 3 días, y encima el usuario estaba constantemente pidiéndolas si entraba en la aplicación varias veces al día.</p>
<p>Bien la solución ante este tipo de situaciones es utilizar una caché: la primera vez que se tiene que realizar esta operación almacenamos en una memoria (una caché) el resultado y le asignamos un identificado único que suele estar asociado a la petición que nos ha resultado este usuario. De esta forma, cuando el usuario vuelva a realizar la petición primero comprobaremos si ya existe un resultado a esta petición en nuestra memoria cache y de ser así en lugar de volver a realizar la operación directamente devolvemos el resultado de esta búsqueda en nuestra memoria caché.</p>
<p>¿Qué es una caché? Bien, una caché es una capa de almacenamiento de alta velocidad en la que almacenamos de manera temporal información.</p>
<h2 id="spring-caché">Spring Caché</h2>
<p>En el siguiente <a href="https://spring.io/guides/gs/caching/">enlace</a> disponemos de un ejemplo de cómo configurar una aplicación simple con la caché que nos proporciona Spring, este ejemplo sencilla funciona con dos:</p>
<ol>
<li>@Cacheable: esta anotación sobre un método nos permitirá indica que se caché lo que devuelva este método; de forma que si se vuelve a realizar una llamada a este método en lugar de llevarse a cabo la operación se tira de la memoria y se devuelve el objeto.</li>
<li>@EnableCaching: Esta anotación se indica donde tengamos el Application, e indica a Spring que habilite la caché al arrancar. Al incluir esta anotación se configura automáticamente la caché, podemos especificar mas configuraciones en el yml.</li>
</ol>
<p>Hay una documentación muy amplia en la <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache">web de Spring</a>.</p>
<p>La Caché al igual que @Transactional está pensaba en Spring para que suponga el menor código posible como podemos ver.</p>
<h3 id="tipos-de-caché">Tipos de Caché</h3>
<p>Según <a href="https://www.javatpoint.com/spring-boot-caching">javapoint</a> podríamos decir que hay cuatro tipos de cachés:</p>
<ol>
<li>Cache en memoria:</li>
</ol>
<blockquote>
<p>In-memory caching increases the performance of the application. It is the area that is frequently used. <strong>Memcached</strong> and <strong>Redis</strong> are examples of in-memory caching. It stores key-value between application and database. Redis is an in-memory, distributed, and advanced caching tool that allows backup and restore facility. We can manage cache in distributed clusters, also.</p>
</blockquote>
<ol>
<li>Base de datos</li>
</ol>
<blockquote>
<p>Database caching is a mechanism that generates web pages on-demand (dynamically) by fetching the data from the database. It is used in a multi-tier environment that involved clients, web-application server, and database. It improves scalability and performance by distributing a query workload. The most popular database caching is the first level cache of Hibernate.</p>
</blockquote>
<ol>
<li>Web server</li>
</ol>
<blockquote>
<p>Web server caching is a mechanism that stores data for reuse. For example, a copy of a web page served by a web server. It is cached for the first time when a user visits the page. If the user requests the same next time, the cache serves a copy of the page. It avoids server form getting overloaded. Web server caching enhances the page delivery speed and reduces the work to be done by the backend server.</p>
</blockquote>
<ol>
<li>CDN Caching</li>
</ol>
<blockquote>
<p>The CDN stands for Content Delivery Network. It is a component used in modern web applications. It improves the delivery of the content by replicating commonly requested files (such as HTML Pages, stylesheet, JavaScript, images, videos, etc.) across a globally distributed set of caching servers.
It is the reason CDN becomes more popular. The CDN reduces the load on an application origin and improves the user experience. It delivers a local copy of the content from a nearby cache edge (a cache server that is closer to the end-user), or a Point of Presence (PoP).</p>
</blockquote>
<p>Por otra parte consultando una <a href="https://www.paradigmadigital.com/dev/caches-por-donde-empezar/">entrada de paradigma</a> se nos indica que hay dos tipos de cache:</p>
<p>Tenemos la <strong>caché local</strong> o de proceso, esta caché pertenece a la aplicación que requiere de utilizar una caché y asigna un espacio dinámico de memoria.</p>
<p>En general vamos a querer que nuestra aplicación pueda escalar en varios contenedores de forma que para ello necesitaremos de una <strong>cache distribuid</strong>a que forme una gran caché lógica.</p>
<h3 id="recorridos-por-cachés-distribuidas">Recorridos por Cachés Distribuidas</h3>
<p>Vamos a mencionar algunas de las cachés distribuidas más conocidas y algunas cosillas curiosas que nos puede proporcionar, en la prueba de concepto que hemos realizado hemos utilizado <strong>Redis</strong>, pero se podría haber hecho con cualquiera de las que vamos a citar.</p>
<ol>
<li><strong>Redis</strong>: esta caché es un sistema de almacenamiento en memoria de código abierto, además de una caché se puede utilizar como memoria o incluso como broker de mensaje. Os recomiendo que os paséis por su <a href="https://redis.io/">web</a> y le echéis un vistazo a su tutorial interactivo.</li>
<li><strong>Hazelcasts</strong>: es un DataGrid de memoria distribuido hecho en java, también de código abierto. Está pensado para un clúster de computadoras y trabajar con escalabilidad horizontal.</li>
<li><strong>MongoDb</strong> In Memory: me ha sorprendido ver que mongo Tambien dispone de un almacenamiento en memoria de alta disponibilidad que posibilita utilizar una caché en formato de Documentos Bson.</li>
<li>Otras que podemos citar y que no conozco demasiado son : <em>Couchbase</em>, <em>Memcached</em> y <em>Ehcache</em>. Son muy utilizadas y también merecería la pena indagar mas en cómo funcionan y qué beneficios aportan.</li>
</ol>
<h3 id="cache-vs-buffer">Cache Vs Buffer</h3>
<p>Y a raíz de lo que nos plantea en la web de Spring nos planteamos la siguiente pregunta: ¿Qué diferencias hay entre la caché y el buffer?. Vamos a poner la tabla que se nos plantea en <a href="https://www.javatpoint.com/spring-boot-caching">javapoint</a> :</p>
<table>
<thead>
<tr>
<th>Caché</th>
<th>Buffer</th>
</tr>
</thead>
<tbody>
<tr>
<td>LRU: Basada en uso mas reciente</td>
<td>FIFO: Primero en entrar primero en salir</td>
</tr>
<tr>
<td>Su tamaño viene determinado por el tamaño de la página de caché</td>
<td>Su tamaño viene determinado por el tamaño de bloque de memoria IO del buffer</td>
</tr>
<tr>
<td>Periodo de vida muy largo</td>
<td>Periodo de memoria muy corto</td>
</tr>
<tr>
<td>Otimizado para lecturas</td>
<td>Optimizado para escrituras</td>
</tr>
</tbody>
</table>
<h1 id="prueba-de-concepto">Prueba de Concepto</h1>
<p>Vamos a realizar una prueba de concepto en la que trabajaremos dos de los temas de los que hemos hablado anteriormente.</p>
<p>Por un lado vamos a disponer de un microservicio desarrollado con spring boot que dispondrá de un servicio que tendrá una operación costosa que tardara varios segundos en realizarse. Este método estará cacheado mediante la anotación cacheable:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nd">@Cacheable</span><span class="o">(</span><span class="n">value</span><span class="o">=</span><span class="s">"example"</span><span class="o">,</span> <span class="n">key</span><span class="o">=</span><span class="s">"#n"</span><span class="o">)</span>
<span class="kd">public</span> <span class="nc">Long</span> <span class="nf">example</span><span class="o">(</span><span class="nc">Long</span> <span class="n">n</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// Example of heavy service wait 5 seconds after return value</span>
<span class="k">try</span> <span class="o">{</span>
<span class="nc">Thread</span><span class="o">.</span><span class="na">sleep</span><span class="o">(</span><span class="n">waitSeconds</span> <span class="o">*</span> <span class="mi">1000</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"return value after {} seconds"</span><span class="o">,</span> <span class="n">waitSeconds</span><span class="o">);</span>
<span class="k">return</span> <span class="n">n</span><span class="o">;</span>
<span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
<span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">RuntimeException</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<p>Al incluir la anotación @Cacheable indicamos que cuando se llame a este método antes de ralizar la operacion Spring comprobará si ya existe una cache de tipo example con el valor de ejemplo que estamos utilizando.</p>
<h3 id="configuración">Configuración</h3>
<p>Para configurar nuestra caché redis hemos tenido que incluir la siguiente configuración en nuestro yaml:</p>
<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="err"> </span> <span class="na">spring</span><span class="pi">:</span>
<span class="na">cache</span><span class="pi">:</span>
<span class="na">type</span><span class="pi">:</span> <span class="s">redis</span>
<span class="na">redis</span><span class="pi">:</span>
<span class="na">host</span><span class="pi">:</span> <span class="s">redis</span>
<span class="na">port</span><span class="pi">:</span> <span class="m">6379</span>
</code></pre></div></div>
<p>Además hemos incluido la anotacion @EnableCaching en nuestra clase Application.</p>
<h3 id="dependencias">Dependencias</h3>
<p>Ha sido necesario incluir las siguientes dependencias en nuestro pom.xml para poder utilizar redis.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><dependency></span>
<span class="nt"><groupId></span>org.springframework.boot<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>spring-boot-starter-data-redis<span class="nt"></artifactId></span>
<span class="nt"></dependency></span>
</code></pre></div></div>
<h2 id="docker-compose">Docker-compose</h2>
<p>Vamos a desplegar varios contenedores para esta prueba, por un lado desplegaremos un contenedor en el que irá nuestro redis exponiendo un puerto y conectado a una red que comunique con otros dos contenedores.</p>
<p>Estos dos contenedores serán dos despliegues de nuestro microservicio</p>
<p>A continuación indico cómo ha quedado el <code class="language-plaintext highlighter-rouge">docker-compose:</code></p>
<div class="language-jsx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">version</span><span class="p">:</span> <span class="dl">'</span><span class="s1">3</span><span class="dl">'</span>
<span class="nx">services</span><span class="p">:</span>
<span class="nx">redis</span><span class="p">:</span>
<span class="nx">image</span><span class="p">:</span> <span class="nx">library</span><span class="o">/</span><span class="nx">redis</span><span class="p">:</span><span class="mf">6.0</span><span class="p">.</span><span class="mi">10</span><span class="o">-</span><span class="nx">alpine</span>
<span class="nx">hostname</span><span class="p">:</span> <span class="nx">redis</span>
<span class="nx">ports</span><span class="p">:</span>
<span class="o">-</span> <span class="mi">6369</span><span class="p">:</span><span class="mi">6369</span>
<span class="nx">networks</span><span class="p">:</span>
<span class="o">-</span> <span class="nx">my_network</span>
<span class="nx">demo</span><span class="o">-</span><span class="nx">service</span><span class="o">-</span><span class="mi">1</span><span class="p">:</span>
<span class="nx">build</span><span class="p">:</span>
<span class="nx">context</span><span class="p">:</span> <span class="p">.</span><span class="o">/</span>
<span class="nx">dockerfile</span><span class="p">:</span> <span class="nx">Dockerfile</span>
<span class="nx">image</span><span class="p">:</span> <span class="nx">app</span>
<span class="nx">hostname</span><span class="p">:</span> <span class="nx">app</span>
<span class="nx">ports</span><span class="p">:</span>
<span class="o">-</span> <span class="dl">"</span><span class="s2">8080:8080</span><span class="dl">"</span>
<span class="nx">depends_on</span><span class="p">:</span>
<span class="o">-</span> <span class="nx">redis</span>
<span class="nx">networks</span><span class="p">:</span>
<span class="o">-</span> <span class="nx">my_network</span>
<span class="nx">demo</span><span class="o">-</span><span class="nx">service</span><span class="o">-</span><span class="mi">2</span><span class="p">:</span>
<span class="nx">build</span><span class="p">:</span>
<span class="nx">context</span><span class="p">:</span> <span class="p">.</span><span class="o">/</span>
<span class="nx">dockerfile</span><span class="p">:</span> <span class="nx">Dockerfile</span>
<span class="nx">image</span><span class="p">:</span> <span class="nx">app</span>
<span class="nx">hostname</span><span class="p">:</span> <span class="nx">app</span>
<span class="nx">ports</span><span class="p">:</span>
<span class="o">-</span> <span class="dl">"</span><span class="s2">8081:8080</span><span class="dl">"</span>
<span class="nx">depends_on</span><span class="p">:</span>
<span class="o">-</span> <span class="nx">redis</span>
<span class="nx">networks</span><span class="p">:</span>
<span class="o">-</span> <span class="nx">my_network</span>
<span class="nx">networks</span><span class="p">:</span>
<span class="nx">my_network</span><span class="p">:</span>
<span class="nx">driver</span><span class="p">:</span> <span class="nx">bridge</span>
</code></pre></div></div>
<h2 id="la-prueba">La prueba</h2>
<p>Para probar nuestra aplicacion haremos <code class="language-plaintext highlighter-rouge">docker-compose up</code> y una vez esten todos los contendores arrancados realizaremos la siguiente prueba y observaremos los resultados de esta:</p>
<ol>
<li>Si realizamos <code class="language-plaintext highlighter-rouge">curl localhost:8080/example/1</code> tras una espera de un poco más de 5 segundos nos devuelve <code class="language-plaintext highlighter-rouge">200 OK</code>.</li>
<li>Si volvemos a realizar la llamada <code class="language-plaintext highlighter-rouge">curl http://localhost:8080/example/1</code> observamos que la respuesta es inmediata, ya que está tirando de la caché.</li>
<li>Si en lugar de 1 llamamos a <code class="language-plaintext highlighter-rouge">curl http://localhost:8080/example/2</code> volvemos a observar esos 5 segundos de espera.</li>
<li>Y si ahora en lugar de llamar al contenedor que tenemos desplegado en el puerto 8080 llamamos al <code class="language-plaintext highlighter-rouge">curl http://localhost:8080/example/2</code> observamos para nuestro asombra que la respuesta es también inmediato. Y es que al estar distribuida cualquier contenedor configurado para apuntar a nuestro redis hará uso de esta caché.</li>
</ol>Diego Rubio AbujasModelo ACID & Teorema CAP2021-02-16T18:02:00+00:002021-02-16T18:02:00+00:00https://pandemoniodigital.es/documentaci%C3%B3n/2021/02/16/acid-cap-theorem<p><img src="https://pandemoniodigital.es/assets/images/acid-and-cap-theorem/databases.png" alt="" /></p>
<h1 id="introduccióm">Introduccióm</h1>
<p>En este artículo quiero hablar del modelo ACID y el teorema CAP. La motivación de este artículo es dar un repaso a ambos conceptos, y mostrar su relación con las bases de datos de tipo noSql. ¡Vamos allá!</p>
<h1 id="acid">ACID</h1>
<p>Partamos de qué es ACID, bien según nuestra amiga Wikipedia:</p>
<blockquote>
<p>En bases de datos se denomina <strong>ACID</strong> a las características de los parámetros que permiten clasificar las transacciones de los sistemas de gestión de bases de datos. Cuando se dice que una acción es ACID compliant se indica -en diversos grados- que ésta permite realizar transacciones.
En concreto, ACID es un acrónimo en inglés de Atomicity, Consistency, Isolation and Durability: Atomicidad, Consistencia, Aislamiento y Durabilidad, en español.</p>
</blockquote>
<p>Y bueno definamos un poco también que una <strong>transacción</strong> es un conjunto de… bueno que lo explique nuestra amiga Wikipedia también:</p>
<blockquote>
<p>Una <strong>transacción</strong> es una interacción con una estructura de datos compleja, compuesta por varios procesos que se han de aplicar uno después del otro. La transacción debe realizarse de una sola vez y sin que la estructura a medio manipular pueda ser alcanzada por el resto del sistema hasta que se hayan finalizado todos sus procesos.</p>
</blockquote>
<p>Las propiedades ACID fueron definidas en California en los años 70.</p>
<h2 id="atomicity-ó-atomicidad">Atomicity ó Atomicidad</h2>
<p>Cuando una operación o transacción es atómica, tenemos la garantía de que se va a realizar al completo o no se va a realizar, pero no va a quedar incompleta. O “todo o nada”.</p>
<h2 id="consistency-ó-consistencia">Consistency ó Consistencia</h2>
<p>Esta propiedad hace referencia a la Integridad de nuestra base de datos, una vez se realice la transacción la base de datos quedará en un estado consistente o integro, no habrá reglas rotas como relaciones foránes que no se cumplan, claves primarias repetidas, nulos donde no debería haber nulos, etc. Es decir, la transacción cumplirá las reglas establecida por nuestra base de datos.</p>
<h2 id="isolation-ó-aislamiento">Isolation ó Aislamiento</h2>
<p>Una operación será aislada de otras, es decir una operación o transacción no va a afectar a otras transacciones. Las transacciones sobre una misma información deben de ser independientes.</p>
<h2 id="durability-ó-durabilidad">Durability ó Durabilidad</h2>
<p>Una vez haya terminado la transacción, los cambios perdurarán en el tiempo.</p>
<h1 id="no-sql">No Sql</h1>
<p>Las bases de datos No Sqs abarcan un amplio número de sistemas gestores de bases de datos pensados para trabajar de manera distribuida; y entre los que podemos enumerar Mongo, Cassandra, Elastic Search… etc.</p>
<p>Estas bases de datos, por lo que poco que sé y por lo que he visto tienen varias características de entre las cuáles quiero enumerar las siguientes.</p>
<ol>
<li>Escalan muy bien de manera horizontal, no tengo que meter mas hardware a mi base de datos, sino que ampliar el cluster con nuevas máquinas, contenedores, etc.</li>
<li>Debido a este ecosistema de muchas máquinas, se crea un sistema de replicas y particiones, todo ello hace que los JOIN y las relaciones entre colecciones, tablas se gestionen de una forma bastante poco eficiente, enrevesada y en algunos casos no se pueda gestionar.</li>
<li>Pueden soportar mayor carga de peticiones y escalar mucho mejor que las bases de datos relacionales.</li>
<li><strong>NO garantizan completamente ACID.</strong></li>
</ol>
<h1 id="teorema-cap">Teorema CAP</h1>
<p>Y finalmente vamos a hablar de este teorema, que finalmente nos aclara la situación de las bases de datos no sql. Este teorema es una conjetura de Eric Brewer en 2000, que posteriormente fue probado por Seth Gilbert y Nancy Lynch en 2002. Dicho Teorema nos plantea los siguiente:</p>
<p><img src="https://pandemoniodigital.es/assets/images/acid-and-cap-theorem/nosql-triangle.png" alt="" /></p>
<p>Existen tres propiedades:</p>
<ol>
<li><strong>Consistencia</strong>: una lectura va a devolver el resultado mas reciente.</li>
<li><strong>Disponibilidad</strong>: una lectura petición va a recibir una respuesta correcta en un periodo de tiempo razonable, aunque puede no ser la mas reciente.</li>
<li><strong>Tolerancia a particionado:</strong> el sistema sigue funcionando a pesar de que algunos nodos de la red pasen a estar caídos o no disponibles.</li>
</ol>
<p>El teorema CAP nos dice que <strong>un sistema de base de datos distribuido no puede garantizar estos tres puntos</strong>, únicamente puede garantizarnos dos de ellos. En el gráfico de más arriba vemos diversas bases de datos, y cómo van agrupadas en dos de las propiedad de este teorema.</p>
<blockquote>
<p>El teorema CAP surgió de una suposición del informático Eric Brewer, que la mencionó durante su conferencia en el Simposio sobre Principios de Computación Distribuida (PODC, por sus siglas en inglés) en el año 2000. Por ello, esta propuesta sobre las limitaciones de las características de los sistemas distribuidos también se conoce como teorema de Brewer o Brewers theorem. En 2002, Seth Gilbert y Nancy Lynch, del MIT, demostraron su validez con evidencia axiomática, estableciéndola como teorema</p>
</blockquote>
<p>No debemos confundirnos, que una base de datos no garantice una de las tres propiedades no quiere decir que abandone la garantía en todo momento, es solo que en casos excepcionales no puede garantizar el cumplimiento de esa propiedad.</p>Diego Rubio Abujas