<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Posts on juanfbermejo.dev</title><link>/posts/</link><description>Recent content in Posts on juanfbermejo.dev</description><image><title>juanfbermejo.dev</title><url>/favicon-32x32.png</url><link>/favicon-32x32.png</link></image><generator>Hugo -- 0.152.2</generator><language>es</language><lastBuildDate>Sat, 04 Apr 2026 17:24:05 +0200</lastBuildDate><atom:link href="/posts/index.xml" rel="self" type="application/rss+xml"/><item><title>Sustituyendo Map por un DTO en nuestra API</title><link>/posts/sustituyendo-map-dto-api/</link><pubDate>Sat, 04 Apr 2026 17:24:05 +0200</pubDate><guid>/posts/sustituyendo-map-dto-api/</guid><description>&lt;p&gt;Si nuestros endpoints devuelven &lt;code&gt;String&lt;/code&gt; o &lt;code&gt;Map&lt;/code&gt;, ya contamos con una API plenamente funcional, pero no es la mejor solución. El problema es que &lt;strong&gt;la estructura de la respuesta está demasiado abierta&lt;/strong&gt;, lo que la hace más &lt;strong&gt;frágil y difícil de mantener&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Para especificar de forma clara el contrato de la API, es conveniente introducir en este punto los DTO (&lt;em&gt;Data Transfer Objects&lt;/em&gt;).&lt;/p&gt;
&lt;h1 id="el-problema-de-devolver-un-map"&gt;El problema de devolver un Map&lt;/h1&gt;
&lt;p&gt;Hasta ahora, nuestra API devuelve datos, pero sin estar representados de forma explícita y mantenible. Spring serializa el &lt;code&gt;Map&lt;/code&gt; sin problema a JSON, pero presenta una serie de inconvenientes que lo hacen poco adecuado para una API que empieza a evolucionar:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Si nuestros endpoints devuelven <code>String</code> o <code>Map</code>, ya contamos con una API plenamente funcional, pero no es la mejor solución. El problema es que <strong>la estructura de la respuesta está demasiado abierta</strong>, lo que la hace más <strong>frágil y difícil de mantener</strong>.</p>
<p>Para especificar de forma clara el contrato de la API, es conveniente introducir en este punto los DTO (<em>Data Transfer Objects</em>).</p>
<h1 id="el-problema-de-devolver-un-map">El problema de devolver un Map</h1>
<p>Hasta ahora, nuestra API devuelve datos, pero sin estar representados de forma explícita y mantenible. Spring serializa el <code>Map</code> sin problema a JSON, pero presenta una serie de inconvenientes que lo hacen poco adecuado para una API que empieza a evolucionar:</p>
<ol>
<li><strong>No está tipado</strong>: No existe una estructura definida que indique qué campos debe contener la respuesta.</li>
<li><strong>Sin documentación clara</strong>: la forma del JSON no queda reflejada en ningún tipo o clase, dificultando entender qué devuelve realmente el endpoint.</li>
<li><strong>Fácil equivocarse en claves</strong>: las claves son cadenas de texto, por lo que un error tipográfico puede pasar desapercibido hasta tiempo de ejecución.</li>
<li><strong>Un cambio de nombre rompe contratos con clientes fácilmente</strong>: modificar una clave puede afectar a clientes que dependen de esa estructura sin que el compilador detecte el problema.</li>
<li><strong>Perdemos claridad y el apoyo del compilador durante el desarrollo</strong>: no contamos con autocompletado ni comprobaciones de tipos al trabajar con los datos.</li>
<li><strong>No es escalable</strong>: a medida que la respuesta crece, el <code>Map</code> se vuelve más difícil de leer, mantener y evolucionar.</li>
</ol>
<p>El problema no es que falle, <strong>es que la estructura de la respuesta queda implícita, dispersa y demasiado abierta</strong>.</p>
<h1 id="qué-es-un-dto">Qué es un DTO</h1>
<p>Un DTO (<em>Data Transfer Object</em>) <strong>es un objeto cuyo objetivo es transportar datos entre capas o entre sistemas</strong>. En una API, uno de sus usos más habituales es representar de forma explícita los datos que enviamos o recibimos en los diferentes endpoints.</p>
<p>Son objetos simples cuya función es <strong>representar datos con una estructura clara y fija</strong>, sin contener ninguna lógica de negocio. De esta forma, la estructura de la respuesta queda definida de manera explícita y controlada.</p>
<h1 id="primer-dto-en-nuestra-api">Primer DTO en nuestra API</h1>
<p>La respuesta que devolvemos actualmente ya está serializada en un JSON con el siguiente contenido:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-JSON" data-lang="JSON"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;message&#34;</span><span class="p">:</span> <span class="s2">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;name&#34;</span><span class="p">:</span> <span class="s2">&#34;Juan&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="s2">&#34;2026-04-04T11:30:38.578535500Z&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>Spring la está formando a partir de un Map en el endpoint:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;timestamp&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Un primer DTO para la respuesta de nuestro endpoint podría ser el siguiente, incluyendo un atributo por cada campo que queremos devolver:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloResponseDto</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">HelloResponseDto</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">message</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">timestamp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getMessage</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">message</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getName</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getTimestamp</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">timestamp</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Quedando el endpoint de la siguiente manera, sin alterar la respuesta de la petición:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">HelloResponseDto</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">HelloResponseDto</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">HelloResponseDto</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Ahora la estructura de la respuesta queda definida de forma explícita en un tipo Java, facilitando su comprensión y evolución.</p>
<h1 id="usando-record-de-java">Usando <code>record</code> de Java</h1>
<p>Como el objeto <code>HelloResponseDto</code> solo transporta datos, Java ofrece una forma más compacta de representarlo: <code>record</code>. Un <code>record</code> <strong>define datos inmutables, reduce el código repetitivo y encaja muy bien en objetos simples de transporte</strong>.</p>
<p>Podríamos redefinir la clase <code>HelloResponseDto</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">record</span> <span class="nc">HelloResponseDto</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">message</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">timestamp</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  </span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>El endpoint no necesita modificarse, ya que el <code>record</code> implementa automáticamente el constructor y los métodos de acceso.</p>
<h1 id="ventajas-frente-a-map">Ventajas frente a Map</h1>
<ol>
<li>Un <code>Map</code> describe datos de forma improvisada; un DTO describe datos de forma intencional.</li>
<li>Más claridad: el nombre <code>HelloResponseDto</code> expresa qué estamos devolviendo.</li>
<li>Estructura explícita: los campos están definidos en un tipo.</li>
<li>Menos errores involuntarios en el desarrollo: no dependemos de escribir claves String a mano</li>
<li>Mejor mantenimiento: el cambio está centralizado.</li>
<li>Mejor evolución: más fácil añadir nuevos campos sin desordenar el controlador</li>
</ol>
<h1 id="evolución-de-nuestra-api">Evolución de nuestra API</h1>
<p>En el endpoint <code>/hello</code> comenzamos devolviendo un <code>String</code>, luego mejoramos la respuesta con <code>ResponseEntity</code>. Después pasamos a devolver un JSON. Y ahora damos un paso más definiendo explícitamente su estructura mediante DTOs. De esta forma, el contrato con los clientes se vuelve más robusto, mantenible y fiable, sentando además una base sólida para seguir evolucionando la API.</p>
]]></content:encoded></item><item><title>Mejorando la respuesta de nuestra API</title><link>/posts/mejorando-respuesta-api/</link><pubDate>Mon, 30 Mar 2026 17:11:14 +0200</pubDate><guid>/posts/mejorando-respuesta-api/</guid><description>&lt;p&gt;Hasta ahora nuestro código está devolviendo un &lt;code&gt;String&lt;/code&gt; de forma directa que &lt;strong&gt;Spring Boot convierte automáticamente en una respuesta HTTP completa&lt;/strong&gt;. Ésta, por defecto, incluye un código de estado (&lt;code&gt;200 OK&lt;/code&gt;) y un cuerpo con el mensaje.&lt;/p&gt;
&lt;p&gt;El siguiente paso para mejorar el diseño de nuestra API es &lt;strong&gt;controlar completamente la respuesta y adaptarla a nuestra lógica&lt;/strong&gt;, para lo que contamos con la clase &lt;code&gt;ResponseEntity&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="qué-es-una-respuesta-http"&gt;Qué es una respuesta HTTP&lt;/h1&gt;
&lt;p&gt;Una &lt;strong&gt;respuesta HTTP&lt;/strong&gt; es el mensaje que devuelve el servidor cuando un cliente realiza una petición a una API. Esta respuesta incluye principalmente &lt;strong&gt;un código de estado&lt;/strong&gt;, que indica si la operación ha sido correcta o ha ocurrido algún problema, y &lt;strong&gt;un cuerpo con los datos&lt;/strong&gt; que queremos devolver.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hasta ahora nuestro código está devolviendo un <code>String</code> de forma directa que <strong>Spring Boot convierte automáticamente en una respuesta HTTP completa</strong>. Ésta, por defecto, incluye un código de estado (<code>200 OK</code>) y un cuerpo con el mensaje.</p>
<p>El siguiente paso para mejorar el diseño de nuestra API es <strong>controlar completamente la respuesta y adaptarla a nuestra lógica</strong>, para lo que contamos con la clase <code>ResponseEntity</code>.</p>
<h1 id="qué-es-una-respuesta-http">Qué es una respuesta HTTP</h1>
<p>Una <strong>respuesta HTTP</strong> es el mensaje que devuelve el servidor cuando un cliente realiza una petición a una API. Esta respuesta incluye principalmente <strong>un código de estado</strong>, que indica si la operación ha sido correcta o ha ocurrido algún problema, y <strong>un cuerpo con los datos</strong> que queremos devolver.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">200 OK
</span></span><span class="line"><span class="cl">Content-Type: application/json
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">{ &#34;Hello World from Spring Boot!&#34; }</span></span></code></pre></div>
<p>En nuestro caso:</p>
<ul>
<li><strong>200 OK</strong>: la petición se ha procesado correctamente.</li>
<li><strong>Content-Type</strong>: indica el tipo de contenido, JSON en este caso.</li>
<li><strong>Hello World from Spring Boot!</strong>: cuerpo de la respuesta, un <code>String</code> que Spring Boot serializa automáticamente.</li>
</ul>
<p>Este concepto es importante porque, a medida que nuestra API evolucione, no solo devolveremos texto, sino también <strong>distintos códigos HTTP y estructuras más completas</strong>, y ahí es donde entra <code>ResponseEntity</code>.</p>
<h1 id="controlando-la-respuesta-con-responseentity">Controlando la respuesta con ResponseEntity</h1>
<p><code>ResponseEntity</code> es una clase de Spring que permite <strong>controlar completamente la respuesta HTTP</strong> que devuelve nuestra API. En lugar de devolver solo el contenido, podemos definir también <strong>el código de estado</strong>, las <strong>cabeceras</strong> y el <strong>cuerpo de la respuesta</strong>.</p>
<p>Hasta ahora, nuestro controlador devuelve un <code>String</code> directamente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Podemos usar <code>ResponseEntity</code> para indicar explícitamente el código HTTP que debe incluir:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">String</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En este caso seguimos devolviendo el mismo contenido, pero ahora <strong>estamos construyendo explícitamente una respuesta HTTP</strong>, algo fundamental cuando queramos devolver errores (códigos <code>400</code> o <code>404</code>), recursos creados ( código <code>201</code>) o respuestas más complejas.</p>
<h1 id="completando-la-respuesta-con-un-json">Completando la respuesta con un JSON</h1>
<p>Otra mejora para conseguir una respuesta más adecuada para una API es <strong>devolver el contenido como un objeto JSON bien estructurado</strong>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En el caso de un <code>String</code> simple puede parecer de poco interés, pero lo habitual será que <strong>la respuesta incluya mucha más información</strong>, de forma que <strong>un JSON permita estructurar mejor los datos</strong>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="o">&gt;</span><span class="w"> </span><span class="n">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Map</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;message&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="s">&#34;timestamp&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">Instant</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">toString</span><span class="p">()</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">content</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si levantamos el servidor y, como venimos haciendo, realizamos una petición desde el navegador, obtendremos la respuesta con el JSON completo.</p>
<p><img alt="Pantalla del navegador con la respuesta JSON de la API" loading="lazy" src="/posts/mejorando-respuesta-api/images/json_response.png"></p>
<h1 id="validando-la-api-con-los-test">Validando la API con los test</h1>
<p>Una vez más, <strong>debemos usar los test para validar los cambios realizados</strong>. Hasta ahora, el contrato de nuestra API incluía una respuesta HTTP formada automáticamente con un único <code>String</code> en el cuerpo. Pero ahora, <strong>la respuesta es mucho más completa, con otro formato y con el mensaje dentro de un JSON</strong>.</p>
<p>Si ejecutamos los test existentes, éstos fallarán, <strong>indicando que hemos roto ese contrato</strong>.</p>
<h2 id="cambio-de-contrato-con-los-clientes">Cambio de contrato con los clientes</h2>
<p>Si nuestra API se encontrase en producción, esto nos indicaría que tendríamos un problema que podríamos afrontar con dos planteamientos:</p>
<ul>
<li>Obligar a todos nuestros clientes a actualizar sus desarrollos para adaptarse al nuevo formato de respuesta.</li>
<li>Versionar nuestra API, manteniendo el endpoint actual sin modificar y publicar el nuevo en una nueva ruta, permitiendo a los clientes migrar de forma progresiva.</li>
</ul>
<h2 id="evolucionando-los-tests">Evolucionando los tests</h2>
<p>Para validar el nuevo desarrollo, adaptamos los test a la nueva respuesta del endpoint.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithoutParamReturnsDefaultValue</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.message&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.name&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.timestamp&#34;</span><span class="p">).</span><span class="na">isString</span><span class="p">()</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithParamReturnsCustomName</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="na">param</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">())</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.message&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.name&#34;</span><span class="p">).</span><span class="na">value</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">jsonPath</span><span class="p">(</span><span class="s">&#34;$.timestamp&#34;</span><span class="p">).</span><span class="na">isString</span><span class="p">());</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><code>jsonPath(&quot;$.message&quot;)</code> indica a <code>MockMvc</code> que acceda al campo <code>message</code> del JSON devuelto:</p>
<ul>
<li><code>$</code>: representa la raíz del JSON</li>
<li><code>$.message</code>: accede al campo <code>message</code></li>
<li><code>.value(...)</code>: comprueba que ese campo tiene el valor esperado</li>
</ul>
<p>Por lo tanto, permite <strong>validar un campo concreto dentro del JSON que devuelve la API</strong> y verifica que:</p>
<ul>
<li>La respuesta tiene formato JSON</li>
<li>Existe el campo <code>message</code></li>
<li>Su valor es exactamente el esperado en cada caso</li>
</ul>
<p>Este enfoque permite <strong>validar respuestas más complejas de forma precisa</strong>, comprobando cada campo individualmente en lugar de comparar todo el contenido como una cadena de texto, lo que hace los tests más robustos y fáciles de mantener.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Mejorando la respuesta de nuestra API, pasando de devolver texto simple a diseñar respuestas HTTP más completas, estamos evolucionándola de forma natural para conseguir un resultado más profesional. Además, con el trabajo continuo con los test, conseguimos validar el contrato que establecemos con nuestros clientes, de forma que podamos realizar esa evolución con seguridad.</p>
]]></content:encoded></item><item><title>Recibiendo datos en una API con @RequestParam</title><link>/posts/recibiendo-datos-request-param/</link><pubDate>Sat, 21 Mar 2026 10:21:14 +0100</pubDate><guid>/posts/recibiendo-datos-request-param/</guid><description>&lt;p&gt;Una API es &lt;strong&gt;la puerta de entrada a nuestra aplicación&lt;/strong&gt;: recibe peticiones desde el exterior,
extrae los datos que le envían, ejecuta la lógica necesaria y devuelve una respuesta. Hasta
ahora, en &lt;a href="/posts/spring-boot-hello-world/"&gt;nuestra API &lt;em&gt;HelloWorld&lt;/em&gt;&lt;/a&gt; solo recibimos
peticiones y damos una respuesta. El siguiente paso es &lt;strong&gt;permitir que un cliente nos pase
información a través de la URL&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="formas-en-las-que-una-api-puede-recibir-datos"&gt;Formas en las que una API puede recibir datos&lt;/h1&gt;
&lt;p&gt;El componente encargado de extraer los datos de la petición es el controlador. En una API HTTP, éstos pueden llegar al endpoint de varias maneras según su finalidad:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una API es <strong>la puerta de entrada a nuestra aplicación</strong>: recibe peticiones desde el exterior,
extrae los datos que le envían, ejecuta la lógica necesaria y devuelve una respuesta. Hasta
ahora, en <a href="/posts/spring-boot-hello-world/">nuestra API <em>HelloWorld</em></a> solo recibimos
peticiones y damos una respuesta. El siguiente paso es <strong>permitir que un cliente nos pase
información a través de la URL</strong>.</p>
<h1 id="formas-en-las-que-una-api-puede-recibir-datos">Formas en las que una API puede recibir datos</h1>
<p>El componente encargado de extraer los datos de la petición es el controlador. En una API HTTP, éstos pueden llegar al endpoint de varias maneras según su finalidad:</p>
<ol>
<li>En la URL como parámetros</li>
<li>En la URL como parte de la ruta</li>
<li>En el cuerpo de la petición</li>
<li>En las cabeceras de la petición</li>
<li>En cookies</li>
<li>Como formularios</li>
<li>Como ficheros</li>
</ol>
<p>Vamos a ver la primera y más sencilla de ellas: recibir datos como parámetros de la URL.</p>
<h1 id="primer-dato-de-entrada-en-nuestra-api">Primer dato de entrada en nuestra API</h1>
<p>Nuestra API de momento, está devolviendo un saludo genérico. A partir de aquí, <strong>empezamos a
construir APIs que reaccionan a lo que les envía el cliente</strong>.</p>
<pre tabindex="0"><code class="language-URL" data-lang="URL">http://localhost:8080/hello?name=Juan</code></pre>
<p>Para recibirlo en el controlador y poder utilizarlo, incorporamos un parámetro con el mismo
nombre al método del endpoint y lo anotamos con <code>@RequestParam</code>. De esta forma Spring Boot extrae
automáticamente el valor de la URL y lo pasa como argumento al método.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si desplegamos la aplicación y probamos desde el navegador, vemos el resultado:</p>
<p><img alt="Navegador web con la respuesta de la API al pasar parámetro en la URL" loading="lazy" src="/posts/recibiendo-datos-request-param/images/HelloJuan.png"></p>
<p>Pero, ¿y si no incluimos el parámetro <code>name</code> en la URL?</p>
<h2 id="parámetros-opcionales">Parámetros opcionales</h2>
<p>Con la configuración actual, la aplicación está esperando siempre recibir el parámetro <code>name</code> en la petición, devolviendo un error de no ser así.</p>
<p>Para evitarlo, <strong>podemos marcar los parámetros como opcionales</strong> con la propiedad <code>required</code> de la
siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">required</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">false</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>En este caso, si no se envía el parámetro, Spring asigna el valor <code>null</code>.</p>
<p><img alt="Navegador web con la respuesta de la API por defecto al no recibir parámetro en la URL" loading="lazy" src="/posts/recibiendo-datos-request-param/images/HelloWorld.png"></p>
<h2 id="valores-por-defecto">Valores por defecto</h2>
<p>La anotación cuenta con la propiedad <code>defaultValue</code> que nos permite simplificar la lógica en el
controlador, <strong>fijando un valor por defecto si no se informa el parámetro</strong>, en cuyo caso Spring asume automáticamente que se trata de un parámetro opcional. Cuando usamos <code>defaultValue</code>,
<strong>Spring considera automáticamente que el parámetro es opcional</strong>, por lo que no es necesario
indicar <code>required = false</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="s">&#34; from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="cuando-usar-parámetros-en-la-url">Cuando usar parámetros en la URL</h1>
<p>Los <code>@RequestParam</code> se utilizan cuando queremos recibir datos simples en la URL, normalmente para:</p>
<ul>
<li>filtros</li>
<li>búsquedas</li>
<li>valores opcionales</li>
</ul>
<h1 id="los-tests-como-red-de-seguridad-ante-cambios">Los tests como red de seguridad ante cambios</h1>
<p>Cada vez que cambiamos el comportamiento de un endpoint, <strong>los tests nos ayuda a comprobar si de
cara al cliente el cambio ha sido transparente</strong>. No solo validan el código, también documentan
cómo debe comportarse la API.</p>
<p>Si lanzamos <a href="/posts/primer-test-spring-boot/">los tests que ya habíamos implementado</a>, vemos
que siguen ejecutándose correctamente. Esto ocurre porque hemos mantenido el comportamiento por
defecto gracias al uso de <code>defaultValue</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloEndpointReturnsHelloWorld</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Consola donde se comprueba que los tests han finalizado correctamente" loading="lazy" src="/posts/recibiendo-datos-request-param/images/TestsSuccess.png"></p>
<h2 id="los-tests-evolucionan-con-el-código">Los tests evolucionan con el código</h2>
<p>Unos test útiles y completos deben <strong>cubrir los distintos escenarios posibles</strong>, y deben
<strong>ampliarse junto con el código de la API</strong>. Nuestro test cubre el comportamiento por defecto,
pero deberemos completarlo ahora para el caso de recibir un parámetro.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithParamReturnsCustomName</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">).</span><span class="na">param</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Juan&#34;</span><span class="p">))</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">())</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello Juan from Spring Boot!&#34;</span><span class="p">));</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="elementos-nuevos-que-usa-el-test">Elementos nuevos que usa el test</h3>
<h4 id="herramientas-para-simular-peticiones-http">Herramientas para simular peticiones HTTP</h4>
<ul>
<li><code>param()</code>: Spring Boot añade este parámetro a la URL como si el cliente lo hubiera enviado en la petición real.</li>
</ul>
<h3 id="nombres-que-reflejen-intención">Nombres que reflejen intención</h3>
<p>Adicionalmente, para reflejar mejor qué caso valida cada test, actualizamos también el nombre
del test previo:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">void</span><span class="w"> </span><span class="nf">helloWithoutParamReturnsDefaultValue</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Lanzamos de nuevo la ejecución de los tests y comprobamos que finalizan correctamente.</p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Las APIs son las puertas de entrada a nuestras aplicaciones por lo que será habitual recibir
datos en alguno de los formatos y por alguna de las vías que proporciona Spring Boot. Los
parámetros en la URL son una de las más sencillas y útiles. A partir de aquí, iremos incorporando
nuevas formas de recibir datos en nuestra API a medida que evolucionamos sus funcionalidades.</p>
<p>También, cada vez que cambiamos el comportamiento de nuestra API, los tests nos ayudan a validar
y revisar los cambios de forma que podamos detectar si estamos modificando algún caso de uso que no teníamos contemplado, pero para que sigan siendo útiles, deben completarse a medida que ampliamos nuestra API.</p>
]]></content:encoded></item><item><title>Arquitectura por capas en aplicaciones</title><link>/posts/arquitectura-capas-aplicaciones/</link><pubDate>Fri, 13 Mar 2026 20:13:11 +0100</pubDate><guid>/posts/arquitectura-capas-aplicaciones/</guid><description>&lt;p&gt;El &lt;a href="/posts/principio-responsabilidad-unica/"&gt;Principio de Responsabilidad Única&lt;/a&gt; nos dice que clases y funciones deben tener una
responsabilidad clara y específica para conseguir código de mayor calidad. Pero podemos ir un paso más allá aplicando este mismo criterio para &lt;strong&gt;organizar una aplicación completa&lt;/strong&gt;, agrupando componentes según su responsabilidad y definiendo cómo se comunican entre sí.&lt;/p&gt;
&lt;p&gt;Este enfoque permite estructurar el sistema de forma que sea &lt;strong&gt;más robusto, mantenible y fácil de evolucionar&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="en-qué-consiste"&gt;En qué consiste&lt;/h1&gt;
&lt;p&gt;La arquitectura por capas consiste en &lt;strong&gt;organizar una aplicación en módulos que comparten una responsabilidad clara&lt;/strong&gt;, estableciendo además una &lt;strong&gt;dirección definida en sus dependencias&lt;/strong&gt;. Cada capa se comunica únicamente con las capas adyacentes.&lt;/p&gt;</description><content:encoded><![CDATA[<p>El <a href="/posts/principio-responsabilidad-unica/">Principio de Responsabilidad Única</a> nos dice que clases y funciones deben tener una
responsabilidad clara y específica para conseguir código de mayor calidad. Pero podemos ir un paso más allá aplicando este mismo criterio para <strong>organizar una aplicación completa</strong>, agrupando componentes según su responsabilidad y definiendo cómo se comunican entre sí.</p>
<p>Este enfoque permite estructurar el sistema de forma que sea <strong>más robusto, mantenible y fácil de evolucionar</strong>.</p>
<h1 id="en-qué-consiste">En qué consiste</h1>
<p>La arquitectura por capas consiste en <strong>organizar una aplicación en módulos que comparten una responsabilidad clara</strong>, estableciendo además una <strong>dirección definida en sus dependencias</strong>. Cada capa se comunica únicamente con las capas adyacentes.</p>
<p>Los objetivos que persigue son:</p>
<ul>
<li>Reducir el acoplamiento.</li>
<li>Facilitar el mantenimiento.</li>
<li>Permitir evolución tecnológica sin afectar al núcleo.</li>
<li>Mejorar la testabilidad.</li>
<li>Aislar la lógica de negocio.</li>
</ul>
<h2 id="estructura-básica">Estructura básica</h2>
<p>Una división mínima podría representarse así:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[    Entrada     ] - Capa de entrada/transporte/salida (HTTP, API...)
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">[    Negocio     ] - Lógica de negocio
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">[  Persistencia  ] - Acceso a datos
</span></span><span class="line"><span class="cl">        ↓
</span></span><span class="line"><span class="cl">[  Base de datos ]</span></span></code></pre></div>
<ul>
<li>La <strong>capa de entrada</strong> recibe peticiones y devuelve respuestas.</li>
<li>La <strong>capa de negocio</strong> contiene la lógica que implementa las reglas de la aplicación.</li>
<li>La <strong>capa de persistencia</strong> se encarga del acceso a los datos.</li>
</ul>
<p>Cada capa tiene responsabilidades claras y solo conoce la capa inmediatamente inferior. De esta forma se evita que detalles de infraestructura o transporte se mezclen con la lógica de negocio.</p>
<p>Por ejemplo:</p>
<ul>
<li>Validar que una dirección de correo tiene un formato correcto pertenece a la <strong>capa de entrada</strong>, ya que depende de cómo llegan los datos (JSON, formulario, etc.).</li>
<li>Comprobar si ese correo pertenece a un cliente registrado es una <strong>regla de negocio</strong>, por lo que debe implementarse en la capa de negocio.</li>
</ul>
<p>De la misma forma, el cálculo del importe total de un pedido debería hacerse en la capa de negocio. Si esta lógica se implementara en la base de datos, cambiar de sistema gestor implicaría rehacer parte de la lógica de la aplicación.</p>
<h1 id="cómo-se-rompe-la-arquitectura-por-capas">Cómo se rompe la arquitectura por capas</h1>
<p>Estructurar una aplicación por capas no consiste únicamente en separar el código en carpetas o crear clases llamadas <code>Controller</code>, <code>Service</code> o <code>Repository</code>. Lo importante es <strong>respetar las responsabilidades y la dirección de las dependencias</strong>.</p>
<p>Un ejemplo de una API de gestión de pedidos donde aparentemente existen las tres capas puede ser el siguiente:</p>
<h4 id="controlador">Controlador</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@RestController</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@RequestMapping</span><span class="p">(</span><span class="s">&#34;/orders&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">OrderController</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">OrderService</span><span class="w"> </span><span class="n">orderService</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">CouponRepository</span><span class="w"> </span><span class="n">couponRepository</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@PostMapping</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">OrderResponseDto</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createOrder</span><span class="p">(</span><span class="nd">@RequestBody</span><span class="w"> </span><span class="n">OrderRequestDto</span><span class="w"> </span><span class="n">request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Coupon</span><span class="w"> </span><span class="n">coupon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">null</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="na">getCouponCode</span><span class="p">()</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="o">!</span><span class="n">request</span><span class="p">.</span><span class="na">getCouponCode</span><span class="p">().</span><span class="na">isBlank</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">coupon</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">couponRepository</span><span class="p">.</span><span class="na">findByCode</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="na">getCouponCode</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeException</span><span class="p">(</span><span class="s">&#34;Cupón no encontrado&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">orderService</span><span class="p">.</span><span class="na">createOrder</span><span class="p">(</span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">coupon</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">mapper</span><span class="p">.</span><span class="na">toResponse</span><span class="p">(</span><span class="n">order</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h4 id="servicio">Servicio</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span><span class="lnt">34
</span><span class="lnt">35
</span><span class="lnt">36
</span><span class="lnt">37
</span><span class="lnt">38
</span><span class="lnt">39
</span><span class="lnt">40
</span><span class="lnt">41
</span><span class="lnt">42
</span><span class="lnt">43
</span><span class="lnt">44
</span><span class="lnt">45
</span><span class="lnt">46
</span><span class="lnt">47
</span><span class="lnt">48
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Service</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">OrderService</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">UserRepository</span><span class="w"> </span><span class="n">userRepository</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">ProductRepository</span><span class="w"> </span><span class="n">productRepository</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">OrderRepository</span><span class="w"> </span><span class="n">orderRepository</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">Order</span><span class="w"> </span><span class="nf">createOrder</span><span class="p">(</span><span class="n">OrderRequestDto</span><span class="w"> </span><span class="n">request</span><span class="p">,</span><span class="w"> </span><span class="n">Coupon</span><span class="w"> </span><span class="n">coupon</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">User</span><span class="w"> </span><span class="n">user</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">userRepository</span><span class="p">.</span><span class="na">findById</span><span class="p">(</span><span class="n">request</span><span class="p">.</span><span class="na">getUserId</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeException</span><span class="p">(</span><span class="s">&#34;Usuario no encontrado&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">totalBeforeDiscount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">OrderItemRequestDto</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">request</span><span class="p">.</span><span class="na">getItems</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">Product</span><span class="w"> </span><span class="n">product</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">productRepository</span><span class="p">.</span><span class="na">findById</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="na">getProductId</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">.</span><span class="na">orElseThrow</span><span class="p">(()</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">RuntimeException</span><span class="p">(</span><span class="s">&#34;Producto no encontrado&#34;</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">lineTotal</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">product</span><span class="p">.</span><span class="na">getPrice</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">.</span><span class="na">multiply</span><span class="p">(</span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="na">getQuantity</span><span class="p">()));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">totalBeforeDiscount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">totalBeforeDiscount</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">lineTotal</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">discountApplied</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">coupon</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">&amp;&amp;</span><span class="w"> </span><span class="n">coupon</span><span class="p">.</span><span class="na">isActive</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">discountApplied</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">totalBeforeDiscount</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">.</span><span class="na">multiply</span><span class="p">(</span><span class="n">coupon</span><span class="p">.</span><span class="na">getPercentage</span><span class="p">())</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                    </span><span class="p">.</span><span class="na">divide</span><span class="p">(</span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">100</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Order</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">order</span><span class="p">.</span><span class="na">setUser</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">order</span><span class="p">.</span><span class="na">setCreatedAt</span><span class="p">(</span><span class="n">LocalDateTime</span><span class="p">.</span><span class="na">now</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">order</span><span class="p">.</span><span class="na">setTotalBeforeDiscount</span><span class="p">(</span><span class="n">totalBeforeDiscount</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">order</span><span class="p">.</span><span class="na">setDiscountApplied</span><span class="p">(</span><span class="n">discountApplied</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">order</span><span class="p">.</span><span class="na">setTotalFinal</span><span class="p">(</span><span class="n">totalBeforeDiscount</span><span class="p">.</span><span class="na">subtract</span><span class="p">(</span><span class="n">discountApplied</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="n">orderRepository</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h4 id="repositorio">Repositorio</h4>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@Repository</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kd">interface</span> <span class="nc">CouponRepository</span><span class="w"> </span><span class="kd">extends</span><span class="w"> </span><span class="n">JpaRepository</span><span class="o">&lt;</span><span class="n">Coupon</span><span class="p">,</span><span class="w"> </span><span class="n">Long</span><span class="o">&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Optional</span><span class="o">&lt;</span><span class="n">Coupon</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">findByCode</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">code</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>A primera vista parece una aplicación bien estructurada: existen controladores, servicios y repositorios. Sin embargo, la arquitectura por capas no se está respetando.</p>
<h2 id="qué-está-mal-aquí">Qué está mal aquí</h2>
<h3 id="1-el-controlador-accede-directamente-al-repositorio">1. El controlador accede directamente al repositorio</h3>
<p>El <code>Controller</code> consulta <code>CouponRepository</code> por su cuenta. Esto rompe la dirección natural de comunicación: Controller → Service → Repository; y hace que la capa de entrada dependa directamente de la persistencia.</p>
<h3 id="2-la-lógica-de-negocio-queda-repartida">2. La lógica de negocio queda repartida</h3>
<p>El controlador decide si hay que buscar el cupón y pasa el resultado al servicio. Parte del caso de uso empieza en el controlador y continúa en el servicio.</p>
<h3 id="3-el-servicio-no-controla-completamente-el-caso-de-uso">3. El servicio no controla completamente el caso de uso</h3>
<p>El método <code>createOrder</code> depende de que el controlador haya resuelto previamente cierta información, por lo que el servicio deja de representar el flujo completo de creación de pedidos.</p>
<h3 id="4-el-servicio-recibe-estructuras-de-transporte">4. El servicio recibe estructuras de transporte</h3>
<p>El servicio recibe <code>OrderRequestDto</code>, que pertenece a la capa de entrada. Esto mezcla el modelo de transporte con la lógica de negocio.</p>
<h2 id="problemas-que-genera">Problemas que genera</h2>
<p>Este tipo de diseño puede funcionar, pero genera varios problemas:</p>
<ul>
<li><strong>Reglas repartidas:</strong> cambios en la lógica obligan a modificar varias capas.</li>
<li><strong>Menor reutilización:</strong> otros puntos de entrada (batch, eventos, etc.) necesitarían replicar parte de la lógica.</li>
<li><strong>Servicios menos autónomos:</strong> el servicio ya no representa el caso de uso completo.</li>
<li><strong>Controladores demasiado complejos:</strong> empiezan a conocer detalles de persistencia y reglas de negocio.</li>
</ul>
<h1 id="versión-correcta">Versión correcta</h1>
<p>En una arquitectura por capas el controlador debería limitarse a recibir la petición y delegar el caso de uso en la capa de negocio.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="nd">@PostMapping</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="o">&lt;</span><span class="n">OrderResponseDto</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">createOrder</span><span class="p">(</span><span class="nd">@RequestBody</span><span class="w"> </span><span class="n">OrderRequestDto</span><span class="w"> </span><span class="n">request</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">CreateOrderDTO</span><span class="w"> </span><span class="n">orderDto</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mapper</span><span class="p">.</span><span class="na">toCreateOrderDto</span><span class="p">(</span><span class="n">request</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">orderService</span><span class="p">.</span><span class="na">createOrder</span><span class="p">(</span><span class="n">orderDto</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ResponseEntity</span><span class="p">.</span><span class="na">ok</span><span class="p">(</span><span class="n">mapper</span><span class="p">.</span><span class="na">toResponse</span><span class="p">(</span><span class="n">order</span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>El servicio se encarga entonces de todo el flujo del caso de uso:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">Order</span><span class="w"> </span><span class="nf">createOrder</span><span class="p">(</span><span class="n">CreateOrderDTO</span><span class="w"> </span><span class="n">orderDto</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">Discount</span><span class="w"> </span><span class="n">discount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">discountService</span><span class="p">.</span><span class="na">calculateDiscount</span><span class="p">(</span><span class="n">orderDto</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// buscar usuario</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// buscar productos</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// validar cupón</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// calcular totales</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// guardar pedido</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>De esta forma:</p>
<ul>
<li>el <strong>Controller</strong> gestiona el transporte (HTTP, DTOs, respuesta),</li>
<li>el <strong>Service</strong> implementa la lógica de negocio,</li>
<li>los <strong>Repositories</strong> se encargan únicamente del acceso a datos.</li>
</ul>
<h2 id="conclusiones">Conclusiones</h2>
<p>Una arquitectura por capas no se rompe solo cuando una clase hace demasiado, sino también cuando las responsabilidades parecen separadas pero el flujo real del caso de uso queda repartido entre capas que no deberían conocer esos detalles.</p>
<p>Tener clases llamadas <code>Controller</code>, <code>Service</code> y <code>Repository</code> no garantiza una arquitectura por capas. Lo que realmente la define es <strong>respetar las responsabilidades de cada capa y mantener una dirección clara en las dependencias</strong>.</p>
]]></content:encoded></item><item><title>Compilar con Maven dentro de Docker - multi-stage build</title><link>/posts/compilar-app-maven-docker/</link><pubDate>Sun, 01 Mar 2026 08:06:08 +0100</pubDate><guid>/posts/compilar-app-maven-docker/</guid><description>&lt;p&gt;Una vez que hemos desacoplado la ejecución de una aplicación de nuestro equipo y su entorno desplegándola en un contenedor, el siguiente paso es &lt;strong&gt;independizar también el proceso de compilación y construcción&lt;/strong&gt;. El objetivo es evitar también problemas de configuraciones y dependencias relacionadas con el entorno de desarrollo, &lt;strong&gt;moviendo la responsabilidad a un proceso automatizado&lt;/strong&gt;. De esta forma nos acercarnos más a un entorno de producción.&lt;/p&gt;
&lt;h1 id="enfoque-de-partida"&gt;Enfoque de partida&lt;/h1&gt;
&lt;p&gt;El Dockerfile con el que hemos trabajado previamente incluye lo mínimo para desplegar el JAR:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una vez que hemos desacoplado la ejecución de una aplicación de nuestro equipo y su entorno desplegándola en un contenedor, el siguiente paso es <strong>independizar también el proceso de compilación y construcción</strong>. El objetivo es evitar también problemas de configuraciones y dependencias relacionadas con el entorno de desarrollo, <strong>moviendo la responsabilidad a un proceso automatizado</strong>. De esta forma nos acercarnos más a un entorno de producción.</p>
<h1 id="enfoque-de-partida">Enfoque de partida</h1>
<p>El Dockerfile con el que hemos trabajado previamente incluye lo mínimo para desplegar el JAR:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:17-jre-alpine</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> target/app.jar app.jar<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Los problemas que presenta son:</p>
<ul>
<li>Necesitamos compilar la aplicación fuera del contenedor.</li>
<li>Dependencias de Maven instaladas en el entorno de desarrollo.</li>
<li>No es reproducible al 100%.</li>
<li>El proceso de compilación y despliegue depende de varios pasos manuales.</li>
</ul>
<p>Para completar el proceso, podríamos incorporar en el mismo contenedor lo necesario para realizar la compilación, pero <strong>todas las herramientas necesarias para ello convivirían con la aplicación desplegada en el contenedor de producción</strong>.</p>
<h1 id="qué-es-un-multi-stage-build">Qué es un multi-stage build</h1>
<p>Un multi-stage build es una técnica de Docker que consiste en <strong>usar un contenedor para construir la aplicación y otro distinto, mucho más pequeño, solo para ejecutarla</strong>. Así Docker, compila dentro de un contenedor pesado con las dependencias necesarias para ese proceso, descarta todo lo innecesario y se queda con la aplicación final compilada para desplegarla en otro contenedor mucho más ligero.</p>
<h2 id="esquema-del-proceso">Esquema del proceso</h2>
<ol>
<li>Stage 1 (builder)
<ul>
<li>Tiene Maven</li>
<li>Compila el proyecto</li>
<li>Genera el JAR</li>
</ul>
</li>
<li>Stage 2 (runtime)
<ul>
<li>Solo tiene Java</li>
<li>Copia el JAR</li>
<li>Ejecuta la app</li>
</ul>
</li>
</ol>
<p>De esta forma, <strong>el contenedor final no contiene Maven, ni código fuente, ni caché</strong>.</p>
<h1 id="nuevo-dockerfile-multi-stage">Nuevo Dockerfile multi-stage</h1>
<p>El nuevo Dockerfile completo con las dos etapas quedaría así:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Dockerfile" data-lang="Dockerfile"><span class="line"><span class="cl"><span class="c"># ---------- STAGE 1: build ----------</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="w"> </span><span class="s">maven:3.9.9-eclipse-temurin-21-alpine</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">build</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> pom.xml .<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> mvn dependency:go-offline<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> src ./src<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> mvn clean package -DskipTests<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ---------- STAGE 2: runtime ----------</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:21-jre-alpine-3.23</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> --from<span class="o">=</span>build /app/target/*-SNAPSHOT.jar app.jar<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8080</span> <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="stage-1---build-con-maven">Stage 1 - Build con Maven</h2>
<p><code>FROM maven:3.9.9-eclipse-temurin-21-alpine AS build</code></p>
<ul>
<li>Imagen base con Maven + JDK 21.</li>
<li><code>AS build</code>: le ponemos nombre a esta fase para poder referenciarla después.</li>
</ul>
<p><code>WORKDIR /app</code></p>
<ul>
<li>Establece el directorio de trabajo dentro del contenedor.</li>
<li>A partir de aquí, todos los comandos se ejecutan desde <code>/app</code>. (equivalente a hacer <code>cd /app</code>)</li>
</ul>
<p><code>COPY pom.xml .</code></p>
<ul>
<li>Copia únicamente el <code>pom.xml</code> al contenedor.</li>
<li>Se hace así para aprovechar la caché de Docker y no tener que descargar dependencias cada vez que cambia el código fuente.</li>
</ul>
<p><code>RUN mvn dependency:go-offline</code></p>
<ul>
<li>Descarga todas las dependencias declaradas en el <code>pom.xml</code>.</li>
<li>Si después cambia el código pero no las dependencias, Docker reutiliza esta capa y el build es mucho más rápido.</li>
<li><code>go-offline</code> es un goal del plugin de Maven, indica que se deben descargar todas las dependencias necesarias para que el proyecto pueda compilarse sin conexión a internet. Es útil para mantener las descargas en la caché de Docker.</li>
</ul>
<p><code>COPY src ./src</code></p>
<ul>
<li>Copia el código fuente de la aplicación dentro del contenedor.</li>
<li>Ya tenemos todo lo necesario para compilar.</li>
</ul>
<p><code>RUN mvn clean package -DskipTests</code></p>
<ul>
<li><code>clean</code> elimina compilaciones anteriores.</li>
<li><code>package</code> compila y genera el JAR ejecutable.</li>
<li><code>-DskipTests</code> evita ejecutar los tests durante la construcción de la imagen. En entornos reales, los tests suelen ejecutarse previamente en un pipeline de integración continua. Separar la fase de validación de la fase de construcción permite builds más rápidos y predecibles.</li>
<li>Aquí se genera el mismo JAR que antes creábamos en local, pero ahora dentro del contenedor.</li>
</ul>
<h2 id="stage-2---runtime-limpio">Stage 2 - Runtime limpio</h2>
<p>Comienza una nueva fase completamente independiente.</p>
<p><code>FROM eclipse-temurin:21-jre-alpine-3.23</code></p>
<ul>
<li>Imagen ligera con solo el entorno de ejecución Java.</li>
<li>No incluye Maven ni herramientas de desarrollo.</li>
</ul>
<p><code>WORKDIR /app </code></p>
<ul>
<li>De nuevo, definimos el directorio de trabajo.</li>
<li>Cada <code>FROM</code> reinicia el contexto, por eso hay que volver a declararlo.</li>
</ul>
<p><code>COPY --from=build /app/target/*-SNAPSHOT.jar app.jar</code></p>
<ul>
<li><code>--from=build</code> indica que copiamos desde el stage anterior.</li>
<li>Copiamos el JAR generado dentro del contenedor de build.</li>
</ul>
<p><code>EXPOSE 8080</code></p>
<ul>
<li>Indica que la aplicación escucha en el puerto 8080.</li>
<li>No abre el puerto, solo lo documenta a nivel de imagen.</li>
</ul>
<p><code>ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;app.jar&quot;]</code></p>
<ul>
<li>Define el comando que se ejecutará cuando el contenedor arranque.</li>
<li>Lanza la aplicación Spring Boot.</li>
<li>Es equivalente a ejecutar <code>java -jar app.jar</code></li>
</ul>
<p>Con esto, <strong>hemos separado el proceso de construcción del proceso de ejecución</strong>. El contenedor ya no depende del entorno del desarrollador, sino de un proceso reproducible y aislado. <strong>Igual que separamos responsabilidades en el código, aquí separamos responsabilidades en el contenedor</strong>.</p>
<h1 id="construcción-de-los-contenedores">Construcción de los contenedores</h1>
<p>De forma similar a cómo construíamos la imagen anterior, lanzamos el siguiente comando en consola que, ahora realizará la compilación de la aplicación y generará la imagen para desplegarla (etiquetada como version 2.0).</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t hello-world-api:2.0 .</span></span></code></pre></div>
<p>En este caso el tiempo para finalizar será superior, aunque será más evidente solo en la primera ejecución. Si el proceso finaliza correctamente, tendremos la nueva imagen disponible.</p>
<p><img alt="Resultado en terminal tras lanzar la construcción de la imagen del contenedor" loading="lazy" src="/posts/compilar-app-maven-docker/images/compilando-app-docker.png"></p>
<p>Para desplegar el contenedor con la aplicación, ejecutamos el siguiente comando:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -p 8080:8080 hello-world-api:2.0</span></span></code></pre></div>
<p>De esta forma, tendremos la aplicación compilada y desplegada, sin haber ejecutado Maven en nuestro equipo.</p>
<p><img alt="Hello World ejecutado en el navegador desde el contenedor final" loading="lazy" src="/posts/compilar-app-maven-docker/images/hello-world-from-docker.png"></p>
<h1 id="comparativa-con-el-enfoque-anterior">Comparativa con el enfoque anterior</h1>
<p>Hemos conseguido desacoplar la compilación del entorno del desarrollador y mover esa responsabilidad al contenedor. Pero además usando multi-stage conseguimos que esa imagen final:</p>
<ul>
<li>No incluye herramientas de construcción innecesarias.</li>
<li>Mantiene una imagen final ligera.</li>
<li>Evita inflar la imagen con Maven y código fuente.</li>
<li>Reproducible totalmente en otros entornos.</li>
<li>Lista para CI/CD.</li>
<li>Más segura (al incluir solo los elementos mínimos para su ejecución).</li>
</ul>
<h1 id="reflexión">Reflexión</h1>
<p>Existe una premisa fundamental a la hora de trabajar con contenedores: <strong>Una imagen debe contener solo lo necesario para ejecutarse. Nada más.</strong> Debido a ello, multi-stage es el <strong>planteamiento recomendado en entornos reales</strong>, salvo para la construcción de prototipos ultra rápidos.</p>
<p>Por otro lado, desacoplando la compilación del entorno del desarrollador ya no importa que versión de Maven tiene, si está instalado, si otro desarrollador usa otro sistema operativo, si el servidor se actualiza, etc. <strong>El contenedor se convierte en el entorno estándar de construcción de la aplicación</strong>. <strong>Esto es el enfoque DevOps</strong>.</p>
]]></content:encoded></item><item><title>El primer bug informático fue literalmente un insecto</title><link>/posts/primer-bug-informatico/</link><pubDate>Wed, 25 Feb 2026 19:00:00 +0100</pubDate><guid>/posts/primer-bug-informatico/</guid><description>&lt;p&gt;Hoy día usamos la palabra &lt;em&gt;bug&lt;/em&gt; de forma cotidiana para referirnos a cualquier error de software. Pero lo curioso es que este término, tan ligado a la informática moderna, &lt;strong&gt;nació de un fallo completamente literal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En el año 1947, un equipo de ingenieros trabajaba con el ordenador electromecánico &lt;a href="https://es.wikipedia.org/wiki/Harvard_Mark_II"&gt;Harvard Mark II&lt;/a&gt;. Durante una sesión de diagnóstico, el sistema empezó a comportarse de forma errática. Tras investigar el problema, encontraron la causa: &lt;strong&gt;una polilla atrapada en uno de los relés del equipo&lt;/strong&gt;, impidiendo su correcto funcionamiento.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hoy día usamos la palabra <em>bug</em> de forma cotidiana para referirnos a cualquier error de software. Pero lo curioso es que este término, tan ligado a la informática moderna, <strong>nació de un fallo completamente literal</strong>.</p>
<p>En el año 1947, un equipo de ingenieros trabajaba con el ordenador electromecánico <a href="https://es.wikipedia.org/wiki/Harvard_Mark_II">Harvard Mark II</a>. Durante una sesión de diagnóstico, el sistema empezó a comportarse de forma errática. Tras investigar el problema, encontraron la causa: <strong>una polilla atrapada en uno de los relés del equipo</strong>, impidiendo su correcto funcionamiento.</p>
<p>El insecto fue retirado y pegado en el cuaderno de incidencias, acompañado de la anotación: <em>“First actual case of bug being found”</em> (“Primer caso real de un bug encontrado”).</p>
<figure>
    <img loading="lazy" src="images/first-computer-bug.jpg"
         alt="Cuaderno de incidencias con la polilla del Harvard Mark II (1947)"/> <figcaption>
            <p>Registro del &lsquo;primer bug&rsquo; encontrado en el Harvard Mark II (1947). Imagen de dominio público, cortesía del Naval Surface Warfare Center, vía Wikimedia Commons.</p>
        </figcaption>
</figure>

<p>Aunque el término <em>bug</em> ya se utilizaba de forma informal en ingeniería para referirse a fallos mecánicos, este episodio popularizado por <a href="https://es.wikipedia.org/wiki/Grace_Murray_Hopper">Grace Hopper</a>, ayudó a consolidarlo definitivamente en el vocabulario informático.</p>
<p>Más allá de la anécdota, la historia es interesante por lo que representa. En aquellos primeros sistemas, <strong>hardware y software estaban íntimamente ligados</strong>. Un fallo podía ser causado por un problema eléctrico, mecánico… o incluso biológico. Hoy los errores son menos visibles, pero el concepto sigue siendo el mismo: <strong>algo rompe un supuesto que dábamos por válido</strong>.</p>
<p>También resulta curioso que el equipo documentara el incidente con tanto cuidado. Desde el inicio, la informática entendió que <strong>registrar errores es tan importante como resolverlos</strong>. Los actuales sistemas de logging, trazas y monitorización son herederos directos de ese cuaderno donde acabó pegada una polilla.</p>
<p>En la actualidad, aunque el software se haya vuelto abstracto y complejo, <strong>los errores siguen siendo inevitables</strong>. Cambian de forma, de escala y de impacto, pero siempre están ahí. Y a veces, entender su origen, aunque sea tan humilde como un insecto, nos ayuda a diseñar sistemas más robustos y a asumir que fallar es parte del proceso.</p>
]]></content:encoded></item><item><title>El principio de responsabilidad única</title><link>/posts/principio-responsabilidad-unica/</link><pubDate>Sat, 21 Feb 2026 12:45:53 +0100</pubDate><guid>/posts/principio-responsabilidad-unica/</guid><description>&lt;p&gt;El principio de responsabilidad única &lt;strong&gt;es uno de los principios SOLID&lt;/strong&gt;. Defiende los beneficios de que clases y funciones tengan una &lt;strong&gt;responsabilidad clara y específica&lt;/strong&gt;, de forma que &lt;strong&gt;solo se tengan que modificar por un motivo&lt;/strong&gt;. Aplicándolo conseguimos que nuestro código sea más claro y mantenible.&lt;/p&gt;
&lt;h1 id="el-problema-real"&gt;El problema real&lt;/h1&gt;
&lt;p&gt;Seguro que en algún momento de tu vida como desarrollador te has cruzado con clases que validan datos, acceden a base de datos, construyen respuestas, escriben logs, aplican lógica de negocio&amp;hellip; todo en un mismo sitio.&lt;/p&gt;</description><content:encoded><![CDATA[<p>El principio de responsabilidad única <strong>es uno de los principios SOLID</strong>. Defiende los beneficios de que clases y funciones tengan una <strong>responsabilidad clara y específica</strong>, de forma que <strong>solo se tengan que modificar por un motivo</strong>. Aplicándolo conseguimos que nuestro código sea más claro y mantenible.</p>
<h1 id="el-problema-real">El problema real</h1>
<p>Seguro que en algún momento de tu vida como desarrollador te has cruzado con clases que validan datos, acceden a base de datos, construyen respuestas, escriben logs, aplican lógica de negocio&hellip; todo en un mismo sitio.</p>
<p>Aplicar una modificación en estas clases, por muy pequeña y sencilla que parezca, <strong>además de la dificultad de ubicarla genera una alta probabilidad de generar problemas</strong>.</p>
<p>Un ejemplo reducido de una clase que viola el principio podría ser el siguiente <code>UserService</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">UserService</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createUser</span><span class="p">(</span><span class="n">User</span><span class="w"> </span><span class="n">user</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="cl"><span class="w">	    </span><span class="c1">// Validacion de los datos de entrada</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">user</span><span class="p">.</span><span class="na">getEmail</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">&#34;Email obligatorio&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// Guardar en base de datos</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">database</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// Enviar email</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">emailSender</span><span class="p">.</span><span class="na">sendWelcomeEmail</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="c1">// Log</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">logger</span><span class="p">.</span><span class="na">info</span><span class="p">(</span><span class="s">&#34;Usuario creado: &#34;</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">user</span><span class="p">.</span><span class="na">getEmail</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Esta clase tiene las responsabilidades de: validar datos, persistencia, comunicación externa, logging y lógica de negocio. Demasiadas razones para cambiar.</p>
<h1 id="responsabilidad-única-en-clases">Responsabilidad única en clases</h1>
<p>Si aplicamos el principio de responsabilidad única a esa misma clase, podría quedar de la siguiente manera.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">UserService</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">UserValidator</span><span class="w"> </span><span class="n">validator</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">UserRepository</span><span class="w"> </span><span class="n">repository</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">NotificationService</span><span class="w"> </span><span class="n">notificationService</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">createUser</span><span class="p">(</span><span class="n">User</span><span class="w"> </span><span class="n">user</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">validator</span><span class="p">.</span><span class="na">validate</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">repository</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	    </span><span class="n">notificationService</span><span class="p">.</span><span class="na">sendWelcome</span><span class="p">(</span><span class="n">user</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>De esta forma obtenemos:</p>
<ul>
<li>Código más fácil de leer</li>
<li>Cambios más seguros</li>
<li>Test más simples</li>
<li>Menos miedo a tocar código antiguo</li>
<li>Menos clases <em>Dios</em></li>
</ul>
<h1 id="responsabilidad-única-en-funciones">Responsabilidad única en funciones</h1>
<p>Este mismo problema surge a nivel de funciones. <strong>Una función debería hacer solo una cosa y hacerla bien</strong>. Y en este caso, <em>una sola cosa</em> no significa <em>una sola línea</em>, sino <strong>un único nivel de abstracción</strong> (qué hace frente al cómo lo hace).</p>
<p>De forma similar al ejemplo anterior de la clase, podríamos encontrarnos la siguiente función.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">processOrder</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Validación</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">order</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">order</span><span class="p">.</span><span class="na">getItems</span><span class="p">().</span><span class="na">isEmpty</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">&#34;Pedido inválido&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Cálculo del total</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">OrderItem</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">order</span><span class="p">.</span><span class="na">getItems</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="na">getPrice</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="na">getQuantity</span><span class="p">())));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Persistencia</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">order</span><span class="p">.</span><span class="na">setTotal</span><span class="p">(</span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">orderRepository</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="c1">// Comunicación externa</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">emailService</span><span class="p">.</span><span class="na">sendConfirmation</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><strong>¿Cuántos motivos distintos podrían obligarme a modificar este código?</strong></p>
<ul>
<li>Cambio en las reglas de validación de los datos del pedido (dirección de entrega obligatoria).</li>
<li>Cambio en el cálculo del importe total del pedido (incluir gastos de envío).</li>
<li>Cambia la forma de almacenar los datos por infraestructura (se migra a otro sistema gestor de base de datos).</li>
<li>Cambia el canal de notificación (se envía un sms).</li>
</ul>
<p>Aplicando SRP a la función, separando responsabilidades y manteniendo el comportamiento, podríamos hacer la siguiente división.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span><span class="lnt">28
</span><span class="lnt">29
</span><span class="lnt">30
</span><span class="lnt">31
</span><span class="lnt">32
</span><span class="lnt">33
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">processOrder</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">validate</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">calculateTotal</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">saveOrder</span><span class="p">(</span><span class="n">order</span><span class="p">,</span><span class="w"> </span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">sendConfirmation</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">validate</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">order</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="kc">null</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">order</span><span class="p">.</span><span class="na">getItems</span><span class="p">().</span><span class="na">isEmpty</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">throw</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IllegalArgumentException</span><span class="p">(</span><span class="s">&#34;Pedido inválido&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="nf">calculateTotal</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">ZERO</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="n">OrderItem</span><span class="w"> </span><span class="n">item</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">order</span><span class="p">.</span><span class="na">getItems</span><span class="p">())</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">total</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">total</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">item</span><span class="p">.</span><span class="na">getPrice</span><span class="p">().</span><span class="na">multiply</span><span class="p">(</span><span class="n">BigDecimal</span><span class="p">.</span><span class="na">valueOf</span><span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="na">getQuantity</span><span class="p">()))</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">total</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">saveOrder</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">,</span><span class="w"> </span><span class="n">BigDecimal</span><span class="w"> </span><span class="n">total</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">order</span><span class="p">.</span><span class="na">setTotal</span><span class="p">(</span><span class="n">total</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">orderRepository</span><span class="p">.</span><span class="na">save</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">private</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">sendConfirmation</span><span class="p">(</span><span class="n">Order</span><span class="w"> </span><span class="n">order</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">emailService</span><span class="p">.</span><span class="na">sendConfirmation</span><span class="p">(</span><span class="n">order</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Aunque ahora hay más funciones, el código <strong>se lee como una historia</strong>. Permite <strong>cambiar una responsabilidad sin tocar las otras</strong> y facilita los test.</p>
<p>Es importante no confundir responsabilidad única con hacer una sola acción. La función <code>processOrder</code> sigue realizando varias acciones (validar, calcular, guardar y notificar), pero todas forman parte de una misma responsabilidad: gestionar el proceso completo de un pedido.</p>
<p>La función <strong>solo cambiará si cambia el flujo del proceso, no si cambian los detalles internos de validación, cálculo o persistencia</strong>.</p>
<h1 id="no-lo-lleves-al-extremo">No lo lleves al extremo</h1>
<p>Algunos de los errores más comunes al intentar aplicar SRP son:</p>
<ul>
<li>Reducir tanto como para llegar a <em>una clase = un método</em></li>
<li>Crear abstracciones innecesarias, llegando a niveles muy profundos</li>
<li>Aplicarlo solo a clases y funciones grandes</li>
<li>Pensar <em>ya lo simplificaremos</em> (no se hará)</li>
</ul>
<p>En aplicaciones pequeñas o scripts simples, dividir responsabilidades puede introducir más complejidad que beneficio. <strong>El diseño debe responder al contexto, no a principios aplicados de forma automática.</strong></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>El principio de responsabilidad única no es una regla estricta, es una alarma. <strong>Cuando una clase o función empieza a crecer sin control, probablemente no esté siendo “productiva”, sino acumulando responsabilidades que no le corresponden</strong>.</p>
]]></content:encoded></item><item><title>Primer test en Spring Boot</title><link>/posts/primer-test-spring-boot/</link><pubDate>Fri, 06 Feb 2026 16:23:02 +0100</pubDate><guid>/posts/primer-test-spring-boot/</guid><description>&lt;p&gt;Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: &lt;strong&gt;el análisis, la documentación… los tests y las pruebas&lt;/strong&gt;. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE y empezar a &lt;em&gt;picar código&lt;/em&gt;, cuando muchas veces &lt;strong&gt;lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Alrededor de la programación hay una serie de tareas menos agradecidas y poco atractivas para la mayoría de los desarrolladores: <strong>el análisis, la documentación… los tests y las pruebas</strong>. Con cada nuevo evolutivo solemos lanzarnos directamente al teclado, abrir nuestro IDE  y empezar a <em>picar código</em>, cuando muchas veces <strong>lo más efectivo a largo plazo es comenzar con papel y bolígrafo, desgranando qué queremos hacer realmente</strong>.</p>
<p>Con las pruebas y los tests sucede algo similar. Durante el desarrollo vamos lanzando comprobaciones manuales y, cuando vemos que todo más o menos funciona, lo damos por válido. Puede que incluso documentemos alguna de ellas, pero suele percibirse como otra tarea pesada que no siempre motiva y que intentamos quitarnos de encima cuanto antes.</p>
<p>Sin embargo, al igual que un buen análisis, diseño y planificación nos ahorra tiempo y evita errores a largo plazo, <strong>un buen diseño de los casos de prueba y su automatización contribuye a obtener un mejor resultado final</strong>. Además, nos garantiza que podremos detectar rápidamente si un nuevo evolutivo introduce errores que no habíamos contemplado.</p>
<h1 id="testing-en-spring-boot">Testing en Spring Boot</h1>
<p>Spring Boot incorpora <em>de serie</em> varias herramientas que facilitan enormemente la implementación de los primeros tests. Entre ellas destacan:</p>
<ul>
<li><strong>JUnit 5</strong>: es un framework de testing que nos permite definir y ejecutar test en Java mediante anotaciones como <code>@Test</code>.</li>
<li><strong>Spring Boot Test</strong>: conjunto de utilidades que facilita probar aplicaciones Spring cargando el contexto, inyectando dependencias y simulando peticiones HTTP.</li>
<li><strong>Maven configurado para ejecutar test</strong>: Maven ejecuta automáticamente los test durante el ciclo de compilación, fallando el proceso si alguno no pasa y evitando la generación de artefactos incorrectos.</li>
</ul>
<h1 id="primer-test-de-hello-world">Primer test de Hello World</h1>
<p>Para implementar los primeros tests nos vamos a basar en el <a href="/posts/spring-boot-hello-world/"><em>Hello World</em> que desarrollamos en el artículo anterior</a> y que <a href="/posts/compilar-app-maven/">ya hemos compilado con Maven</a>.</p>
<p>Al crear la estructura básica del proyecto con Spring Initializr e incluir la dependencia <em>Spring Web</em>, ya deberíamos tener en el <code>pom.xml</code> la dependencia <code>spring-boot-starter-webmvc-test</code>, que nos proporciona todo lo necesario para estos primeros tests.</p>
<h2 id="la-aplicación-arranca">La aplicación arranca</h2>
<p>Se trata de un test de contexto, anotado con <code>@SpringBootTest</code>, que ya tendremos creado automáticamente en la estructura del proyecto dentro de la carpeta <code>src/test</code>. En nuestro caso se denomina <code>SpringBootHelloWorldApplicationTests</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">class</span> <span class="nc">SpringBootHelloWorldApplicationTests</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">contextLoads</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Este test no contiene lógica alguna, pero precisamente ahí reside su valor. Comprueba que el contexto de Spring arranca correctamente y, si falla, indica que existe un problema grave en la configuración de la aplicación.</p>
<h2 id="testear-un-endpoint-sencillo-hello-world">Testear un endpoint sencillo: Hello World</h2>
<p>El siguiente paso es diseñar e implementar un test para nuestro endpoint <code>/hello</code>. Aunque se trata de un endpoint muy sencillo, ya podemos validar tres aspectos básicos:</p>
<ol>
<li>Que el endpoint existe bajo la URL</li>
<li>Que responde con código HTTP 200</li>
<li>Que devuelve el contenido esperado</li>
</ol>
<p>El código del test es el siguiente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.junit.jupiter.api.Test</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.beans.factory.annotation.Autowired</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.test.context.SpringBootTest</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.MockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.content</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import static</span><span class="w"> </span><span class="nn">org.springframework.test.web.servlet.result.MockMvcResultMatchers.status</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@SpringBootTest</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@AutoConfigureMockMvc</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloControllerTest</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Autowired</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="n">MockMvc</span><span class="w"> </span><span class="n">mockMvc</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@Test</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kt">void</span><span class="w"> </span><span class="nf">helloEndpointReturnsHelloWorld</span><span class="p">()</span><span class="w"> </span><span class="kd">throws</span><span class="w"> </span><span class="n">Exception</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">mockMvc</span><span class="p">.</span><span class="na">perform</span><span class="p">(</span><span class="w"> </span><span class="n">get</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">status</span><span class="p">().</span><span class="na">isOk</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">                </span><span class="p">.</span><span class="na">andExpect</span><span class="p">(</span><span class="w"> </span><span class="n">content</span><span class="p">().</span><span class="na">string</span><span class="p">(</span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="elementos-que-se-usan-en-el-test">Elementos que se usan en el test</h3>
<h4 id="anotaciones-de-spring-y-junit">Anotaciones de Spring y JUnit:</h4>
<ul>
<li><code>@Autowired</code>: le indica a Spring que inyecte automáticamente una instancia de <code>MockMvc</code> en el test.</li>
<li><code>@Test</code>: anotación de JUnit 5 que marca un método como test y permite que el framework lo ejecute durante la fase de testing.</li>
<li><code>@AutoConfigureMockMvc</code>: indica a Spring Boot que configure automáticamente <code>MockMvc</code>, permitiéndonos simular peticiones HTTP a los endpoints sin arrancar un servidor real.</li>
</ul>
<h4 id="herramientas-para-simular-peticiones-http">Herramientas para simular peticiones HTTP:</h4>
<ul>
<li><code>MockMvc</code>: utilidad de Spring para simular llamadas HTTP (GET, POST, etc.) contra los controladores y verificar sus respuestas.</li>
<li><code>get()</code>: construye una petición HTTP de tipo GET contra la ruta indicada, en nuestro ejemplo <code>/hello</code>.</li>
<li><code>perform()</code>: ejecuta la petición HTTP simulada y definida previamente. Devuelve el resultado para poder hacer comprobaciones sobre la respuesta.</li>
</ul>
<h4 id="validación-de-la-respuesta">Validación de la respuesta:</h4>
<ul>
<li><code>andExpect()</code>: define una expectativa sobre la respuesta obtenida; si no se cumple, el test falla.</li>
<li><code>status()</code> e <code>isOk()</code>: comprueban que el código de estado HTTP de la respuesta sea 200 (OK).</li>
<li><code>content()</code>: permite validar el contenido del cuerpo de la respuesta.</li>
</ul>
<h1 id="ejecutar-los-test-con-maven">Ejecutar los test con Maven</h1>
<p>Una vez configurados los tests, podemos ejecutarlos desde el propio IDE o, al igual que para la construcción del artefacto, desde la línea de comandos utilizando Maven.</p>
<p>Desde un terminal, situados en la carpeta raíz del proyecto, ejecutamos:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn test</span></span></code></pre></div>
<p>Maven compilará el código y lanzará todos los tests definidos en el proyecto, mostrando el resultado en la consola.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado positivo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ok.png"></p>
<p>Si forzamos un error -por ejemplo, cambiando el texto esperado en el cuerpo de la respuesta- y ejecutamos de nuevo el comando, veremos cómo el test falla. La salida por consola indicará qué test ha fallado, qué se esperaba que ocurriera y cuál ha sido el resultado real, proporcionando información muy útil para entender el problema y corregirlo.</p>
<p><img alt="Terminal que muestra que el test ha dado resultado negativo" loading="lazy" src="/posts/primer-test-spring-boot/images/mvn_test_ko.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>En este artículo hemos dado el primer paso en la incorporación de tests automatizados a nuestra API Spring Boot. Partiendo de un ejemplo muy sencillo, hemos visto cómo <strong>validar que la aplicación arranca correctamente y cómo comprobar el comportamiento de un endpoint HTTP mediante tests automatizados</strong>.</p>
<p>Hasta ahora, en la serie hemos creado una API básica, la hemos compilado con Maven y la hemos preparado para su ejecución. Con estos primeros tests añadimos <strong>una capa fundamental de seguridad que nos permitirá evolucionar el código con mayor confianza</strong>.</p>
<p>En los siguientes artículos iremos ampliando este enfoque, incorporando tests más completos y sentando las bases para su integración en un proceso de integración continua (CI), donde estos tests se ejecutarán automáticamente en cada cambio del código.</p>
]]></content:encoded></item><item><title>Desplegar una aplicación Spring Boot en un contenedor</title><link>/posts/desplegar-aplicacion-contenedor/</link><pubDate>Fri, 30 Jan 2026 17:11:15 +0100</pubDate><guid>/posts/desplegar-aplicacion-contenedor/</guid><description>&lt;p&gt;Hasta ahora, &lt;a href="/posts/spring-boot-hello-world/"&gt;hemos creado una aplicación Hello World con Spring Boot&lt;/a&gt;, la hemos &lt;a href="/posts/compilar-app-maven/"&gt;compilado con Maven y la hemos ejecutado correctamente en nuestro equipo&lt;/a&gt;. Sin embargo, &lt;strong&gt;ejecutar una aplicación en local es solo el primer paso&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;En cuanto queremos compartirla, desplegarla en un servidor o moverla entre distintos entornos (desarrollo, pruebas, producción), empiezan a aparecer los problemas clásicos: &lt;strong&gt;versiones diferentes de Java, dependencias que no coinciden, configuraciones específicas de la máquina o incluso sistemas operativos diferentes&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Hasta ahora, <a href="/posts/spring-boot-hello-world/">hemos creado una aplicación Hello World con Spring Boot</a>, la hemos <a href="/posts/compilar-app-maven/">compilado con Maven y la hemos ejecutado correctamente en nuestro equipo</a>. Sin embargo, <strong>ejecutar una aplicación en local es solo el primer paso</strong>.</p>
<p>En cuanto queremos compartirla, desplegarla en un servidor o moverla entre distintos entornos (desarrollo, pruebas, producción), empiezan a aparecer los problemas clásicos: <strong>versiones diferentes de Java, dependencias que no coinciden, configuraciones específicas de la máquina o incluso sistemas operativos diferentes</strong>.</p>
<p>Aquí es donde <strong>encapsular la aplicación en un contenedor</strong> se convierte en el siguiente paso natural.</p>
<h1 id="qué-es-un-contenedor">Qué es un contenedor</h1>
<p>Un contenedor <strong>nos permite empaquetar la aplicación junto con todo lo que necesita para ejecutarse</strong>: la versión de Java, las dependencias y la configuración básica del entorno. El resultado es un <strong>paquete autocontenido que se comporta igual en cualquier máquina donde se ejecute</strong>.</p>
<p><a href="/posts/instalar-docker-windows/">Docker</a> es una de las herramientas más extendidas para crear, ejecutar y gestionar contenedores de forma sencilla.</p>
<h1 id="configuración-del-contenedor">Configuración del contenedor</h1>
<p>De forma similar al fichero <code>pom.xml</code> de Maven, para configurar un contenedor Docker usaremos un fichero <code>Dockerfile</code> (sin extensión) que se debe ubicar en la misma ruta del proyecto.</p>
<p>Con el siguiente Dockerfile mínimo se está creando de la forma más simple y limpia un contenedor Docker para ejecutar una aplicación Spring Boot:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span><span class="lnt">8
</span><span class="lnt">9
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="k">FROM</span><span class="w"> </span><span class="s">eclipse-temurin:21-jre-alpine-3.23</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="w"> </span><span class="s">/app</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">COPY</span> target/SpringBootHelloWorld-0.0.1-SNAPSHOT.jar app.jar  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">EXPOSE</span><span class="w"> </span><span class="s">8080</span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>  <span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENTRYPOINT</span> <span class="p">[</span><span class="s2">&#34;java&#34;</span><span class="p">,</span><span class="s2">&#34;-jar&#34;</span><span class="p">,</span><span class="s2">&#34;app.jar&#34;</span><span class="p">]</span></span></span></code></pre></td></tr></table>
</div>
</div>
<ul>
<li>En primer lugar se indica una imagen base para el contenedor. En este caso se trata de una distribución ligera de Linux con Java 21 (solo el entorno de ejecución), suficiente para ejecutar una aplicación Spring Boot.</li>
<li>Después se indica el directorio de trabajo dentro del contenedor. A partir de aquí, todos los comandos se ejecutarán desde /app.</li>
<li>Se copia el JAR generado por Maven desde el equipo anfitrión al contenedor, renombrándolo como app.jar. El nombre del JAR debe coincidir exactamente con el generado.</li>
<li>Se indica el puerto en el que escucha la aplicación, que en Spring Boot suele ser el 8080 por defecto, pero a nivel informativo, no se abre el puerto.</li>
<li>Por último, se define el comando que se ejecutará al arrancar el contenedor, lanzando la aplicación Spring Boot.</li>
</ul>
<h1 id="construcción-de-la-imagen">Construcción de la imagen</h1>
<p>El primer paso es construir una imagen Docker a partir de la cual se podrán crear instancias del contenedor y desplegarlas.</p>
<p>Con Docker ejecutándose, y desde la raíz del proyecto, se lanza el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker build -t hello-world-api:1.0 .</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Qué significa:</p>
<ul>
<li><code>-t helloWorld-api:1.0</code> es el nombre y versión de la imagen.</li>
<li><code>.</code> indica que se trabaja en el directorio actual, donde está el Dockerfile sobre el que generar la imagen.</li>
</ul>
<p><img alt="Pantalla de terminal que muestra el resultado de la construcción de la imagen Docker" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_build.png"></p>
<p>Si todo va bien, podemos listar las imágenes del sistema con el comando <code>docker images</code>.</p>
<p><img alt="Pantalla de terminal que muestra el listado de imágenes disponibles" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_images.png"></p>
<h1 id="ejecutar-el-contenedor">Ejecutar el contenedor</h1>
<p>Para crear y ejecutar un contenedor a partir de la imagen generada, se lanza el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -p 8080:8080 --name hello-world-api-container hello-world-api:1.0</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Qué significa:</p>
<ul>
<li><code>-p 8080:8080</code> indica el puerto del equipo donde se quieren recibir las peticiones del contenedor, seguido del puerto del contenedor a donde deben dirigirse. En este caso, usa el mismo.</li>
<li><code>--name helloWorld-api-container</code> indica un nombre para identificar al contenedor.</li>
</ul>
<p><img alt="Pantalla de terminal que muestra el contenedor construido en ejecución" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_run.png"></p>
<p>De nuevo, si todo va bien, podremos abrir un navegador y acceder a la url <code>http://localhost:8080</code> y ver el mensaje de &lsquo;Hello World&rsquo;, esta vez lanzado desde la API en el contenedor.</p>
<p><img alt="Pantalla del navegador con la invocación a la API Hello World desplegada en el contenedor" loading="lazy" src="/posts/desplegar-aplicacion-contenedor/images/docker_hello.png"></p>
<p>Para parar el contenedor, que se habrá quedado en la terminal, pulsamos la combinación de teclas <code>Ctrl + C</code>.</p>
<h1 id="otros-comandos-útiles">Otros comandos útiles</h1>
<h2 id="ejecución-del-contenedor-en-segundo-plano">Ejecución del contenedor en segundo plano</h2>
<p>Para no tener que mantener la ventana de terminal con el contenedor en ejecución, se puede lanzar en segundo plano añadiendo <code>-d</code> al comando <code>run</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run -d -p 8080:8080 --name helloWorld-api-container helloWorld-api:1.0</span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="ver-logs-del-contenedor">Ver logs del contenedor</h2>
<p>Para consultar los logs en la consola de Spring Boot dentro del contenedor, lanzamos el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker logs helloWorld-api-container</span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="detener-el-contenedor">Detener el contenedor</h2>
<p>Al no estar ahora ligada la ejecución del contenedor a la sesión de terminal, para detenerlo, se debe lanzar el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker stop helloWorld-api-container</span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="conclusiones">Conclusiones</h1>
<p>Ya hemos dado un paso clave: <strong>llevar nuestra aplicación Spring Boot desde un simple JAR hasta un contenedor ejecutable</strong>, garantizando que se comporta igual independientemente del entorno donde se despliegue.</p>
<p>El flujo que hemos seguido hasta ahora es muy sencillo, pero extremadamente importante:</p>
<ul>
<li>Hemos desarrollado una <strong>API sencilla en Spring Boot</strong></li>
<li>Con <strong>Apache Maven</strong> hemos generado un artefacto ejecutable (el JAR)</li>
<li>Ese JAR lo hemos encapsulado en un contenedor usando <strong>Docker</strong>, resolviendo de un plumazo los problemas de dependencias, versiones y entornos.</li>
<li>Hemos visto que desplegar la aplicación se reduce a construir una imagen y arrancar un contenedor.</li>
</ul>
<p>A partir de aquí, se abre un abanico de mejoras naturales como optimizar el proceso, orquestar varios servicios y llegar a automatizar todo el flujo de construcción y despliegue. Todo ello de forma cercana a un entorno real de producción.</p>
]]></content:encoded></item><item><title>Pasos para instalar Docker en Windows</title><link>/posts/instalar-docker-windows/</link><pubDate>Sun, 25 Jan 2026 19:54:26 +0100</pubDate><guid>/posts/instalar-docker-windows/</guid><description>&lt;h1 id="qué-es-docker"&gt;¿Qué es Docker?&lt;/h1&gt;
&lt;p&gt;Docker permite &lt;strong&gt;crear y ejecutar aplicaciones dentro de contenedores&lt;/strong&gt;, facilitando un &lt;strong&gt;entorno consistente y reproducible tanto en desarrollo y despliegue&lt;/strong&gt;. Esto evita el clásico &lt;em&gt;“en mi máquina funciona”&lt;/em&gt; y lo convierte en una herramienta clave en flujos modernos de CI/CD.&lt;/p&gt;
&lt;h1 id="instalación-de-docker-en-windows"&gt;Instalación de Docker en Windows&lt;/h1&gt;
&lt;p&gt;En Windows, la forma recomendada de trabajar con Docker es mediante &lt;a href="https://www.docker.com/products/docker-desktop/"&gt;Docker Desktop&lt;/a&gt;, ya que su instalación es muy sencilla e incluye todo lo necesario para empezar.&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="qué-es-docker">¿Qué es Docker?</h1>
<p>Docker permite <strong>crear y ejecutar aplicaciones dentro de contenedores</strong>, facilitando un <strong>entorno consistente y reproducible tanto en desarrollo y despliegue</strong>. Esto evita el clásico <em>“en mi máquina funciona”</em> y lo convierte en una herramienta clave en flujos modernos de CI/CD.</p>
<h1 id="instalación-de-docker-en-windows">Instalación de Docker en Windows</h1>
<p>En Windows, la forma recomendada de trabajar con Docker es mediante <a href="https://www.docker.com/products/docker-desktop/">Docker Desktop</a>, ya que su instalación es muy sencilla e incluye todo lo necesario para empezar.</p>
<h2 id="requisitos-previos">Requisitos previos</h2>
<p>En la actualidad, para poder instalar Docker en Windows, el sistema debe cumplir los siguientes requisitos:</p>
<ul>
<li>Contar con Windows 10 u 11 de 64 bits (Home o Pro)</li>
<li>Tener habilitada la virtualización en la BIOS</li>
<li>Tener activo WSL 2 (Windows Subsystem for Linux)</li>
</ul>
<h1 id="virtualización-habilitada">Virtualización habilitada</h1>
<p>Para poder ejecutar Docker y contenedores en un equipo, no solo debe contar con un procesador que lo soporte, también debe tener habilitada la opción de virtualización en la BIOS.</p>
<p>Para verificarlo, se puede acudir al Administrador de tareas de Windows, sección CPU, donde en la parte inferior aparece una etiqueta que indica si la virtualización está habilitada.</p>
<p><img alt="Administrador de tareas de Windows donde se muestra si está habilitada la virtualización" loading="lazy" src="/posts/instalar-docker-windows/images/virtualizacion_habilitada.png"></p>
<p>Si la configuración indica que está deshabilitada, se debe cambiar desde la BIOS del propio equipo siguiendo las indicaciones específicas del fabricante.</p>
<h1 id="instalación-de-wsl-2">Instalación de WSL 2</h1>
<p>WSL (Windows Subsystem for Linux) permite <strong>ejecutar un entorno Linux directamente en Windows</strong>. Su versión más moderna, <strong>WSL 2</strong>, ofrece mejor rendimiento y compatibilidad, y es la opción recomendada para trabajar con Docker. Docker Desktop utiliza WSL 2 como backend para ejecutar los contenedores Linux de forma eficiente dentro de Windows.</p>
<p>La instalación es muy sencilla, solo habrá que lanzar el comando siguiente en una terminal.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wsl --install</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Esto, automáticamente:</p>
<ul>
<li>Habilita las características necesarias de Windows.</li>
<li>Instala <strong>WSL 2</strong> (la versión recomendada).</li>
<li>Descarga e instala <strong>Ubuntu</strong> como distribución por defecto.</li>
</ul>
<p>Tras un reinicio, nos pedirá crear un usuario de Linux con una contraseña (no es necesario que coincida con los de Windows.)</p>
<p>Para usar Linux desde Windows, basta con abrir una terminal y lanzar el siguiente comando.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">wsl</span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="descarga-e-instalación-de-docker-desktop">Descarga e instalación de Docker Desktop</h1>
<p>Docker Desktop para Windows está disponible para su descarga desde la <a href="https://www.docker.com/products/docker-desktop/">web oficial de Docker</a>. Ahí está disponible un instalador ejecutable.</p>
<p>Una vez descargado, basta con ejecutarlo siguiendo los pasos marcados, y marcando la opción de &ldquo;Use WSL 2 instead of Hyper-V&rdquo; cuando nos lo solicite.</p>
<p>Al finalizar, es posible que sea necesario reiniciar el equipo. Tras ello, ejecutamos Docker Desktop desde el menú Inicio. La primera vez puede tardar unos minutos mientras se configuran los componentes necesarios, llegando a una pantalla similar a la siguiente si todo ha ido bien.</p>
<p><img alt="Ventana de Docker Desktop en ejecución" loading="lazy" src="/posts/instalar-docker-windows/images/docker_desktop.png"></p>
<p>También se puede verificar que todo está funcionando desde la terminal, lanzado el siguiente comando que mostrará la versión en ejecución.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker --version</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Ventana de terminal mostrando la versión de Docker en ejecución" loading="lazy" src="/posts/instalar-docker-windows/images/docker_version.png"></p>
<p>Opcionalmente, se puede lanzar un contenedor de prueba que mostrará un mensaje de bienvenida con el siguiente comando.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docker run hello-world</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Ventana de terminal con una ejecución del contenedor Hello World" loading="lazy" src="/posts/instalar-docker-windows/images/docker_hello.png"></p>
<h1 id="conclusión">Conclusión</h1>
<p>Con Docker Desktop instalado en Windows ya tenemos un entorno preparado para trabajar con contenedores de forma local. A partir de ahí ya se pueden crear y ejecutar contenedores e integrar Docker en proyectos de desarrollo. Las posibilidades son infinitas y con cierta complejidad si queremos especializarnos, pero la potencia que ofrecen los contenedores, aun con configuraciones básicas, ya son muy grandes.</p>
]]></content:encoded></item><item><title>El bug del año 2038: cuando el tiempo se desborda</title><link>/posts/bug-tiempo-desborda/</link><pubDate>Sat, 10 Jan 2026 14:04:00 +0100</pubDate><guid>/posts/bug-tiempo-desborda/</guid><description>&lt;p&gt;Una de las fuentes habituales de errores a la hora de desarrollar software y administrar sistemas informáticos es el tiempo. Formatos, husos horarios, conversiones&amp;hellip; suelen generar problemas cuando diferentes aplicaciones o sistemas no están alineados.&lt;/p&gt;
&lt;p&gt;En sistemas tipo Unix, el tiempo se representa como &lt;strong&gt;el número de segundos transcurridos desde el 1 de enero de 1970&lt;/strong&gt;. Este valor se conoce como &lt;em&gt;Unix time&lt;/em&gt;. El problema aparece cuando ese valor se almacena en un &lt;strong&gt;entero de 32 bits con signo&lt;/strong&gt;. Ese tipo de dato tiene un valor máximo que, cuando se alcanza, el contador se desborda y pasa a valores negativos.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una de las fuentes habituales de errores a la hora de desarrollar software y administrar sistemas informáticos es el tiempo. Formatos, husos horarios, conversiones&hellip; suelen generar problemas cuando diferentes aplicaciones o sistemas no están alineados.</p>
<p>En sistemas tipo Unix, el tiempo se representa como <strong>el número de segundos transcurridos desde el 1 de enero de 1970</strong>. Este valor se conoce como <em>Unix time</em>. El problema aparece cuando ese valor se almacena en un <strong>entero de 32 bits con signo</strong>. Ese tipo de dato tiene un valor máximo que, cuando se alcanza, el contador se desborda y pasa a valores negativos.</p>
<h1 id="el-efecto-2000">El efecto 2000</h1>
<p>A finales de los años 90, el mundo de la tecnología vivió una cuenta atrás peculiar que se denominó <em>efecto 2000</em>. En etapas iniciales de desarrollo de los sistemas informáticos la memoria era un elemento con un coste elevado, por lo que <strong>muchos almacenaban el año con sólo dos dígitos</strong>. Asumían implícitamente que siempre sería “19xx”, lo cual iba a provocar que al pasar de &ldquo;99&rdquo; a &ldquo;00&rdquo; se interpretase el año 2000 como 1900.</p>
<p>El problema se mitigó sin incidentes relevantes y dejó una lección: <strong>las decisiones técnicas aparentemente inocentes, en sistemas destinados a perdurar en el tiempo, pueden tener consecuencias mucho tiempo después</strong>.</p>
<h1 id="el-tiempo-se-romperá-de-nuevo-en-2038">El tiempo se romperá de nuevo en 2038</h1>
<p>El <strong>19 de enero de 2038 a las 03:14:07 UTC</strong> los segundos transcurridos desde la fecha base en Unix alcanzarán el valor máximo que pueden almacenar los sistemas de 32 bits. En el siguiente, <strong>el valor se desbordará y el tiempo &ldquo;volverá atrás&rdquo;, provocando comportamientos impredecibles</strong>.</p>
<p>A diferencia del año 2000, esta vez, el problema no afecta tanto a sistemas de escritorio modernos, sino a pequeños dispositivos de infraestructura. Esto son: <strong>sistemas embebidos, dispositivos IoT, maquinaria industrial o software legado que sigue funcionando años después de haber sido escrito</strong>. Muchos de estos sistemas no se actualizan con facilidad, o directamente no se actualizan, y podrían seguir funcionando dentro de 10 o 15 años y romperse de golpe.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Year2038Example</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="kt">int</span><span class="w"> </span><span class="n">maxSeconds</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Integer</span><span class="p">.</span><span class="na">MAX_VALUE</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">maxSeconds</span><span class="p">);</span><span class="w">      </span><span class="c1">//  2147483647</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">		</span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">maxSeconds</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">1</span><span class="p">);</span><span class="w">  </span><span class="c1">// -2147483648</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p><strong>No es un fallo del futuro, sino una deuda del pasado</strong>. Una decisión técnica antigua que sigue vigente. <strong>Diseñar pensando a largo plazo, así como tener en cuenta los supuestos ocultos o extremos, es una de las responsabilidades de los ingenieros de software.</strong></p>
]]></content:encoded></item><item><title>Compilar una aplicación Java usando Maven</title><link>/posts/compilar-app-maven/</link><pubDate>Fri, 02 Jan 2026 19:40:50 +0100</pubDate><guid>/posts/compilar-app-maven/</guid><description>&lt;p&gt;Aunque hoy día existen muy buenos IDEs con numerosos plugins que permiten centrarse en el código y en el desarrollo de la propia aplicación, es útil también &lt;strong&gt;conocer qué hacen internamente y poder lanzar tareas de forma más cercana a cómo se haría en un entorno de producción&lt;/strong&gt;.&lt;/p&gt;
&lt;h1 id="objetivo"&gt;Objetivo&lt;/h1&gt;
&lt;p&gt;Se trata de &lt;strong&gt;compilar y desplegar una aplicación Spring Boot con Maven&lt;/strong&gt; utilizando los comandos correspondientes a Maven y Java.&lt;/p&gt;
&lt;h1 id="prerrequisitos"&gt;Prerrequisitos&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;1. Tener instalado y configurado Java&lt;/strong&gt;. Para comprobarlo, lanzamos el siguiente comando en una terminal:&lt;/p&gt;</description><content:encoded><![CDATA[<p>Aunque hoy día existen muy buenos IDEs con numerosos plugins que permiten centrarse en el código y en el desarrollo de la propia aplicación, es útil también <strong>conocer qué hacen internamente y poder lanzar tareas de forma más cercana a cómo se haría en un entorno de producción</strong>.</p>
<h1 id="objetivo">Objetivo</h1>
<p>Se trata de <strong>compilar y desplegar una aplicación Spring Boot con Maven</strong> utilizando los comandos correspondientes a Maven y Java.</p>
<h1 id="prerrequisitos">Prerrequisitos</h1>
<p><strong>1. Tener instalado y configurado Java</strong>. Para comprobarlo, lanzamos el siguiente comando en una terminal:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Que debe devolver una salida similar a la siguiente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">openjdk version &#34;25.0.1&#34; 2025-10-21 LTS
</span></span><span class="line"><span class="cl">OpenJDK Runtime Environment Temurin-25.0.1+8 (build 25.0.1+8-LTS)
</span></span><span class="line"><span class="cl">OpenJDK 64-Bit Server VM Temurin-25.0.1+8 (build 25.0.1+8-LTS, mixed mode, sharing)</span></span></code></pre></td></tr></table>
</div>
</div>
<p><strong>2. Tener instalado y configurado Maven.</strong> Para ello, se puede seguir el siguiente artículo: <a href="/posts/instalar-maven-windows/">Pasos para instalar Maven en Windows</a></p>
<p><strong>3. Tener una aplicación Java que haga uso de Maven</strong>. Se puede usar la desarrollada en el siguiente artículo: <a href="/posts/spring-boot-hello-world/">Spring Boot Hello World</a></p>
<h1 id="compilar-el-proyecto">Compilar el proyecto</h1>
<ol>
<li>Navegar hasta la ruta del proyecto donde está el fichero pom.xml</li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="nb">cd</span> ruta/a/tu/proyecto</span></span></code></pre></td></tr></table>
</div>
</div>
<ol start="2">
<li>Limpiar y empaquetar usando el comando <code>mvn</code></li>
</ol>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn clean package</span></span></code></pre></td></tr></table>
</div>
</div>
<ul>
<li><code>clean</code> → borra compilaciones previas</li>
<li><code>package</code> → compila + ejecuta tests + genera el <strong>JAR</strong></li>
</ul>
<p>Si todo va bien, la consola mostrará un mensaje de &lsquo;BUILD SUCCESS&rsquo;.</p>
<h1 id="localización-del-artefacto">Localización del artefacto</h1>
<p>Si la compilación ha finalizado correctamente, se habrá generado el fichero <code>jar</code> dentro de la carpeta <code>target</code> dentro de la estructura de carpetas del proyecto. Pero en el caso de una aplicación Spring Boot, nos encontraremos con dos ficheros, un <code>.jar</code> y adicionalmente un <code>.jar.original</code>.</p>
<p><img alt="Ficheros jar generados al compilar la aplicación con Maven" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-jars.png"></p>
<p>El <code>.jar</code> es el que debemos usar para desplegar, ya que contiene:</p>
<ul>
<li>El <strong>código de la aplicación compilado</strong></li>
<li><strong>TODAS</strong> las dependencias</li>
<li>El cargador de Spring Boot</li>
<li>Un <code>Main-Class</code> ejecutable</li>
</ul>
<p>El <code>.jar.original</code> por contra, no se usa para desplegar, ya que:</p>
<ul>
<li>Es el <strong>JAR “normal” de Maven</strong></li>
<li>Solo contiene el código compilado</li>
<li><strong>No incluye dependencias</strong></li>
<li><strong>No es ejecutable</strong></li>
</ul>
<p>Lo que ha sucedido es Maven ha creado inicialmente el <code>jar</code> estándar <code>SpringBootHelloWorld-0.0.1-SNAPSHOT.jar</code>. Posteriormente, el plugin de Spring Boot lo renombra a <code>SpringBootHelloWorld-0.0.1-SNAPSHOT.jar.original</code> dejándolo como respaldo interno del proceso y realiza un reempaquetado generando el <code>.jar</code> que contiene todo lo necesario para que sea ejecutable.</p>
<h1 id="despliegue-de-la-aplicación">Despliegue de la aplicación</h1>
<p>Una vez localizado el fichero <code>.jar</code> se ejecuta mediante el comando <code>java</code> de la siguiente manera:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">java -jar target/SpringBootHelloWorld-0.0.1-SNAPSHOT.jar</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Mostrando la consola algo similar a lo que se ve en la siguiente imagen.</p>
<p><img alt="Aplicación en ejecución tras lanzar el jar" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-ejecucion.png"></p>
<p>Y en este caso, se podrá probar que la aplicación funciona accediendo a un navegador.</p>
<p><img alt="Probando la aplicación en el navegador" loading="lazy" src="/posts/compilar-app-maven/images/compilar-app-java-maven-test.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Este enfoque permite comprender mejor el ciclo de construcción de una aplicación Java, identificar el artefacto correcto para su despliegue y ganar autonomía a la hora de ejecutar la aplicación en otros entornos.</p>
<p>Entender qué genera Maven y cómo Spring Boot reempaqueta el artefacto final es un paso fundamental antes de abordar escenarios más avanzados como el despliegue en contenedores Docker o en servidores de producción.</p>
]]></content:encoded></item><item><title>Spring Boot Hello World</title><link>/posts/spring-boot-hello-world/</link><pubDate>Thu, 01 Jan 2026 12:29:15 +0100</pubDate><guid>/posts/spring-boot-hello-world/</guid><description>&lt;p&gt;&lt;a href="https://spring.io/projects/spring-boot"&gt;Spring Boot&lt;/a&gt; es en la actualidad un &lt;strong&gt;estándar en el desarrollo de aplicaciones Java&lt;/strong&gt;, con mucho protagonismo en el mundo empresarial. Con una &lt;strong&gt;configuración inicial simplificada e inteligente&lt;/strong&gt; permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de &lt;em&gt;starters&lt;/em&gt; que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.&lt;/p&gt;
&lt;h1 id="objetivo"&gt;Objetivo&lt;/h1&gt;
&lt;p&gt;Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de &lt;em&gt;Hello World&lt;/em&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://spring.io/projects/spring-boot">Spring Boot</a> es en la actualidad un <strong>estándar en el desarrollo de aplicaciones Java</strong>, con mucho protagonismo en el mundo empresarial. Con una <strong>configuración inicial simplificada e inteligente</strong> permite levantar servicios en minutos. Incluye un servidor embebido y un ecosistema muy completo de <em>starters</em> que simplifican las dependencias necesarias para el desarrollo de muchos tipos de aplicaciones.</p>
<h1 id="objetivo">Objetivo</h1>
<p>Para ejemplificar esta simplicidad, se desarrolla un servicio web con un solo endpoint que devuelva un mensaje de <em>Hello World</em>.</p>
<h1 id="creación-del-proyecto-base">Creación del proyecto base</h1>
<p>El ecosistema Spring cuenta con una herramienta que facilita la creación de la estructura base del proyecto, se trata de <a href="https://start.spring.io/">Spring initializr</a>.</p>
<p>Ésta genera la estructura básica de un proyecto Spring Boot en segundos. Permite seleccionar la versión de Spring, el lenguaje, el <em>build system</em> (Maven o Gradle) y las dependencias necesarias, creando automáticamente un proyecto listo para abrir en cualquier IDE.</p>
<p>Para el <em>Hello World</em> solo es necesario incluir la dependencia de <code>Spring Web</code>.</p>
<p><img alt="Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr.png"></p>
<p>Al pulsar en <em>GENERATE</em> se descarga un fichero comprimido con el proyecto que cuenta con la estructura y componentes mínimos para importar en cualquier IDE y desplegar la aplicación.</p>
<p><img alt="Proyecto generado con Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-initializr-project.png"></p>
<p>Este proyecto se puede importar o abrir desde cualquier IDE que cuente con soporte para desarrollo de aplicaciones Spring Boot. Para el ejemplo, se utiliza IntelliJ.</p>
<p><img alt="Aplicación generada por Spring Initializr" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-app.png"></p>
<h1 id="creación-del-controlador">Creación del controlador</h1>
<p>Un controlador en Spring <strong>es el componente encargado de recibir las peticiones HTTP, procesarlas y devolver una respuesta</strong>. Actúa como punto de entrada de la aplicación, mapeando URLs y métodos HTTP (GET, POST, etc.) a métodos Java. Separa la lógica de exposición de la API del resto de la aplicación y su lógica, que se conformará con otro tipo de componentes que se utilizarán para obtener respuestas más complejas.</p>
<h2 id="anotaciones-restcontroller-y-getmapping">Anotaciones RestController y GetMapping</h2>
<p>Las anotaciones en Spring <strong>son etiquetas que se colocan sobre clases y métodos para decirle al framework qué papel cumple cada uno y cómo deben actuar</strong>, sin necesidad de escribir código adicional de configuración.</p>
<h3 id="restcontroller">RestController</h3>
<p>Es la anotación que marca una clase como <strong>un componente que expone endpoints REST</strong>. Todo lo que devuelvan sus métodos será serializado automáticamente (Strings, JSONs, etc.).</p>
<h3 id="getmapping">GetMapping</h3>
<p>Asocia un método a <strong>una petición HTTP GET y a una ruta concreta</strong> que se debe especificar como un parámetro de la propia anotación.</p>
<h2 id="ejemplo-completo">Ejemplo completo</h2>
<p>Para disponer de un endpoint bajo la ruta <code>/hello</code> que responda a una petición GET, bastaría con completar el Controlador <code>HelloController</code> de la siguiente forma:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-Java" data-lang="Java"><span class="line"><span class="cl"><span class="kn">package</span><span class="w"> </span><span class="nn">dev.juanfbermejo.SpringBootHelloWorld</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.GetMapping</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.RestController</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="nd">@RestController</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">HelloController</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">&#34;/hello&#34;</span><span class="p">)</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">hello</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="s">&#34;Hello World from Spring Boot!&#34;</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="prueba-de-la-aplicación">Prueba de la aplicación</h1>
<p>Para probar la aplicación y ver el <em>HelloWorld</em> bastaría con ejecutar en nuestro IDE la clase anotada con <code>SpringBootApplication</code>.</p>
<p>En la consola veremos aparecer varios logs que nos indicarán:</p>
<ul>
<li>Inicialización del servidor web (Tomcat por defecto) y el puerto en el que estará escuchando para recibir las peticiones (8080 por defecto)</li>
<li>Inicialización correcta de la propia aplicación.</li>
</ul>
<p>En ese momento, podremos abrir un navegador web e intentar cargar la url <code>http://localhost:8080/hello</code> lo cual realiza por defecto una petición GET a la ruta, devolviendo en la pantalla el texto de <em>HelloWorld</em>.</p>
<p><img alt="Hello World con Spring Boot en el navegador" loading="lazy" src="/posts/spring-boot-hello-world/images/spring-boot-hello.png"></p>
<h1 id="conclusiones">Conclusiones</h1>
<p>Montar un <em>Hello World</em> en Spring Boot es rápido y sencillo, y un buen punto de partida para entender las piezas que lo hacen posible. Las anotaciones son uno de los pilares del framework, y dominar su significado permite construir aplicaciones más limpias, expresivas y mantenibles.</p>
]]></content:encoded></item><item><title>Pasos para instalar Maven en Windows</title><link>/posts/instalar-maven-windows/</link><pubDate>Fri, 26 Dec 2025 17:10:36 +0100</pubDate><guid>/posts/instalar-maven-windows/</guid><description>&lt;h1 id="qué-es-maven"&gt;¿Qué es Maven?&lt;/h1&gt;
&lt;p&gt;Apache Maven es una &lt;strong&gt;herramienta de gestión y automatización de proyectos Java.&lt;/strong&gt; Su objetivo principal es &lt;strong&gt;simplificar ciertas tareas de desarrollo&lt;/strong&gt; como la compilación, la gestión de dependencias, ejecución de test y generación de artefactos (como pueden ser los archivos JAR).&lt;/p&gt;
&lt;p&gt;La configuración del proyecto se centraliza en el fichero &lt;code&gt;pom.xml&lt;/code&gt; (&lt;em&gt;Project Object Model&lt;/em&gt;) a partir de la cual, Maven se encarga de descargar librerías y ejecutar las tareas correspondientes.&lt;/p&gt;</description><content:encoded><![CDATA[<h1 id="qué-es-maven">¿Qué es Maven?</h1>
<p>Apache Maven es una <strong>herramienta de gestión y automatización de proyectos Java.</strong> Su objetivo principal es <strong>simplificar ciertas tareas de desarrollo</strong> como la compilación, la gestión de dependencias, ejecución de test y generación de artefactos (como pueden ser los archivos JAR).</p>
<p>La configuración del proyecto se centraliza en el fichero <code>pom.xml</code> (<em>Project Object Model</em>) a partir de la cual, Maven se encarga de descargar librerías y ejecutar las tareas correspondientes.</p>
<p>Maven se ha convertido en una herramienta esencial en el desarrollo de aplicaciones Java.</p>
<h1 id="instalación-de-maven-en-windows">Instalación de Maven en Windows</h1>
<p>Aunque Windows quizás no es el mejor sistema para utilizarlo en desarrollo de software, y la instalación de algunas herramientas implica cierta dificultad, en el caso de Maven basta con seguir un poco pasos para tenerlo funcionando.</p>
<h2 id="comprobación-previa-a-la-instalación">Comprobación previa a la instalación</h2>
<p>En primer lugar, se puede verificar si el equipo ya cuenta con una instalación de Maven correctamente configurada. Para ello, basta con abrir una consola de terminal y ejecutar el siguiente comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si no está instalado o configurado, obtendremos un mensaje que nos avisa de que el sistema no reconoce el comando:</p>
<p><img alt="Maven no está instalado" loading="lazy" src="/posts/instalar-maven-windows/images/maven-no-instalado.png"></p>
<h2 id="descarga-de-maven">Descarga de Maven</h2>
<p>Desde la web <a href="https://maven.apache.org/download.cgi">https://maven.apache.org/download.cgi</a> descargamos un &lsquo;<em>Binary zip archive</em>&rsquo;, que es un fichero zip que contiene lo necesario para ejecutar Maven.</p>
<p>Lo descomprimimos en una ruta de nuestro equipo donde queramos dejarlo instalado. Por ejemplo en  <code>C:\user\jfber\Maven\apache-maven-3.9.12</code></p>
<p><img alt="Carpeta de Maven en el sistema" loading="lazy" src="/posts/instalar-maven-windows/images/maven-instalacion.png"></p>
<h2 id="configuración-de-sistema">Configuración de sistema</h2>
<p>Ahora hay que configurar el sistema para que sepa donde localizar el ejecutable de Maven y que se pueda invocar desde cualquier ruta del sistema.</p>
<p>En primer lugar, hay que configurar una variable de entorno llamada <code>MAVEN_HOME</code> cuyo valor será la ruta completa de la carpeta de Maven creada en el paso anterior.</p>
<p>En el sistema Windows, en <code>Panel de control → Sistema → Editar las variables de entorno del Sistema</code> se crea la nueva variable:</p>
<p><img alt="Configurando la variable MAVEN_HOME" loading="lazy" src="/posts/instalar-maven-windows/images/maven-config-var.png"></p>
<p>Una vez configurada la variable que almacena la ruta donde se encuentra instalado Maven, se incluye la ruta del ejecutable en la variable <code>Path</code> de forma que se pueda ejecutar Maven desde cualquier ubicación del sistema.</p>
<p>En la variable <code>Path</code> se incluye el siguiente valor, que hace uso de la variable declarada anteriormente:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">%MAVEN_HOME%\bin</span></span></code></pre></td></tr></table>
</div>
</div>
<p><img alt="Incluyendo Maven en el path" loading="lazy" src="/posts/instalar-maven-windows/images/maven-config-path.png"></p>
<h2 id="comprobación-de-la-instalación">Comprobación de la instalación</h2>
<p>Una vez completada la configuración, se debe reiniciar la terminal y se vuelve a lanzar el comando:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">mvn -version</span></span></code></pre></td></tr></table>
</div>
</div>
<p>Si todo está correcto, ahora muestra la información sobre la versión de Maven instalada y el sistema está listo para comenzar a usar la herramienta.</p>
<p><img alt="Maven funcionando correctamente" loading="lazy" src="/posts/instalar-maven-windows/images/maven-instalado.png"></p>
]]></content:encoded></item><item><title>Sobrecarga de constructores con métodos de factoría</title><link>/posts/constructores-metodos-factoria/</link><pubDate>Sun, 30 Nov 2025 14:04:49 +0100</pubDate><guid>/posts/constructores-metodos-factoria/</guid><description>&lt;p&gt;Cuando hablamos de &lt;code&gt;Clean Code&lt;/code&gt;, una de las reglas básicas es &lt;strong&gt;utilizar nombres descriptivos que declaren la intención&lt;/strong&gt;. Esto es trivial en métodos y variables, pero los constructores tienen un nombre fijo y único, y cuando empiezan a aparecer sobrecargas, esa imposibilidad de nombrarlos de forma expresiva &lt;strong&gt;penaliza la claridad y legibilidad del código&lt;/strong&gt;. Es ahí donde &lt;a href="https://es.wikipedia.org/wiki/Robert_C._Martin" target="_blank" rel="noopener noreferrer"&gt;Robert C. Martin&lt;/a&gt; sugiere en su libro &lt;a href="https://archive.org/details/cleancodehandboo0000unse" target="_blank" rel="noopener noreferrer"&gt;Clean Code&lt;/a&gt; el uso de &lt;strong&gt;métodos de factoría y constructores privados&lt;/strong&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Cuando hablamos de <code>Clean Code</code>, una de las reglas básicas es <strong>utilizar nombres descriptivos que declaren la intención</strong>. Esto es trivial en métodos y variables, pero los constructores tienen un nombre fijo y único, y cuando empiezan a aparecer sobrecargas, esa imposibilidad de nombrarlos de forma expresiva <strong>penaliza la claridad y legibilidad del código</strong>. Es ahí donde <a href="https://es.wikipedia.org/wiki/Robert_C._Martin" target="_blank" rel="noopener noreferrer">Robert C. Martin</a> sugiere en su libro <a href="https://archive.org/details/cleancodehandboo0000unse" target="_blank" rel="noopener noreferrer">Clean Code</a> el uso de <strong>métodos de factoría y constructores privados</strong>.</p>
<h2 id="el-problema-con-la-sobrecarga-de-constructores">El problema con la sobrecarga de constructores</h2>
<p>A priori, la sobrecarga de constructores parece una solución práctica que nos permite declarar tantos como necesitemos y haciendo cualquier combinación de atributos, según las necesidades del código.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">new</span><span class="w"> </span><span class="n">User</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">new</span><span class="w"> </span><span class="n">User</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">35</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">new</span><span class="w"> </span><span class="n">User</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">35</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">);</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Pero esta sobrecarga escala muy mal y penaliza la legibilidad y comprensión. No sabemos qué representa cada parámetro, son ambiguos y amplían la posibilidad de error aunque el programador invierta tiempo en repasar su definición antes de cada uso.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">User</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">User</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">User</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">isAdmin</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="nf">User</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">,</span><span class="w"> </span><span class="kt">boolean</span><span class="w"> </span><span class="n">isAdmin</span><span class="p">,</span><span class="w"> </span><span class="n">LocalDate</span><span class="w"> </span><span class="n">createdAt</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">...</span><span class="w"> </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="uso-de-métodos-factoría">Uso de métodos factoría</h2>
<p>Los métodos de factoría (<em>factory methods</em>) son métodos estáticos con nombre, ayudan a resolver esta ambigüedad y expresar y definir de manera más clara qué se está creando. Para evitar totalmente la ambigüedad, los constructores pueden ser privados.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">class</span> <span class="nc">User</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">private</span><span class="w"> </span><span class="nf">User</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">name</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">name</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">this</span><span class="p">.</span><span class="na">age</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">age</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="nf">withName</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">0</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="kd">public</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="n">User</span><span class="w"> </span><span class="nf">withNameAndAge</span><span class="p">(</span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">age</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">User</span><span class="p">(</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="n">age</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">User</span><span class="w"> </span><span class="n">u1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">User</span><span class="p">.</span><span class="na">withName</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">User</span><span class="w"> </span><span class="n">u2</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">User</span><span class="p">.</span><span class="na">withNameAndAge</span><span class="p">(</span><span class="s">&#34;Juan&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">35</span><span class="p">);</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h3 id="ventajas-clave">Ventajas clave</h3>
<ol>
<li><strong>Claridad</strong>: los nombres expresan la intención.</li>
<li><strong>Flexibilidad</strong>: se pueden devolver subtipos, instancias cacheadas, objetos inmutables, etc.</li>
<li><strong>Ocultan la complejidad</strong>: se centraliza la lógica de creación en un único punto.</li>
<li><strong>Evolución más sencilla</strong>: se puede ampliar el comportamiento sin romper constructores existentes.</li>
</ol>
<h2 id="cuando-es-mejor-usar-un-builder">Cuando es mejor usar un Builder</h2>
<p>Si la clase cuenta con muchos parámetros, y/o muchos de ellos son opcionales y, además, queremos incluir una lógica de validación compleja y configuraciones combinatorias (valores de un atributo condicionados al valor de otro), los <code>Builder</code> pasan a ser la solución más potente e indicada.</p>
<h2 id="conclusiones">Conclusiones</h2>
<p>Para casos simples, un constructor normal está bien. Pero cuando tenemos varias formas de crear el mismo objeto o una lógica de inicialización con cierta complejidad, <strong>el uso de métodos de factoría o Builders se posicionan como una solución más clara</strong>. El objetivo final es que el código sea <strong>fácil de leer, más difícil de romper y más sencillo de extender</strong>.</p>
]]></content:encoded></item><item><title>Nuevo switch expression de Java. Menos ruido, más claridad</title><link>/posts/nuevo-switch-expression-java/</link><pubDate>Fri, 21 Nov 2025 21:17:34 +0100</pubDate><guid>/posts/nuevo-switch-expression-java/</guid><description>&lt;p&gt;Aunque realmente las nuevas &lt;code&gt;switch expressions&lt;/code&gt; no son una novedad reciente, ya que están disponibles desde la versión 14 de Java, es otra de esas mejoras que pasan desapercibidas aunque, con un pequeño cambio de sintaxis, proporcionan una versión moderna, limpia, segura y expresiva de los tradicionales &lt;code&gt;switch&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id="qué-aporta-el-switch-moderno"&gt;¿Qué aporta el switch moderno?&lt;/h1&gt;
&lt;h2 id="1-es-una-expresión-no-solo-una-sentencia"&gt;1. Es una expresión, no solo una sentencia&lt;/h2&gt;
&lt;p&gt;El nuevo &lt;code&gt;switch&lt;/code&gt; puede devolver valores directamente a una variable haciendo uso del operador &lt;code&gt;flecha&lt;/code&gt;.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Aunque realmente las nuevas <code>switch expressions</code> no son una novedad reciente, ya que están disponibles desde la versión 14 de Java, es otra de esas mejoras que pasan desapercibidas aunque, con un pequeño cambio de sintaxis, proporcionan una versión moderna, limpia, segura y expresiva de los tradicionales <code>switch</code>.</p>
<h1 id="qué-aporta-el-switch-moderno">¿Qué aporta el switch moderno?</h1>
<h2 id="1-es-una-expresión-no-solo-una-sentencia">1. Es una expresión, no solo una sentencia</h2>
<p>El nuevo <code>switch</code> puede devolver valores directamente a una variable haciendo uso del operador <code>flecha</code>.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">dayOfWeek</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">1</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Monday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">2</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Tuesday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">3</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Wednesday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">4</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Thursday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">5</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Friday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">6</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Saturday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">7</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Sunday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">default</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Invalid day&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="2-evita-el-fall-through">2. Evita el &lsquo;fall-through&rsquo;</h2>
<p>En los tradicionales <code>switch</code>, si te olvidabas un <code>break</code> provocaba la ejecución de varios <code>case</code>. Ahora ya no son necesarios, eliminado verbosidad y aportando robustez y permitiendo igualmente agrupar casos.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">dayType</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">dayOfWeek</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">1</span><span class="p">,</span><span class="w"> </span><span class="n">2</span><span class="p">,</span><span class="w"> </span><span class="n">3</span><span class="p">,</span><span class="w"> </span><span class="n">4</span><span class="p">,</span><span class="w"> </span><span class="n">5</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Weekday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">6</span><span class="p">,</span><span class="w"> </span><span class="n">7</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Weekend&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">default</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="s">&#34;Invalid&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="3-menos-código-y-más-claro">3. Menos código y más claro</h2>
<p>En el <code>switch clásico</code>, había que declarar la variable, abrir el <code>switch</code>, asignar valor en cada <code>case</code>, acordarte de los <code>break</code> y cerrar la expresión correctamente.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span><span class="lnt">18
</span><span class="lnt">19
</span><span class="lnt">20
</span><span class="lnt">21
</span><span class="lnt">22
</span><span class="lnt">23
</span><span class="lnt">24
</span><span class="lnt">25
</span><span class="lnt">26
</span><span class="lnt">27
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="n">String</span><span class="w"> </span><span class="n">day</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">dayOfWeek</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">1</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Monday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">2</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Tuesday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">3</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Wednesday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">4</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Thursday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">5</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Friday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">6</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Saturday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">7</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Sunday&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">break</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">default</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">day</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">&#34;Invalid day&#34;</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="4-uso-de-yield-para-casos-más-complejos">4. Uso de &lsquo;yield&rsquo; para casos más complejos</h2>
<p>Se trata de un nuevo operador, un <code>return</code> que sólo existe dentro de las nuevas <code>switch expressions</code>, y que no finaliza la ejecución del método. Permite desarrollar un <code>case</code> más complejo y devolver un valor en el mismo.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kt">double</span><span class="w"> </span><span class="n">price</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">customerType</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">STANDARD</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">basePrice</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">PREMIUM</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kt">double</span><span class="w"> </span><span class="n">discount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">basePrice</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">10</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">discount</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">30</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;Aplicando tope de descuento&#34;</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">discount</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">30</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="kd">yield</span><span class="w"> </span><span class="n">basePrice</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">discount</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">STUDENT</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">basePrice</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">85</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">default</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">basePrice</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">};</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="5-switch-más-exhaustivos-usando-enums">5. Switch más exhaustivos usando Enums</h2>
<p>Otra mejora interesante, disponible desde Java 17, es el uso de <code>Enums</code> para definir los <code>case</code>. De esta forma el desarrollador está obligado a usar todos sus valores, obteniendo un aviso del compilador en caso contrario.</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt"> 1
</span><span class="lnt"> 2
</span><span class="lnt"> 3
</span><span class="lnt"> 4
</span><span class="lnt"> 5
</span><span class="lnt"> 6
</span><span class="lnt"> 7
</span><span class="lnt"> 8
</span><span class="lnt"> 9
</span><span class="lnt">10
</span><span class="lnt">11
</span><span class="lnt">12
</span><span class="lnt">13
</span><span class="lnt">14
</span><span class="lnt">15
</span><span class="lnt">16
</span><span class="lnt">17
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="kd">enum</span><span class="w"> </span><span class="n">CustomerType</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="n">REGULAR</span><span class="p">,</span><span class="w"> </span><span class="n">SILVER</span><span class="p">,</span><span class="w"> </span><span class="n">GOLD</span><span class="p">,</span><span class="w"> </span><span class="n">PLATINUM</span><span class="p">,</span><span class="w"> </span><span class="n">DIAMOND</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kd">public</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="nf">calculateDiscount</span><span class="p">(</span><span class="n">CustomerType</span><span class="w"> </span><span class="n">type</span><span class="p">,</span><span class="w"> </span><span class="kt">double</span><span class="w"> </span><span class="n">amount</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="k">switch</span><span class="w"> </span><span class="p">(</span><span class="n">type</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">REGULAR</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">0</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">SILVER</span><span class="w">  </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">05</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">GOLD</span><span class="w">    </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">10</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="k">case</span><span class="w"> </span><span class="n">PLATINUM</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">{</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">&#34;Aplicando descuento especial&#34;</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="kt">double</span><span class="w"> </span><span class="n">bonus</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">1000</span><span class="w"> </span><span class="o">?</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">02</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">0</span><span class="p">.</span><span class="na">0</span><span class="p">;</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">            </span><span class="kd">yield</span><span class="w"> </span><span class="n">amount</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">0</span><span class="p">.</span><span class="na">15</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">bonus</span><span class="p">);</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="p">}</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">};</span><span class="w">  
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h1 id="conclusiones">Conclusiones</h1>
<p>Las nuevas <code>switch expressions</code> no cambian la forma de pensar en condiciones, pero sí nos permite expresarlas con menos ruido y más seguridad. Es una pieza pequeña, pero encaja muy bien en el Java moderno: más conciso, más claro y más difícil de romper por descuido.</p>
]]></content:encoded></item><item><title>Comparar Strings en Java evitando excepciones</title><link>/posts/comparar-strings-java-excep/</link><pubDate>Mon, 10 Nov 2025 19:35:29 +0100</pubDate><guid>/posts/comparar-strings-java-excep/</guid><description>&lt;p&gt;Una de las primeras lecciones que aprendemos cuando empezamos con Java es que &amp;ldquo;en Java, todo son objetos&amp;rdquo;. Por eso, a la hora de comparar dos Strings, no debemos hacerlo con el operador &lt;code&gt;==&lt;/code&gt;, ya que estaríamos comparando sus referencias de memoria y no su contenido.&lt;/p&gt;
&lt;p&gt;Pero otro problema relacionado con la comparación de Strings puede surgir al usar la función &lt;code&gt;equals&lt;/code&gt; y que, aunque sea menos evidente, puede provocar errores difíciles de detectar.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Una de las primeras lecciones que aprendemos cuando empezamos con Java es que &ldquo;en Java, todo son objetos&rdquo;. Por eso, a la hora de comparar dos Strings, no debemos hacerlo con el operador <code>==</code>, ya que estaríamos comparando sus referencias de memoria y no su contenido.</p>
<p>Pero otro problema relacionado con la comparación de Strings puede surgir al usar la función <code>equals</code> y que, aunque sea menos evidente, puede provocar errores difíciles de detectar.</p>
<p>La mayoría de desarrolladores tenderíamos a montar la comparación en el siguiente orden (quizás porque nos centramos en nuestra variable), lo cual, si en algún momento <code>diaSemana</code> es nulo, genera una <code>NullPointerException</code>:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">diaSemana</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="s">&#34;SUNDAY&#34;</span><span class="p">)</span><span class="w"> </span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">hacemosAlgo</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Para evitarlo, basta con invertir el orden, ya que <code>SUNDAY</code> nunca será nulo:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="s">&#34;SUNDAY&#34;</span><span class="p">.</span><span class="na">equals</span><span class="p">(</span><span class="n">diaSemana</span><span class="p">)</span><span class="w"> </span><span class="p">){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">hacemosAlgo</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<h2 id="por-qué-este-detalle-importa-en-proyectos-grandes">¿Por qué este detalle importa en proyectos grandes?</h2>
<p>Imaginemos que en nuestro desarrollo original, el valor de la variable <code>diaSemana</code> lo obtenemos mediante algún mecanismo que nos garantice que en ningún caso sea nulo. Por ejemplo:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">obtenerDiaSemana</span><span class="p">(){</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="n">LocalDate</span><span class="p">.</span><span class="na">now</span><span class="p">().</span><span class="na">getDayOfWeek</span><span class="p">().</span><span class="na">name</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>Por lo que la primera implementación de la comparativa de la variable con la cadena <code>SUNDAY</code> funcionaría correctamente y nuestro desarrollo iría a producción pasando los test.</p>
<p>Pero también podría pasar, que en una aplicación grande y donde tocan varios equipos, se realiza un desarrollo nuevo que podría consistir en que un usuario puede configurar en su perfil con una zona horaria específica.</p>
<p>De esta forma, ese segundo equipo cambia la implementación de la función <code>obtenerDiaSemana</code> para hacerlo de la siguiente manera (asumiendo que el contexto es la sesión en la aplicación):</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">obtenerDiaSemana</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">Usuario</span><span class="w"> </span><span class="n">usuario</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">contexto</span><span class="p">.</span><span class="na">getUsuarioActual</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="n">ZonaHoraria</span><span class="w"> </span><span class="n">zona</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">usuario</span><span class="p">.</span><span class="na">getPreferencias</span><span class="p">().</span><span class="na">getZonaHoraria</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">	</span><span class="k">return</span><span class="w"> </span><span class="n">LocalDate</span><span class="p">.</span><span class="na">now</span><span class="p">(</span><span class="n">zona</span><span class="p">).</span><span class="na">getDayOfWeek</span><span class="p">().</span><span class="na">name</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="p">}</span></span></span></code></pre></td></tr></table>
</div>
</div>
<p>De esta forma, una implementación determinista y segura, &ldquo;imposible de romper&rdquo;, pasa a depender de otros factores que podrían desencadenar en que la variable <code>diaSemana</code> en el punto en que se compara con un valor, pueda ser nula.</p>
<p>Aunque el segundo equipo podría haber detectado el fallo en pruebas, el primero habría evitado el problema desde el principio escribiendo código más robusto. Un pequeño detalle en apariencia, pero de los que separan código que funciona de código que sobrevive.</p>
]]></content:encoded></item></channel></rss>