<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Dotnet on Patrones — Exeal</title><link>https://patterns.exeal.com/tags/dotnet/</link><description>Recent content in Dotnet on Patrones — Exeal</description><generator>Hugo</generator><language>es-ES</language><lastBuildDate>Thu, 24 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://patterns.exeal.com/tags/dotnet/index.xml" rel="self" type="application/rss+xml"/><item><title>API Lite: un segundo host con infraestructura en memoria</title><link>https://patterns.exeal.com/patterns/api-lite/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/api-lite/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Un frontend (o una suite de Playwright) que depende del backend real para poder trabajar arrastra todos los problemas del backend real: hay que levantar Postgres, colas, un proveedor OIDC, a veces Hangfire o un bucket S3, y el estado se acumula entre ejecuciones. Para tocar UI esto es fricción pura — nadie necesita persistencia real para ver si un botón pinta bien. Para tests de extremo a extremo es peor: los tests dejan de ser deterministas (dependen de datos que quedaron de ejecuciones anteriores, de latencia real contra servicios externos, de que Postgres/Hangfire arranquen a tiempo) y se vuelven lentos de levantar y frágiles de mantener.&lt;/p&gt;</description></item><item><title>Arquitectura hexagonal (puertos y adaptadores) en .NET</title><link>https://patterns.exeal.com/patterns/arquitectura-hexagonal-puertos-y-adaptadores/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/arquitectura-hexagonal-puertos-y-adaptadores/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Cuando la lógica de aplicación y la infraestructura (base de datos, storage, proveedores externos) conviven en el mismo proyecto, es fácil que un caso de uso acabe llamando directamente a un &lt;code&gt;DbContext&lt;/code&gt;, un cliente HTTP o &lt;code&gt;HttpContext&lt;/code&gt;. El código compila, funciona, pero deja de ser unit-testable: cualquier test de un caso de uso arrastra un contenedor de base de datos o un mock del framework web. Con el tiempo esto también acopla el dominio a decisiones de infraestructura que deberían poder cambiar solas — cambiar de ORM, servir el mismo core desde dos hosts distintos (uno con infraestructura real y otro en memoria para tests de frontend), o simplemente entender qué necesita realmente un caso de uso del mundo exterior.&lt;/p&gt;</description></item><item><title>CLI de seed de escenarios de prueba contra base de datos local</title><link>https://patterns.exeal.com/patterns/cli-de-seed-de-escenarios-contra-postgres-local/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/cli-de-seed-de-escenarios-contra-postgres-local/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Un sistema con varias máquinas de estado (un pedido, un cobro, una verificación de identidad&amp;hellip;) tiene decenas de combinaciones de estado posibles, y muchas de ellas dependen de integraciones externas (una pasarela de pago, un proveedor de scoring, un servicio de verificación) que tardan, cuestan o simplemente no se pueden disparar a demanda en local. Para llegar a probar a mano un caso concreto — por ejemplo, un pedido rechazado por scoring, o uno con la verificación de dirección fallida — habría que arrastrar un pedido real por todo el flujo: crearlo, esperar la respuesta del proveedor de turno, forzar el caso concreto que se quiere ver.&lt;/p&gt;</description></item><item><title>Comandos de mantenimiento en apps .NET</title><link>https://patterns.exeal.com/patterns/comando-mantenimiento-autodescubierto/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/comando-mantenimiento-autodescubierto/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Tarde o temprano toda API con un composition root no trivial (DI container, configuración por entorno, logging estructurado) necesita ejecutar una tarea de mantenimiento puntual: un backfill, recalcular una agregación, purgar registros huérfanos, deduplicar datos tras un bug. Esto no es solo leer o escribir filas: a menudo hay que invocar los mismos servicios de dominio que la app usa para garantizar sus invariantes (un agregador, un motor de fusión de duplicados, un validador), en vez de reimplementar su lógica en un script.&lt;/p&gt;</description></item><item><title>Gestión de errores explícita con un Result tipado</title><link>https://patterns.exeal.com/patterns/result-tipado/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/result-tipado/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;En un caso de uso hay dos tipos de &amp;ldquo;fallo&amp;rdquo; completamente distintos, y usar excepciones para ambos los mezcla en el mismo mecanismo: &amp;ldquo;crédito denegado&amp;rdquo; o &amp;ldquo;el email ya está registrado&amp;rdquo; son resultados esperados del dominio — pasan en producción todos los días y el código que llama al caso de uso tiene que decidir qué hacer con ellos — mientras que &amp;ldquo;la base de datos no responde&amp;rdquo; o &amp;ldquo;el proveedor de scoring ha dado timeout&amp;rdquo; son fallos de infraestructura que no le competen al dominio decidir, solo propagar para que alguien reintente o salte una alerta.&lt;/p&gt;</description></item><item><title>Host fino y módulos deep</title><link>https://patterns.exeal.com/patterns/host-fino-y-modulos-deep/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/host-fino-y-modulos-deep/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;A medida que una aplicación crece, es fácil que todo acabe en un único proyecto (o en una pila de proyectos sin fronteras reales) donde cualquier clase puede llamar a cualquier otra. El resultado son dos problemas que se retroalimentan:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;No se puede razonar ni testear una pieza en aislamiento.&lt;/strong&gt; Para validar la lógica de un caso de uso hace falta levantar medio sistema, porque nada impide que esa lógica dependa de detalles internos de otra parte de la aplicación.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Una IA (o una persona nueva) que trabaja sobre una funcionalidad concreta necesita cargar contexto de todo el sistema&lt;/strong&gt; para saber qué es seguro tocar y qué no, porque la superficie pública y la implementación interna son indistinguibles a simple vista.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Esto se agrava en aplicaciones con piezas muy distintas entre sí: un listener de webhooks, una API administrativa, lógica de negocio con jobs en background. Cada una tiene su propia complejidad interna, pero todas acaban compartiendo el mismo espacio de nombres sin fronteras que el compilador conozca.&lt;/p&gt;</description></item><item><title>Snapshot de contrato OpenAPI aceptado explícitamente</title><link>https://patterns.exeal.com/patterns/snapshot-contrato-openapi/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/snapshot-contrato-openapi/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Cuando el contrato HTTP de una API se genera automáticamente a partir del código (controladores, DTOs, atributos), nadie lo &amp;ldquo;escribe&amp;rdquo; a mano — y por eso nadie lo revisa a propósito tampoco. Un refactor interno que renombra un campo, cambia su tipo, o hace opcional algo que antes era obligatorio, cambia el contrato público como efecto secundario silencioso: compila, los tests unitarios pasan, y el PR se fusiona sin que nadie haya mirado el swagger. El consumidor de esa API (un frontend, otro servicio, un cliente externo) se entera cuando algo se rompe en producción, no en la revisión del cambio.&lt;/p&gt;</description></item><item><title>Tests de aceptación de backend sobre una imagen Docker ya construida</title><link>https://patterns.exeal.com/patterns/tests-aceptacion-sobre-imagen-docker/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/tests-aceptacion-sobre-imagen-docker/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Un backend .NET no se despliega como código fuente: se despliega como imagen Docker. Entre el código que pasa los tests unitarios y la imagen que corre en producción hay una capa entera que ningún test contra el código fuente toca — el &lt;code&gt;Dockerfile&lt;/code&gt;, las variables de entorno que el contenedor espera recibir, si el puerto publicado es el correcto, si las migraciones corren al arrancar, si el healthcheck responde. Un &lt;code&gt;WebApplicationFactory&lt;/code&gt; o un test de integración que arranca la app in-process compila contra las mismas clases que ya se probaron, así que no puede detectar ninguno de esos fallos — están todos fuera del código, en el empaquetado.&lt;/p&gt;</description></item><item><title>Tests de persistencia del repositorio real</title><link>https://patterns.exeal.com/patterns/tests-de-persistencia-del-repositorio-real/</link><pubDate>Thu, 24 Sep 2026 00:00:00 +0000</pubDate><guid>https://patterns.exeal.com/patterns/tests-de-persistencia-del-repositorio-real/</guid><description>&lt;h2 id="problema"&gt;Problema&lt;/h2&gt;&#10;&lt;p&gt;Con el patrón Repositorio, la lógica de negocio se testea contra una implementación fake en memoria del repositorio: rápida, sin dependencias externas, perfecta para cubrir reglas de negocio con muchos casos. El problema es que esos tests solo prueban la lógica de negocio — nunca ejercitan la implementación real del repositorio, la que de verdad habla con la base de datos en producción.&lt;/p&gt;&#10;&lt;p&gt;Eso deja un hueco concreto: una query LINQ que EF Core no puede traducir a SQL y falla en tiempo de ejecución, un &lt;code&gt;GroupBy&lt;/code&gt;/&lt;code&gt;Join&lt;/code&gt;/&lt;code&gt;Distinct&lt;/code&gt; que en memoria cuenta bien porque ejecuta el mismo predicado C# pero en SQL agrupa distinto, una constraint única que en producción vive en el esquema de la base de datos pero que el fake in-memory no reproduce porque no impone ninguna unicidad, una condición de carrera que solo se resuelve con un &lt;code&gt;INSERT ... ON CONFLICT&lt;/code&gt; real. Nada de esto puede fallar en un test contra el fake, porque el fake y la implementación real solo comparten la interfaz — su comportamiento interno es completamente distinto. El fake existe justamente para no pagar el coste de hablar con una base de datos real en cada test de negocio, así que por construcción no puede detectar estos fallos.&lt;/p&gt;</description></item></channel></rss>