Menu
CoddyTech

Patrón Repository

Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy. Lección 100 de 110.

El patrón de repositorio separa la lógica de acceso a los datos de la lógica de negocio mediante la creación de una capa de abstracción entre tu aplicación y las fuentes de datos. En lugar de que tus clases interactúen directamente con bases de datos, API o archivos, trabajan con un repositorio que gestiona todas las operaciones con los datos.

El patrón define una interfaz para las operaciones de datos, y las implementaciones concretas se encargan del mecanismo de almacenamiento real. Esto facilita cambiar las fuentes de datos sin modificar la lógica de negocio:

// Entidad
class User {
  final int id;
  final String name;
  User(this.id, this.name);
}

// Interfaz del repositorio
abstract class UserRepository {
  User? getById(int id);
  List<User> getAll();
  void save(User user);
  void delete(int id);
}

// Implementación en memoria
class InMemoryUserRepository implements UserRepository {
  final Map<int, User> _users = {};

  @override
  User? getById(int id) => _users[id];

  @override
  List<User> getAll() => _users.values.toList();

  @override
  void save(User user) => _users[user.id] = user;

  @override
  void delete(int id) => _users.remove(id);
}

void main() {
  UserRepository repo = InMemoryUserRepository();
  
  repo.save(User(1, 'Alice'));
  repo.save(User(2, 'Bob'));
  
  print(repo.getById(1)?.name);  // Alice
  print(repo.getAll().length);    // 2
}

Tu lógica de negocio depende únicamente de la interfaz UserRepository. Más adelante podrías crear un DatabaseUserRepository o un ApiUserRepository que implemente la misma interfaz y cambiar las implementaciones sin modificar ningún código que use el repositorio. Este patrón es esencial para crear aplicaciones fáciles de probar y mantener, en las que las fuentes de datos pueden cambiar con el tiempo.

challenge icon

Desafío

Fácil

¡Vamos a crear un sistema de inventario de productos usando el patrón Repository! Crearás una capa de abstracción que separa la forma en que se almacenan los productos de la forma en que se utilizan, lo que facilita cambiar las implementaciones de almacenamiento sin modificar la lógica de negocio.

Organizarás tu código en dos archivos:

  • product_repository.dart: Este archivo contiene las clases de entidad y de repositorio. Empieza con una clase Product que tenga campos final para id (int), name (String) y price (double). Luego define una clase abstracta ProductRepository que declare el contrato para las operaciones con datos: getById(int id), que devuelve un Product nullable, getAll(), que devuelve una lista de productos, save(Product product), para almacenar un producto, y delete(int id), para eliminar uno. Por último, crea un InMemoryProductRepository que implemente esta interfaz usando un Map<int, Product> para el almacenamiento.
  • main.dart: Importa el archivo del repositorio y demuestra el patrón Repository en acción. Crea un InMemoryProductRepository, pero guárdalo en una variable de tipo ProductRepository; esto muestra cómo tu código depende de la interfaz, no de la implementación. Guarda tres productos: Laptop (id: 1, price: 999.99), Mouse (id: 2, price: 29.99) y Keyboard (id: 3, price: 79.99). Luego recupera e imprime el producto con id 2 con el formato Found: [name] - $[price]. Elimina el producto con id 1. Por último, imprime la cantidad de productos restantes como Products in stock: [count].

La ventaja de este patrón es que tu código principal solo conoce la interfaz ProductRepository. Más adelante podrías crear un DatabaseProductRepository o un ApiProductRepository y sustituirlo sin cambiar nada de la lógica de negocio.

Salida esperada:

Found: Mouse - $29.99
Products in stock: 2

Pruébalo tú mismo

import 'product_repository.dart';

void main() {
  // Crea un repositorio; ten en cuenta que el tipo es la interfaz, no la implementación
  // TODO: Crea un InMemoryProductRepository y guárdalo en una variable ProductRepository
  
  // TODO: Guarda tres productos:
  // - Laptop (id: 1, price: 999.99)
  // - Mouse (id: 2, price: 29.99)
  // - Keyboard (id: 3, price: 79.99)
  
  // TODO: Recupera el producto con id 2 e imprime: "Found: [name] - $[price]"
  
  // TODO: Elimina el producto con id 1
  
  // TODO: Imprime la cantidad de productos restantes: "Products in stock: [count]"
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos

Practica por tu cuenta: Compilador de Dart online