Skip to main content

Depend on Abstractions

The Dependency Inversion Principle says: high-level code — your business logic — should not depend on low-level details like a specific database or file format. Both should depend on an abstraction: in Go, an interface.

The "inversion" is the direction of the arrow. Instead of the service pointing at one concrete storage, the service points at an interface — and every storage points at that same interface from the other side.


Hard-Wired to the Details

This user service creates and uses a concrete DiskStore directly. It works — but the service is welded to one storage. A different store, a faster store, a fake store for tests: every one of them means editing the service itself.

The service welded to its storage

UserService builds its own DiskStore inside. High-level registration logic now depends on a low-level detail — the file on disk.

Prompt
Write a Go program showing a Dependency Inversion violation: a UserService whose Register method uses a concrete DiskStore created inside the service, printing that the user was written to a file on disk. Register one user.
Output: Go program and captured output

Invert the Dependency

The fix: the service asks for a Storage interface in its constructor and never learns which one it got. The disk store and the memory store both implement the interface.

Now the arrow points from both sides to the abstraction — and swapping the storage is a one-line change at the place where the service is created, not inside it.

The service accepts an interface

Same registration logic, two different storages. NewUserService(DiskStore{}) or NewUserService(MemoryStore{}) — the service code itself never changes.

Prompt
Invert the dependency in Go: define a Storage interface with a Save method, a DiskStore and a MemoryStore that implement it, and a UserService created with NewUserService that accepts any Storage. Register one user through the disk store and one through the memory store to show the swap without changing the service.
Output: Go program and captured output
The service accepts an interface

The payoff: testing without a disk

Because the service depends on the abstraction, a test can hand it a memory store and check the result instantly — no files, no cleanup. Dependency inversion is what makes code easy to unit-test.

Prompt
Show the testing payoff of Dependency Inversion in Go: give MemoryStore a record of saved users, hand it to the UserService, register two users, and verify the memory store holds both, printing the check results like a small test.
Output: Go program and captured output