principletypescriptMajor
SOLID — Dependency Inversion Principle: Depend on Abstractions Not Concretions
Viewed 0 times
dependency inversionDIPSOLIDabstractionsports and adaptersclean architectureinterface ownership
Problem
High-level policy classes directly import and depend on low-level implementation classes. When the database changes from Postgres to MongoDB, the high-level OrderService must be rewritten.
Solution
Both high-level and low-level modules depend on an abstraction (interface). The abstraction is owned by the high-level module. Low-level modules implement it.
// DIP compliant: high-level module defines the interface it needs
// domain/ports/order.repository.ts
export interface OrderRepository {
findById(id: string): Promise<Order | null>;
save(order: Order): Promise<void>;
}
// domain/use-cases/get-order.ts — high-level, depends on interface only
export class GetOrderUseCase {
constructor(private repo: OrderRepository) {}
async execute(id: string) { return this.repo.findById(id); }
}
// infrastructure/postgres-order.repository.ts — low-level, implements interface
import { OrderRepository } from '../../domain/ports/order.repository';
export class PostgresOrderRepository implements OrderRepository {
async findById(id: string) { /* sql query */ return null; }
async save(order: Order) { /* sql insert */ }
}Why
DIP is the principle behind clean architecture. The domain (highest level) knows nothing of databases, frameworks, or HTTP. All arrows point inward: infrastructure depends on domain, never the reverse.
Gotchas
- DIP is not just 'use interfaces'. The key is that the interface is defined where it is used (domain), not where it is implemented (infrastructure).
- TypeScript's module system makes interface ownership explicit via import paths — a domain module importing from infrastructure is a DIP violation.
- DIP and Dependency Injection are related but distinct: DIP is the principle, DI is one technique to achieve it.
Revisions (0)
No revisions yet.