intermediate
Repository pattern
Expose collection-like persistence operations while hiding query details and keeping service code testable.
A repository exposes collection-like persistence operations for an aggregate: `findById`, `save`, `listActiveForUser`. It hides ORM or SQL details from application services when contracts stay use-case oriented.
interface OrderRepository {
findOpenForCustomer(customerId: string): Promise<Order[]>;
save(order: Order): Promise<void>;
}
Good repositories model business queries, not every possible column filter. They sit at a boundary — not a magic layer that removes the need to think about SQL.
On interviews: mention aggregate ownership, pagination, filtering, transactions, and when a query builder is clearer than a repository method explosion.
Common pitfalls: generic `findByAnyField` repositories that leak the database; repositories that become thin ORM wrappers with no design value; hiding needed query semantics behind vague method names.
The trade-off is clearer service code and test doubles versus extra abstraction when the domain is only CRUD.
Checklist:
- Model use-case-oriented methods.
- Avoid hiding needed query semantics.
- Keep transactions explicit at the right layer.
- Use query builder when joins or locks need control.