Design principles are the guiding beliefs that inform architectural decisions. They're not rigid rules β they're heuristics that help you make better trade-offs. Knowing when to apply a principle (and when to bend it) separates good architects from great ones.
4.1 SOLID Principles
Originally formulated for object-oriented design by Robert C. Martin, the SOLID principles apply equally well at the architectural level β to modules, components, and services.
S β Single Responsibility Principle (SRP)
"A module should have one, and only one, reason to change."
This doesn't mean a module does only one thing β it means it serves only one stakeholder or business capability. If a component handles both billing logic and email formatting, changes to email templates could break billing calculations. They should be separate.
At the architecture level, SRP means each building block should have a clear, focused purpose. A "UserService" that handles authentication, profile management, billing, and email sending violates SRP at the service level.
O β Open/Closed Principle (OCP)
"Software entities should be open for extension, closed for modification."
You should be able to add new behavior without changing existing code. In architecture, this means designing extension points: plugin systems, strategy patterns, event-driven hooks. When a new payment method is added, you write a new adapter β you don't modify the core payment processing logic.
L β Liskov Substitution Principle (LSP)
"Subtypes must be substitutable for their base types without altering the correctness of the program."
At the architecture level, this means any component that implements an interface must behave consistently with that interface's contract. If you swap a PostgreSQL repository for a MongoDB repository, the service using it shouldn't break β both must honor the same behavioral contract.
I β Interface Segregation Principle (ISP)
"Clients should not be forced to depend on interfaces they do not use."
Instead of one large, general-purpose interface, provide several smaller, specific ones. A service that only needs to read orders shouldn't depend on an interface that also includes creating and deleting orders. This reduces coupling and makes the system easier to evolve.
D β Dependency Inversion Principle (DIP)
"High-level modules should not depend on low-level modules. Both should depend on abstractions."
This is arguably the most important principle for architecture. Instead of the business logic depending directly on the database, both depend on an interface (a "port" in hexagonal architecture). This allows you to swap implementations without touching the business logic.
4.2 Coupling and Cohesion
These are the two most fundamental metrics for evaluating any decomposition. They apply at every level: classes, modules, components, services.
Types of Coupling (worst β best)
Types of Cohesion (worst β best)
4.3 Other Essential Principles
| Principle | Meaning | Practical Implication |
|---|---|---|
| Separation of Concerns | Each module addresses one distinct concern | Don't mix logging with business logic, or UI with persistence |
| Information Hiding (Parnas) | Hide internal details; expose only what's necessary | Modules communicate through interfaces, never by reaching into each other's internals |
| DRY | Don't Repeat Yourself β avoid duplicating knowledge | But beware: forcing DRY across service boundaries can create coupling. Sometimes duplication is preferable to the wrong abstraction. |
| KISS | Keep It Simple, Stupid | Prefer the simplest solution that works. Complexity should be justified by actual needs, not speculative future requirements. |
| YAGNI | You Aren't Gonna Need It | Don't build abstractions or features until they're actually needed. Premature generalization is a common source of unnecessary complexity. |
When Principles Conflict
In real systems, principles often tension against each other. DRY says "don't duplicate," but applying DRY across microservices creates coupling. KISS says "keep it simple," but DIP adds indirection layers. OCP says "extend without modifying," but YAGNI says "don't build extension points you don't need yet."
The skill of architecture is navigating these tensions β knowing which principle to prioritize in which context, and being explicit about the trade-off you're making.