Skip to content

Repository Pattern in DDD: When NOT to Use It

¿Qué es el Patrón de Repositorio?

El patrón de repositorio actúa como un intermediario entre la capa de dominio y la capa de datos, encapsulando la lógica de acceso a los datos y las operaciones realizadas sobre ellos. Esto permite que la lógica del dominio permanezca agnóstica respecto a los mecanismos de persistencia, como bases de datos o servicios externos.

Implementación del Patrón de Repositorio

Un repositorio debe corresponder a una raíz de agregado específico en tu modelo de dominio. Aquí hay algunas recomendaciones para implementarlo: Enfoque en el Agregado: Cada repositorio debe manejar un solo agregado, asegurando así que las reglas de negocio se mantengan intactas

1.- Interfaz Clara: Las interfaces de los repositorios deben centrarse en operaciones esenciales (agregar, eliminar, buscar), evitando complejidades innecesarias

2.- Métodos Específicos del Dominio: En lugar de exponer operaciones crudas de base de datos, los métodos deben reflejar las operaciones del dominio, como findByUserId o addCommentToPost

Quick Reminder – What Is a Repository?

  • Manages Aggregate Roots from the domain
  • Exposes only minimal persistence/query operations (findById, save, etc.)
  • Hides infrastructure details from higher layers
  • Uses the ubiquitous language (business intent, not DB/API details)

Example 1 – FFRepository

interface FFRepository {
  fun isAnyEnabled(): Boolean
}

Discussion questions - Does it manage an Aggregate Root? - Is it really about persistence or just config lookup? - Would FFRepositoryProvider or FFRepositoryService be a better name?


Example 2 – AnyGatewayRepository

interface AnyGatewayRepository {
  fun copyAd(
    adId: AdId,
    destinationTable: Table,
  )
  fun delete(adId: AdId)
}

Discussion questions - Does it return or persist an aggregate? - Does it mix infrastructure operations (copy/delete between tables)? - Better alternative: AnyGateway, AnyGatewayClient?

Example 3 – BookRepository

interface BookRepository {
  fun persist(offer: Book): Offer
  fun findBy(offerId: BookId): Offer?
  fun findBy(userId: UserId): List<Offer>
  fun findBy(adId: AdId): List<Offer>
  // this method is suspicious
  fun findByTop100UserIdOrderingByLastTimeUpdatedDesc(userId: UserId): List<Book>

}

Why it’s a good Repository - ✅ Manages a clear Aggregate Root (Book) - ✅ Exposes only essential persistence and query operations - 🤔 Specific queries can be delegated to a Criteria, or Specification pattern, o use more meaninful names for the business.


Quick Evaluation Criteria

✅ Does it manage an Aggregate Root?
✅ Does it actually persist or retrieve the AR?
✅ Does it use domain language, avoiding technical details?
✅ Does it avoid infrastructure logic inside the repository?