Challenge 1: Design a Repository Interface — Possible Solution ==================================================================== interface Product { id: string; name: string; price: number; } type CreateProductInput = Omit; interface ProductRepository { findById(id: string): Promise; findAll(): Promise; create(input: CreateProductInput): Promise; } // --- In-memory implementation (great for tests) --- class InMemoryProductRepository implements ProductRepository { private products: Product[] = []; private nextId = 1; async findById(id: string): Promise { return this.products.find((p) => p.id === id) ?? null; } async findAll(): Promise { return [...this.products]; } async create(input: CreateProductInput): Promise { const product: Product = { id: String(this.nextId++), ...input }; this.products.push(product); return product; } } // --- ORM-backed implementation (sketch, assumes a Prisma-style client) --- interface PrismaLikeClient { product: { findUnique(args: { where: { id: string } }): Promise; findMany(): Promise; create(args: { data: CreateProductInput }): Promise; }; } class PrismaProductRepository implements ProductRepository { constructor(private prisma: PrismaLikeClient) {} findById(id: string): Promise { return this.prisma.product.findUnique({ where: { id } }); } findAll(): Promise { return this.prisma.product.findMany(); } create(input: CreateProductInput): Promise { return this.prisma.product.create({ data: input }); } } // --- Usage: business logic depends only on the interface --- class CatalogService { constructor(private products: ProductRepository) {} async listProducts(): Promise { return this.products.findAll(); } } // In tests: const testService = new CatalogService(new InMemoryProductRepository()); // In production: // const prodService = new CatalogService(new PrismaProductRepository(prismaClient)); WHY THIS WORKS -------------- - CatalogService depends on ProductRepository (the interface), never on InMemoryProductRepository or PrismaProductRepository directly — the exact same principle as Database/Mailer in Chapter 1's DI chapter. - Tests can use InMemoryProductRepository and run instantly with no real database, network, or Docker container required. - Swapping the actual persistence technology (Prisma today, something else in two years) means writing one new class that implements ProductRepository — CatalogService and everything above it never changes.