Dependency Inversion Principle
A design principle where high-level modules depend on abstractions, not on low-level implementation details.
The Dependency Inversion Principle says that high-level modules should not depend on low-level modules. Both should depend on abstractions. It's the "D" in SOLID, and it's one of the ideas that makes Clean Architecture work in practice.
The rule is straightforward: outer layers (implementation details like databases, HTTP handlers, and third-party APIs) can refer to inner layers (your business logic), but not the other way around. Your domain and application code should depend on interfaces, not on concrete implementations.
In Go, this maps naturally to how interfaces work. You define an interface where it's used, not where it's implemented. Your application layer declares a OrderRepository interface, and the adapters layer provides the PostgreSQL implementation. The application layer never imports the adapters package.
// Defined in the application layer, not in the adapters layer.
type OrderRepository interface {
Save(ctx context.Context, order Order) error
FindByID(ctx context.Context, id string) (Order, error)
}
This is different from Dependency Injection, though the two work together. Dependency Inversion is the principle: depend on abstractions. Dependency Injection is the mechanism: pass those abstractions from the outside. You can follow the principle without a DI framework by wiring everything manually in main.go.
The main benefit is testability and flexibility. When your business logic depends on interfaces, you can swap implementations without touching the core code. You can test with in-memory stores, switch databases, or easily replace a third-party service. The Repository Pattern is a classic example of this principle applied to data access.
Go's implicit interfaces make this feel natural. You don't need to declare "implements" anywhere. If your struct has the right methods, it satisfies the interface.
References
- How to implement Clean Architecture in Go (Golang) — Dedicated section on the Dependency Inversion Principle explaining how outer layers refer to inner layers but not the other way around. Shows that the principle is the 'D' in SOLID and that Go interfaces are a perfect match for it.
- Microservices test architecture. Can you sleep well without end-to-end tests? — Notes that commands and queries with external dependencies are trivial to mock when you follow the Dependency Inversion Principle. Reminds that depending on interfaces rather than structs makes injecting mocks at the service level straightforward.
- The Go libraries that never failed us: 22 libraries you need to know — Recommends the go-cleanarch linter for enforcing the Dependency Inversion Rule and correct interaction between packages in Clean/Hexagonal Architecture projects.
- Is Clean Architecture Overengineering? — Lists the Dependency Inversion Principle as one of the SOLID principles closely related to Clean Architecture. Discusses how separating domain from infrastructure follows this principle.