Menu
Coddy logo textTech

Padrão Repository

Parte da seção Programação Orientada a Objetos do Journey de Dart da Coddy. Lição 100 de 110.

O padrão Repository separa a lógica de acesso a dados da lógica de negócios, criando uma camada de abstração entre sua aplicação e as fontes de dados. Em vez de suas classes interagirem diretamente com bancos de dados, APIs ou arquivos, elas trabalham com um repositório que lida com todas as operações de dados.

O padrão define uma interface para operações de dados, e as implementações concretas lidam com o mecanismo de armazenamento real. Isso facilita a troca das fontes de dados sem alterar sua lógica de negócios:

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

// Interface do repositório
abstract class UserRepository {
  User? getById(int id);
  List<User> getAll();
  void save(User user);
  void delete(int id);
}

// Implementação em memória
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
}

Sua lógica de negócio depende apenas da interface UserRepository. Mais tarde, você poderia criar um DatabaseUserRepository ou um ApiUserRepository que implemente a mesma interface e trocar as implementações sem modificar nenhum código que use o repositório. Esse padrão é essencial para aplicações testáveis e fáceis de manter, nas quais as fontes de dados podem mudar ao longo do tempo.

challenge icon

Desafio

Fácil

Vamos criar um sistema de inventário de produtos usando o padrão Repository! Você criará uma camada de abstração que separa a forma como os produtos são armazenados da forma como são usados, facilitando a troca das implementações de armazenamento sem alterar a lógica de negócio.

Você organizará seu código em dois arquivos:

  • product_repository.dart: Este arquivo contém suas entidades e classes de repositório. Comece com uma classe Product que tenha campos final para id (int), name (String) e price (double). Em seguida, defina uma classe abstrata ProductRepository que declare o contrato para as operações de dados: getById(int id), retornando um Product anulável; getAll(), retornando uma lista de produtos; save(Product product), para armazenar um produto; e delete(int id), para remover um produto. Por fim, crie um InMemoryProductRepository que implemente essa interface usando um Map<int, Product> para o armazenamento.
  • main.dart: Importe seu arquivo de repositório e demonstre o padrão Repository em ação. Crie um InMemoryProductRepository, mas armazene-o em uma variável do tipo ProductRepository — isso mostra como seu código depende da interface, não da implementação. Salve três produtos: Laptop (id: 1, price: 999.99), Mouse (id: 2, price: 29.99) e Keyboard (id: 3, price: 79.99). Em seguida, recupere e imprima o produto com id 2 no formato Found: [name] - $[price]. Exclua o produto com id 1. Por fim, imprima a quantidade de produtos restantes como Products in stock: [count].

O poder desse padrão é que seu código principal conhece apenas a interface ProductRepository. Posteriormente, você poderia criar um DatabaseProductRepository ou um ApiProductRepository e substituí-lo sem alterar nenhuma parte da lógica de negócio!

Saída esperada:

Found: Mouse - $29.99
Products in stock: 2

Experimente você mesmo

import 'product_repository.dart';

void main() {
  // Criar repositório - note que o tipo é a interface, não a implementação
  // TODO: Criar um InMemoryProductRepository e armazená-lo em uma variável ProductRepository
  
  // TODO: Salvar três produtos:
  // - Laptop (id: 1, price: 999.99)
  // - Mouse (id: 2, price: 29.99)
  // - Keyboard (id: 3, price: 79.99)
  
  // TODO: Recuperar o produto com id 2 e imprimir: "Found: [name] - $[price]"
  
  // TODO: Excluir o produto com id 1
  
  // TODO: Imprimir a contagem de produtos restantes: "Products in stock: [count]"
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Dart online