Design pattern | Repository

Design pattern | Repository

January 10, 2024

design-pattern
repository-pattern
refactorit

The Repository pattern is a fundamental pillar in modern software architecture. Its primary purpose is to abstract data access logic, allowing the rest of the code to interact with a uniform interface, regardless of how the data is stored or retrieved. This promotes greater separation of concerns and cleaner, more maintainable code.

Cause

In applications with a database, it is common to find data access logic scattered throughout the code. This leads to a high dependency between the business logic and the specific implementation of the database, which makes testing, maintenance, and scalability of the code difficult.

Example

Let's consider a user management system. Without the Repository pattern, the code to access user data might look like this:

class UserService {
    private db
 
    constructor(db: any) {
        this.db = db
    }
 
    async findUserById(id: number) {
        return this.db.query('SELECT * FROM users WHERE id = ?', [id])
    }
    // Other methods that interact directly with the database
}

This approach couples the user service to a specific database implementation, making it difficult to change the data source or database technology in the future.

Solution

Let's implement the Repository pattern in this case we will do it in TypeScript. First, we define a UserRepository interface that declares the methods to access user data:

interface UserRepository {
    findById(id: number): Promise<User>
    // Other relevant methods
}

Next, we create a concrete implementation of this interface, in this case suppose we need the implementation for MySQL, the implementation would be the same for any other database system since we are forcing the UserRepository contract to be fulfilled:

class MySQLUserRepository implements UserRepository {
    private db: MySqlDB
 
    constructor(db: MySqlDB) {
        this.db = db
    }
 
    async findById(id: number): Promise<User> {
        return this.db.query('SELECT * FROM users WHERE id = ?', [id])
    }
    // Other relevant methods
}

Finally, we modify UserService to use UserRepository:

class UserService {
    private userRepository: UserRepository
 
    constructor(userRepository: UserRepository) {
        this.userRepository = userRepository
    }
 
    async findUserById(id: number): Promise<User> {
        return this.userRepository.findById(id)
    }
    // Other methods that use userRepository
}

Benefits

Adopting the Repository pattern offers several benefits:

Implementing the Repository pattern may seem like an extra task at first, but the long-term benefits in terms of maintainability and scalability are significant. It's a worthwhile investment for any application that hopes to evolve and grow over time.


Thanks for reading me 😊