Repository Design Pattern (original) (raw)

Last Updated : 13 May, 2026

The Repository Design Pattern provides an abstraction layer between business logic and data storage, offering a consistent way to access and manage data while hiding the details of the underlying data source.

Real-World Example

The Repository Design Pattern is like a librarian in a library.

Imagine you're at a library to find a book. You don't go into the storage room to search for it yourself, instead, you ask the librarian to help you find the book. The librarian knows where the books are kept and can give you the book you want without you having to worry about where it's stored.

Implementation

Problem statement

Suppose you are developing an e-commerce application that needs to manage products. The application should be able to add new products, retrieve existing products, update product information, and delete products. Instead of directly interacting with the database, we will utilize the Repository Pattern to handle these operations.

Step 1: Define the Product Entity

The Product class defines the attributes of product, such as id, name and price. This serves as the basic data structure representing a product.

C++ `

// Product entity representing a product class Product { public: int id; std::string name; float price;

Product(int id, std::string name, float price) : id(id), name(std::move(name)), price(price) {}

};

Java

/* Product entity representing a product */ public class Product { public int id; public String name; public float price;

public Product(int id, String name, float price) {
    this.id = id;
    this.name = name;
    this.price = price;
}

}

Python

class Product: # Product entity representing a product def init(self, id, name, price): self.id = id self.name = name self.price = price

JavaScript

// Product entity representing a product class Product { constructor(id, name, price) { this.id = id; this.name = name; this.price = price; } }

`

Step 2: Create the Repository Interface

The ProductRepository class is an abstract class that declares methods to manage products, such as adding, retrieving, updating, and deleting.

C++ `

// Interface for the repository class ProductRepository { public: virtual void addProduct(const Product& product) = 0; virtual Product getProductById(int productId) = 0; virtual void updateProduct(const Product& product) = 0; virtual void deleteProduct(int productId) = 0; };

Java

import java.util.List;

interface ProductRepository { void addProduct(Product product); Product getProductById(int productId); void updateProduct(Product product); void deleteProduct(int productId); }

Python

from abc import ABC, abstractmethod

class ProductRepository(ABC): @abstractmethod def add_product(self, product): pass

@abstractmethod
def get_product_by_id(self, product_id):
    pass

@abstractmethod
def update_product(self, product):
    pass

@abstractmethod
def delete_product(self, product_id):
    pass

JavaScript

class ProductRepository { addProduct(product) { throw new Error('Method not implemented.'); } getProductById(productId) { throw new Error('Method not implemented.'); } updateProduct(product) { throw new Error('Method not implemented.'); } deleteProduct(productId) { throw new Error('Method not implemented.'); } }

`

Step 3: Implement a Concrete Repository

The InMemoryProductRepository class is a concrete implementation of the ProductRepository__._ It uses an in-memory data structure (here, a vector) to manage products.

C++ `

// Concrete implementation of the repository (in-memory repository) class InMemoryProductRepository : public ProductRepository { private: std::vector products;

public: void addProduct(const Product& product) override { products.push_back(product); }

Product getProductById(int productId) override {
    for (const auto& product : products) {
        if (product.id == productId) {
            return product;
        }
    }
    return Product(-1, "Not Found", 0.0); // Return a default product if not found
}

void updateProduct(const Product& updatedProduct) override {
    for (auto& product : products) {
        if (product.id == updatedProduct.id) {
            product = updatedProduct;
            return;
        }
    }
}

void deleteProduct(int productId) override {
    products.erase(std::remove_if(products.begin(), products.end(),
        [productId](const Product& product) { return product.id == productId; }),
        products.end());
}

};

Java

import java.util.ArrayList; import java.util.List;

class Product { private int id; private String name; private double price;

public Product(int id, String name, double price) {
    this.id = id;
    this.name = name;
    this.price = price;
}

public int getId() {
    return id;
}

public String getName() {
    return name;
}

public double getPrice() {
    return price;
}

}

interface ProductRepository { void addProduct(Product product); Product getProductById(int productId); void updateProduct(Product updatedProduct); void deleteProduct(int productId); }

class InMemoryProductRepository implements ProductRepository { private List products = new ArrayList<>();

@Override
public void addProduct(Product product) {
    products.add(product);
}

@Override
public Product getProductById(int productId) {
    for (Product product : products) {
        if (product.getId() == productId) {
            return product;
        }
    }
    return new Product(-1, "Not Found", 0.0); // Return a default product if not found
}

@Override
public void updateProduct(Product updatedProduct) {
    for (int i = 0; i < products.size(); i++) {
        if (products.get(i).getId() == updatedProduct.getId()) {
            products.set(i, updatedProduct);
            return;
        }
    }
}

@Override
public void deleteProduct(int productId) {
    products.removeIf(product -> product.getId() == productId);
}

}

Python

class Product: def init(self, id, name, price): self.id = id self.name = name self.price = price

class ProductRepository: def addProduct(self, product): pass

def getProductById(self, productId):
    pass

def updateProduct(self, updatedProduct):
    pass

def deleteProduct(self, productId):
    pass

class InMemoryProductRepository(ProductRepository): def init(self): self.products = []

def addProduct(self, product):
    self.products.append(product)

def getProductById(self, productId):
    for product in self.products:
        if product.id == productId:
            return product
    return Product(-1, "Not Found", 0.0)  # Return a default product if not found

def updateProduct(self, updatedProduct):
    for i, product in enumerate(self.products):
        if product.id == updatedProduct.id:
            self.products[i] = updatedProduct
            return

def deleteProduct(self, productId):
    self.products = [product for product in self.products if product.id!= productId]

JavaScript

class Product { constructor(id, name, price) { this.id = id; this.name = name; this.price = price; } }

class ProductRepository { addProduct(product) {} getProductById(productId) {} updateProduct(updatedProduct) {} deleteProduct(productId) {} }

class InMemoryProductRepository extends ProductRepository { constructor() { super(); this.products = []; }

addProduct(product) {
    this.products.push(product);
}

getProductById(productId) {
    for (const product of this.products) {
        if (product.id === productId) {
            return product;
        }
    }
    return new Product(-1, 'Not Found', 0.0); // Return a default product if not found
}

updateProduct(updatedProduct) {
    for (let i = 0; i < this.products.length; i++) {
        if (this.products[i].id === updatedProduct.id) {
            this.products[i] = updatedProduct;
            return;
        }
    }
}

deleteProduct(productId) {
    this.products = this.products.filter(product => product.id!== productId);
}

}

`

Step 4: Usage in Main Function

The main function demonstrates the usage of the InMemoryProductRepository by performing various operations on products.

C++ `

int main() { InMemoryProductRepository productRepo;

// Adding products
productRepo.addProduct(Product(1, "Keyboard", 25.0));
productRepo.addProduct(Product(2, "Mouse", 15.0));

// Retrieving and updating product
Product retrievedProduct = productRepo.getProductById(1);
std::cout << "Retrieved Product: " << retrievedProduct.name << " - $" << retrievedProduct.price << std::endl;

retrievedProduct.price = 30.0;
productRepo.updateProduct(retrievedProduct);

// Deleting a product
productRepo.deleteProduct(2);

return 0;

}

Java

import java.util.ArrayList; import java.util.List;

class Product { int id; String name; double price;

public Product(int id, String name, double price) {
    this.id = id;
    this.name = name;
    this.price = price;
}

}

class InMemoryProductRepository { private List products = new ArrayList<>();

public void addProduct(Product product) {
    products.add(product);
}

public Product getProductById(int id) {
    for (Product product : products) {
        if (product.id == id) {
            return product;
        }
    }
    return null;
}

public void updateProduct(Product product) {
    for (int i = 0; i < products.size(); i++) {
        if (products.get(i).id == product.id) {
            products.set(i, product);
            break;
        }
    }
}

public void deleteProduct(int id) {
    products.removeIf(product -> product.id == id);
}

}

public class Main { public static void main(String[] args) { InMemoryProductRepository productRepo = new InMemoryProductRepository();

    // Adding products
    productRepo.addProduct(new Product(1, "Keyboard", 25.0));
    productRepo.addProduct(new Product(2, "Mouse", 15.0));

    // Retrieving and updating product
    Product retrievedProduct = productRepo.getProductById(1);
    System.out.println("Retrieved Product: " + retrievedProduct.name + " - $" + retrievedProduct.price);

    retrievedProduct.price = 30.0;
    productRepo.updateProduct(retrievedProduct);

    // Deleting a product
    productRepo.deleteProduct(2);
}

}

Python

class Product: def init(self, id, name, price): self.id = id self.name = name self.price = price

class InMemoryProductRepository: def init(self): self.products = []

def add_product(self, product):
    self.products.append(product)

def get_product_by_id(self, id):
    for product in self.products:
        if product.id == id:
            return product
    return None

def update_product(self, product):
    for i, p in enumerate(self.products):
        if p.id == product.id:
            self.products[i] = product
            break

def delete_product(self, id):
    self.products = [product for product in self.products if product.id!= id]

if name == 'main': product_repo = InMemoryProductRepository()

# Adding products
product_repo.add_product(Product(1, 'Keyboard', 25.0))
product_repo.add_product(Product(2, 'Mouse', 15.0))

# Retrieving and updating product
retrieved_product = product_repo.get_product_by_id(1)
print(f'Retrieved Product: {retrieved_product.name} - ${retrieved_product.price}')

retrieved_product.price = 30.0
product_repo.update_product(retrieved_product)

# Deleting a product
product_repo.delete_product(2)

` JavaScript ``

class Product { constructor(id, name, price) { this.id = id; this.name = name; this.price = price; } }

class InMemoryProductRepository { constructor() { this.products = []; }

addProduct(product) {
    this.products.push(product);
}

getProductById(id) {
    for (let product of this.products) {
        if (product.id === id) {
            return product;
        }
    }
    return null;
}

updateProduct(product) {
    for (let i = 0; i < this.products.length; i++) {
        if (this.products[i].id === product.id) {
            this.products[i] = product;
            break;
        }
    }
}

deleteProduct(id) {
    this.products = this.products.filter(product => product.id!== id);
}

}

const productRepo = new InMemoryProductRepository();

// Adding products productRepo.addProduct(new Product(1, 'Keyboard', 25.0)); productRepo.addProduct(new Product(2, 'Mouse', 15.0));

// Retrieving and updating product let retrievedProduct = productRepo.getProductById(1); console.log(Retrieved Product: <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mrow><mi>r</mi><mi>e</mi><mi>t</mi><mi>r</mi><mi>i</mi><mi>e</mi><mi>v</mi><mi>e</mi><mi>d</mi><mi>P</mi><mi>r</mi><mi>o</mi><mi>d</mi><mi>u</mi><mi>c</mi><mi>t</mi><mi mathvariant="normal">.</mi><mi>n</mi><mi>a</mi><mi>m</mi><mi>e</mi></mrow><mo>−</mo></mrow><annotation encoding="application/x-tex">{retrievedProduct.name} - </annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:0.7778em;vertical-align:-0.0833em;"></span><span class="mord"><span class="mord mathnormal">re</span><span class="mord mathnormal">t</span><span class="mord mathnormal" style="margin-right:0.02778em;">r</span><span class="mord mathnormal">i</span><span class="mord mathnormal">e</span><span class="mord mathnormal" style="margin-right:0.03588em;">v</span><span class="mord mathnormal">e</span><span class="mord mathnormal">d</span><span class="mord mathnormal" style="margin-right:0.13889em;">P</span><span class="mord mathnormal">ro</span><span class="mord mathnormal">d</span><span class="mord mathnormal">u</span><span class="mord mathnormal">c</span><span class="mord mathnormal">t</span><span class="mord">.</span><span class="mord mathnormal">nam</span><span class="mord mathnormal">e</span></span><span class="mord">−</span></span></span></span>${retrievedProduct.price});

retrievedProduct.price = 30.0; productRepo.updateProduct(retrievedProduct);

// Deleting a product productRepo.deleteProduct(2);

``

This code implements a simple in-memory repository for demonstration purposes, but in real-world scenario, the repository would likely interact with a database or some other persistent storage.

Advantages

The Repository Design Pattern offers several benefits that improve the structure and quality of an application.

Disadvantages

The disadvantages of Repository Design Pattern are

Use Cases

The use cases of Repository Design Pattern are

Limitation: Over-Abstraction of Data Sources

The Repository Pattern works well when dealing with similar types of data sources (like relational databases). However, not all data sources behave the same way.

For example:

If we try to force both into a single generic repository interface (CRUD), we may lose these specialized capabilities.