The coupling problem
If you scatter SQL queries (or ORM calls) all over the application, your business logic becomes coupled to the database. Switching from Postgres to another source, or writing tests without a real database, becomes very hard.
The solution: the repository
A repository concentrates all the data access of an entity behind a set of functions named after the domain, not after SQL:
create(data)findById(id)list()update(id, changes)remove(id)
The rest of the program calls these functions and does not know whether SQL, an ORM or an array sits underneath. That boundary is the valuable part.
function createUserRepo(db) {
return {
async list() {
const { rows } = await db.query("SELECT * FROM users");
return rows;
},
async findById(id) {
const { rows } = await db.query(
"SELECT * FROM users WHERE id = $1",
[id]
);
return rows[0];
},
async create(user) {
const { rows } = await db.query(
"INSERT INTO users (email) VALUES ($1) RETURNING *",
[user.email]
);
return rows[0];
},
};
}
In-memory repositories
Since the repository is just an interface (an object with functions), you can implement one in memory over an array for tests. The logic that uses it does not notice the difference: in production it receives the real repo; in tests, the in-memory one. That is what you will practice in the exercises.
Repository functions are usually
async(they return aPromise), even if internally they are synchronous, so the signature does not change when moving from an array to a real database.