Suggest an editImprove this articleRefine the answer for “Why should high-level modules not depend on low-level modules?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)High-level modules should not depend on low-level modules because this **undermines the system's stability, flexibility, and reusability**. This principle is the basis of the **Dependency Inversion Principle (DIP)** from SOLID, and its goal is to make the architecture independent of implementation details. **Key point:** both sides should depend on shared abstractions - business logic describes "what needs to be done", and implementation details adapt to it, which makes the system flexible, modular, and resilient to change.Shown above the full answer for quick recall.Answer (EN)ImageHigh-level modules should not depend on low-level modules, because this **undermines the system's stability, flexibility, and reusability**. This principle is the basis of the **Dependency Inversion Principle (DIP)** from SOLID, and its goal is to make the architecture **independent of implementation details**. --- ### 1. What high-level and low-level modules are - **High-level modules** contain the business logic, rules, and main scenarios of how the system works. Example: `OrderService`, `PaymentProcessor`, `UserManager`. - **Low-level modules** handle implementation details: the database, files, network, API. Example: `MySQLRepository`, `EmailSender`, `FileLogger`. When a high-level module uses a low-level one directly, it becomes **tightly tied to specific technologies and tools**, which makes the system fragile. --- ### 2. What happens with a direct dependency ```python class OrderService: def __init__(self): self.db = MySQLRepository() # low-level dependency def create_order(self, order): self.db.save(order) ``` The problem: - Switching from MySQL to PostgreSQL or MongoDB means rewriting `OrderService`. - The business logic cannot be tested without a real database. - The domain logic "knows" about technical details. In other words, **the high-level code becomes dependent on infrastructure details**, even though it should only define *what to do*, not *how exactly*. --- ### 3. After the dependency inversion ```python class Repository(Protocol): def save(self, entity): ... class OrderService: def __init__(self, repository: Repository): self.repository = repository def create_order(self, order): self.repository.save(order) ``` Now: - `OrderService` depends on an **abstraction**, not on a concrete class. - Low-level code (`MySQLRepository`, `MongoRepository`) implements this abstraction. - The specific implementation is supplied from outside (through an IoC container or manually). This way **the dependency is inverted**: the details depend on abstractions, not the other way around. --- ### 4. Why this is needed 1. **Isolating the business logic** - changes in the infrastructure (database, logging, API) do not require changes to the business code. 2. **Testability** - mocks and fake implementations can be plugged in without real dependencies. 3. **Flexibility and scalability** - technologies can be swapped easily without refactoring key modules. 4. **Clean architecture** - the upper layers (use cases, business logic) are independent of the lower ones (database, UI). --- ### 5. The Dependency Inversion principle stated > 1. High-level modules should not depend on low-level modules. > Both should depend on abstractions. > 2. Abstractions should not depend on details. > Details should depend on abstractions. --- **Summary:** *High-level modules should not depend on low-level modules, because this leads to tight coupling and makes maintenance and testing harder. Instead, both sides should depend on shared abstractions: business logic describes "what needs to be done", and implementation details adapt to it. This approach makes the system flexible, modular, and resilient to change.*For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.