Cuando los desarrolladores comienzan a estudiar Arquitectura Hexagonal, suelen comprender rápidamente conceptos como dominio, puertos y adaptadores. Sin embargo, existe un patrón que aparece una y otra vez en prácticamente todas las implementaciones exitosas: el Repository.
A pesar de su popularidad, el patrón Repository suele ser malinterpretado. En algunos proyectos se convierte en una simple envoltura alrededor de Entity Framework Core. En otros, termina transformándose en una capa genérica que agrega complejidad sin aportar valor real.
La intención original del patrón es mucho más importante. Repository existe para proteger al dominio de los detalles de persistencia y permitir que la lógica de negocio evolucione independientemente de la tecnología utilizada para almacenar datos.
En un mundo donde los agentes de IA generan código constantemente, esta separación se vuelve aún más relevante. Cuanto más claras sean las fronteras entre negocio e infraestructura, más fácil será mantener la calidad arquitectónica del sistema.
El problema que Repository intenta resolver
Imaginemos una aplicación ASP.NET Core donde un servicio de negocio accede directamente a Entity Framework Core.
public class OrderService
{
private readonly ApplicationDbContext _dbContext;
public OrderService(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task CreateOrder(Order order)
{
_dbContext.Orders.Add(order);
await _dbContext.SaveChangesAsync();
}
}
Este enfoque funciona perfectamente al inicio. La implementación es simple y fácil de comprender. El problema aparece cuando el sistema comienza a crecer.
Ahora el dominio depende directamente de Entity Framework Core. Si decidimos migrar a otro mecanismo de persistencia, la lógica de negocio deberá modificarse. Si queremos ejecutar pruebas aisladas, necesitaremos simular componentes específicos de la infraestructura. Si aparece un nuevo requisito tecnológico, el impacto puede extenderse por múltiples capas del sistema.
En este escenario, la base de datos deja de ser un detalle técnico y comienza a influir directamente sobre el diseño del negocio.
Eso es precisamente lo que la Arquitectura Hexagonal intenta evitar.
La idea fundamental detrás de Repository
El patrón Repository propone una separación sencilla: el dominio no debería saber cómo se almacenan los datos. Su única preocupación debería ser acceder y persistir entidades mediante contratos bien definidos.
Desde la perspectiva del dominio, obtener un cliente o guardar una orden son operaciones del negocio. Los detalles sobre SQL Server, PostgreSQL, MongoDB o cualquier otra tecnología son responsabilidades de infraestructura.
Por esa razón, el dominio define una interfaz:
public interface IOrderRepository
{
Task<Order?> GetByIdAsync(OrderId id);
Task SaveAsync(Order order);
}
Este contrato representa un puerto dentro de la Arquitectura Hexagonal. El dominio expresa qué necesita, pero no cómo debe implementarse.
La infraestructura implementa el contrato
Una vez definido el puerto, la infraestructura puede proporcionar una implementación concreta.
Por ejemplo:
public class EfOrderRepository : IOrderRepository
{
private readonly ApplicationDbContext _dbContext;
public EfOrderRepository(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task<Order?> GetByIdAsync(OrderId id)
{
return await _dbContext.Orders.FindAsync(id.Value);
}
public async Task SaveAsync(Order order)
{
_dbContext.Update(order);
await _dbContext.SaveChangesAsync();
}
}
La lógica de negocio continúa utilizando únicamente la interfaz. La implementación concreta queda encapsulada dentro de la infraestructura.
Esto significa que el dominio nunca necesita conocer Entity Framework Core, DbContext, consultas SQL o detalles específicos del proveedor de datos.
El verdadero beneficio: cambiar la persistencia sin tocar el negocio
Muchos desarrolladores escuchan la clásica frase "podrás cambiar de base de datos fácilmente" y concluyen que se trata de un caso poco frecuente.
Y tienen razón. La mayoría de los proyectos no cambian de motor de base de datos cada seis meses.
Sin embargo, el verdadero valor del Repository no está en la migración en sí misma. El valor está en que la lógica del negocio permanece aislada de cualquier decisión tecnológica.
Supongamos que una organización decide migrar parte de sus servicios desde SQL Server hacia PostgreSQL por motivos de costo o estrategia tecnológica.
Si el dominio depende directamente de Entity Framework Core y contiene consultas específicas, el esfuerzo de migración puede ser considerable.
Si la persistencia está encapsulada detrás de repositorios, el impacto se concentra principalmente en la infraestructura.
La lógica del negocio continúa funcionando exactamente igual.
Repository como guardarraíl para la IA
Uno de los aspectos más interesantes del patrón Repository en la era de la IA es que actúa como un mecanismo de protección arquitectónica.
Cuando un agente recibe instrucciones ambiguas, suele mezclar responsabilidades. Es común encontrar implementaciones donde el acceso a datos aparece directamente dentro de controladores, servicios de aplicación o incluso entidades de dominio.
Por ejemplo:
public async Task ApproveOrder(Guid id)
{
var order = await _dbContext.Orders.FindAsync(id);
order.Approve();
await _dbContext.SaveChangesAsync();
}
Aunque el código funciona, introduce una dependencia directa entre la lógica del negocio y la infraestructura.
Cuando existe un contrato explícito mediante Repository, el agente tiene menos libertad para romper las reglas arquitectónicas.
El flujo esperado se vuelve evidente:
Caso de Uso
↓
Repository
↓
Infraestructura
La arquitectura proporciona límites claros y reduce la probabilidad de generar código acoplado.
Los errores más comunes al implementar Repository
A pesar de ser un patrón ampliamente conocido, existen varios errores que aparecen con frecuencia.
Crear repositorios genéricos para todo
Es habitual encontrar implementaciones como esta:
public interface IRepository<T>
{
Task Add(T entity);
Task Update(T entity);
Task Delete(T entity);
}
Aunque parece una solución elegante, suele terminar ocultando necesidades específicas del dominio.
Los repositorios deberían expresar operaciones significativas para el negocio y no únicamente operaciones CRUD genéricas.
Filtrar entidades mediante IQueryable
Otro error frecuente consiste en exponer IQueryable desde los repositorios.
IQueryable<Order> GetOrders();
Esto permite que las consultas se construyan fuera del repositorio, filtrando detalles de persistencia hacia otras capas.
En la práctica, el dominio vuelve a quedar acoplado a la tecnología de acceso a datos.
Convertir el repositorio en una copia de DbContext
Si cada método del repositorio refleja exactamente un método de Entity Framework Core, probablemente el patrón no esté aportando ningún beneficio real.
El objetivo no es esconder DbContext detrás de otra clase. El objetivo es proteger al dominio de los detalles de persistencia.
Un ejemplo práctico en Arquitectura Hexagonal
Imaginemos un caso de uso para aprobar una orden.
La lógica de aplicación podría verse así:
public class ApproveOrderUseCase
{
private readonly IOrderRepository _repository;
public ApproveOrderUseCase(IOrderRepository repository)
{
_repository = repository;
}
public async Task Execute(OrderId id)
{
var order = await _repository.GetByIdAsync(id);
order.Approve();
await _repository.SaveAsync(order);
}
}
Observemos que el caso de uso no sabe si los datos provienen de SQL Server, PostgreSQL, MongoDB o una API externa.
Su única preocupación es aplicar reglas del negocio.
Eso es exactamente lo que buscamos en una arquitectura bien desacoplada.
Cómo ayuda Repository cuando trabajamos con IA
Los agentes generan mejores resultados cuando existen contratos claros. Un repositorio bien diseñado reduce la cantidad de decisiones ambiguas y proporciona una estructura predecible para el desarrollo.
Cuando una IA recibe instrucciones como:
Implementa un nuevo caso de uso respetando
Arquitectura Hexagonal y utilizando IOrderRepository
para acceder a la persistencia.
Las probabilidades de obtener una implementación consistente aumentan considerablemente.
El agente comprende dónde debe vivir la lógica, dónde debe acceder a los datos y cuáles son los límites que no debe cruzar.
La arquitectura deja de depender exclusivamente de revisiones manuales y comienza a formar parte del propio proceso de generación.
Conclusión
El patrón Repository no existe para ocultar una base de datos ni para crear capas adicionales de abstracción. Su propósito principal es proteger al dominio de los detalles tecnológicos que inevitablemente cambiarán con el tiempo.
Al definir contratos claros entre negocio e infraestructura, Repository permite que las reglas del dominio permanezcan estables mientras la tecnología evoluciona alrededor de ellas.
Esta separación siempre ha sido valiosa, pero adquiere una importancia especial en la era de la inteligencia artificial. Los agentes generan código con gran velocidad, pero necesitan límites claros para producir soluciones sostenibles.
Un repositorio bien diseñado actúa como uno de esos límites. Ayuda a preservar la arquitectura, mantiene el dominio independiente y proporciona los guardarraíles necesarios para que tanto desarrolladores como agentes puedan trabajar de forma consistente a largo plazo.
No comments:
Post a Comment