cconnected.
Understand the pattern. Find your own way.Find your starting point
Your learning feed/Idea 182 of 218
Ideas this builds on
Low-level design182 / 218

Repository and unit of work

Picture

Let a notebook service ask a repository for notes. The repository owns how those notes are stored and queried.

Predict

Does having a repository automatically make multiple writes transactional?

Walk through the small example

No, define the unit of work. Storage access and transaction boundaries are separate responsibilities.

Mental model and complete Python solution

MemoryNotes implements a small save/get boundary. UnitOfWork stages a complete working copy, then commits it explicitly; exceptions leave the original state untouched. This demonstrates the atomic grouping concept in one process, not a concurrency-safe database transaction. A production repository should not hide query costs, isolation requirements, or pagination behind misleading collection-like methods.

What the function promises: This local unit of work supports explicit commit of in-memory note changes. Exiting without commit discards the draft. Concurrent transactions are not supported.

class MemoryNotes:
    def __init__(self):
        self.data = {}
    def get(self, key):
        return self.data.get(key)

class UnitOfWork:
    def __init__(self, repository):
        self.repository = repository
    def __enter__(self):
        self.draft = self.repository.data.copy()
        return self
    def save(self, key, value):
        self.draft[key] = value
    def commit(self):
        self.repository.data = self.draft.copy()
    def __exit__(self, kind, value, traceback):
        return False

repo = MemoryNotes()
with UnitOfWork(repo) as work:
    work.save("a", "idea")
assert repo.get("a") is None
with UnitOfWork(repo) as work:
    work.save("a", "idea")
    work.commit()
assert repo.get("a") == "idea"

Work and storage: O(number of notes) copying per unit of work in this teaching implementation. Real databases use different transactional storage mechanisms.

When to recognize it: A memory repository checks domain behavior without a live database. It is useful only if its relevant semantics match the real adapter.

Check before applying it: A repository abstraction cannot replace explicit isolation, concurrency, and transaction guarantees.

Change one rule

Two units of work start from the same snapshot and both commit. What can go wrong?

See what changes

The later commit can overwrite the earlier update. A real implementation needs optimistic version checks, locking, or suitable database isolation rather than unconditional snapshot replacement.

Connect

Keep changes from spreading everywhere: Separate domain responsibility from a concrete storage detail.

Choose storage from reads and writes: A real database adds indexes, isolation, and transactional behavior.