PythiloLesson 6 of 15 · progress kept in this browser

The repository pattern

The domain should not care whether products live in a dict, a sqlite file or a remote service. A repository hides storage behind a small collection-like interface: add an object, get it back by key, list them all.

from dataclasses import dataclass

@dataclass
class Product:
    sku: str
    stock: int

class MemoryRepo:
    def __init__(self):
        self._rows = {}

    def add(self, product):
        self._rows[product.sku] = product

    def get(self, sku):
        return self._rows.get(sku)

repo = MemoryRepo()
repo.add(Product("MUG", 3))
print(repo.get("MUG"))

Services receive a repository as an argument. Tests pass the in-memory one, and the application passes one backed by a database:

import sqlite3

db = sqlite3.connect(":memory:")
db.execute(
    "CREATE TABLE products (sku TEXT"
    " PRIMARY KEY, stock INT)")
db.execute(
    "INSERT INTO products VALUES (?, ?)",
    ("MUG", 3))
rows = db.execute("SELECT * FROM products")
print(rows.fetchall())

Both must behave the same, so write one contract test and run it against every implementation.

Your task

Write SqliteProductRepository(conn) matching InMemoryProductRepository: it creates a products table, add(product) inserts or replaces, get(sku) returns a new Product or None, and list() returns every product sorted by sku. Then fix the in-memory repository so that, like a database, it stores and returns copies.