SOLID Principles
SOLID principles guide object-oriented design for maintainable, extensible code. Phexium's architecture enforces these principles through its layered structure and plugin system.
Single Responsibility Principle (SRP)
A class should have only one reason to change. Each component in the architecture has a single, well-defined responsibility.
Phexium application:
| Component | Responsibility | Does NOT do |
|---|---|---|
| Controller | Parse HTTP request, dispatch to bus | Business logic, data formatting |
| Handler | Orchestrate domain operations | HTTP concerns, presentation |
| Presenter | Transform response to view model | Fetch data, modify state |
| Repository | Persist and retrieve entities | Validation, business rules |
| Entity | Enforce business invariants | Persistence, HTTP |
Violations to avoid:
- Controller containing business logic (should delegate to handler)
- Handler formatting output for display (should return domain data)
- Entity persisting itself (should use repository)
- Repository validating business rules (should only persist)
Open/Closed Principle (OCP)
Software entities should be open for extension but closed for modification. Adding new features should not require changing existing code.
Phexium application:
Adding a new feature follows a predictable pattern:
Existing handlers remain unchanged. The bus discovers and routes to the new handler automatically.
Extension patterns:
- New repository implementation → Create new Adapter implementing existing Port
- New event reaction → Add new listener, no handler changes needed
- New query variation → Create new Query + Handler pair
- New validation rule → Extend Value Object, don't modify existing ones
Liskov Substitution Principle (LSP)
Subtypes must be substitutable for their base types without altering program correctness. All implementations of an interface must behave consistently.
Phexium application:
All repository implementations (InMemory, Sqlite, Mysql, Postgresql) are interchangeable. Code using BookRepositoryInterface works identically regardless of which adapter is injected.
Requirements for adapters:
- Honor the full interface contract
- Throw only expected exception types
- Maintain consistent behavior across implementations
- Pass the same test suite regardless of implementation
Violations to avoid:
- Adapter throwing unexpected exceptions
- Implementation ignoring part of the interface contract
- Adapter with different side effects than others
Interface Segregation Principle (ISP)
Clients should not depend on methods they don't use. Prefer small, focused interfaces over large, general-purpose ones.
Phexium application:
The framework splits concerns into separate interfaces:
| Instead of | Phexium uses |
|---|---|
Generic BusInterface | CommandBusInterface + QueryBusInterface |
Monolithic SessionInterface | SessionInterface + FlashInterface |
Large RepositoryInterface | Per-aggregate interfaces (BookRepositoryInterface, UserRepositoryInterface) |
Benefits:
- Classes depend only on methods they actually use
- Easier to create focused test doubles
- Clear contracts for each concern
Dependency Inversion Principle (DIP)
High-level modules should not depend on low-level modules. Both should depend on abstractions.
Phexium application:
The application layer never imports concrete infrastructure:
// Application layer depends on abstraction (Port)
public function __construct(
private readonly BookRepositoryInterface $repository,
)
// NOT on concrete implementation (Adapter)
// private readonly SqliteBookRepository $repository ← WRONG
Dependency flow:
Controller → CommandBusInterface → Handler → BookRepositoryInterface
↑
(injected by DI container)
↓
SqliteBookRepository (or any adapter)
The DI container in config/{app}/container.php wires concrete implementations to interfaces. Application code remains unaware of which adapter is used.
Validation Checklist
When reviewing code, verify:
- Each class has a single, clear responsibility
- New features add new classes rather than modifying existing handlers
- All adapters fully implement their port contracts
- Interfaces are minimal and focused on one concern
- No direct imports of concrete infrastructure in application layer
See Also
- SOLID Principles (Wikipedia)
- CQRS - OCP demonstrated with Command/Handler pattern
- Port & Adapter Pattern - LSP with interchangeable adapters
- Command Bus - ISP example of focused interfaces
- Query Bus - ISP example of focused interfaces