Lesson 22 / 25
Architectural Patterns Overview
MVC, repository, unit of work and a pointer to CQRS.
Patterns at a larger scale
Some patterns organise whole applications rather than a few classes. MVC (Model-View-Controller) separates data and rules (model), presentation (view) and input handling (controller); variants include MVP and MVVM. The Repository pattern (from Martin Fowler's Patterns of Enterprise Application Architecture and Domain-Driven Design) gives a collection-like interface for loading and saving domain objects, hiding SQL or ORM details from business logic. Unit of Work tracks changes made during a business operation and commits them together, usually in one transaction; many ORMs implement it for you (for example a session or context object). CQRS (Command Query Responsibility Segregation) uses separate models for writes and reads; it can help with complex domains or very different read and write loads, but adds significant complexity and is not a default choice.
From catalogue to judgement
Patterns at the architecture level, the smells that signal misuse, and how to talk about patterns.
Repository and unit of work
Business logic talks to interfaces; one transaction commits the changes.
interface AccountRepository {
findById(id: string): Promise<Account | null>;
save(account: Account): Promise<void>;
}
interface UnitOfWork {
accounts: AccountRepository;
ledger: LedgerRepository;
commit(): Promise<void>;
rollback(): Promise<void>;
}
async function transferFunds(uowFactory: () => Promise<UnitOfWork>, fromId: string, toId: string, cents: number) {
const uow = await uowFactory(); // e.g. begins a database transaction
try {
const from = await uow.accounts.findById(fromId);
const to = await uow.accounts.findById(toId);
if (!from || !to) throw new Error('Account not found');
from.withdraw(cents); // domain rules live on the model
to.deposit(cents);
await uow.accounts.save(from);
await uow.accounts.save(to);
await uow.ledger.record({ fromId, toId, cents });
await uow.commit(); // all or nothing
} catch (err) {
await uow.rollback();
throw err;
}
}Do not wrap an ORM twice
If your ORM already provides a repository-like API and a unit of work, a generic repository layer on top often adds code without adding value. Introduce one when you need to hide persistence from domain logic or test without a database.
Quick check: What does the Unit of Work pattern do?
- Separates read and write databases
- Renders HTML views
- Ensures only one database connection exists
- Tracks changes in a business operation and commits them together
Answer
Tracks changes in a business operation and commits them together — It groups changes into one atomic commit.