Course contentsShow
← Node.js
Lesson 694 of 1,9807. Web Framework Architecture and MiddlewareFree lesson

Repository Pattern Implementation

Abstract database access methods behind a consistent interface for easier testing.

In this lesson, you will learn how to decouple your business logic from database-specific code using the Repository Pattern to make your application easier to test.

The Problem: Tight Coupling

When you write database queries directly inside your service layer, your business logic becomes "stuck" to a specific implementation. If you use MongoDB, your services are riddled with db.collection.find(). If you ever decide to switch to PostgreSQL or need to test your service without a live database, you have to rewrite your business logic.

The Repository Pattern introduces an abstraction layer—a "middleman"—that handles all data access, presenting a clean interface to the rest of your application.

The Repository Interface

Think of the Repository as a collection of domain objects in memory. Your service layer doesn't need to know if the data comes from a JSON file, a Redis cache, or a SQL database. It only knows that it can ask the repository for data.

Here is how you might abstract a UserRepository:

// The Interface (Contract)
class UserRepository {
  async getById(id) { throw new Error("Not implemented"); }
}

// The Implementation (e.g., MongoDB)
class MongoUserRepository extends UserRepository {
  async getById(id) {
    return await db.users.findOne({ _id: id });
  }
}

Why This Matters for Testing

Because your UserService now relies on an instance of UserRepository (via dependency injection) rather than a direct database connection, you can easily swap the MongoUserRepository for a MockUserRepository during unit tests.

// A simple mock for testing
class MockUserRepository {
  async getById(id) {
    return { id, name: "Test User" };
  }
}

// Your service remains unchanged!
const service = new UserService(new MockUserRepository());

By abstracting data access, your services focus strictly on business rules, while the repository acts as the gatekeeper for data persistence. This creates a clear boundary, allowing you to swap storage backends or mock entire data layers without touching your core application logic.

Key Takeaway: The Repository Pattern shields your business logic from database specifics, allowing you to swap storage engines or inject mock data for seamless, isolated testing.