Separating Class Responsibilities with Clprolf
Source: Dev.to
Overview
Designing clean, well‑structured classes is a central challenge in object‑oriented programming.
Clprolf introduces declensions – a simple way to express the nature of a class and the domain it belongs to – without replacing your existing architecture. Repositories, services, controllers, entities, and abstractions stay where they are; the declension is added on top to ensure consistency.
Declensions
A declension tells whether a class belongs to the business/domain layer (agent) or the technical layer (worker_agent). The term is borrowed from natural languages, where a word changes form depending on its use while keeping the same core identity.
Main declensions
| Declension | Typical responsibilities |
|---|---|
| agent | Business logic, domain concepts, abstractions, simulation entities |
| worker_agent | Technical components: repositories, display logic, file operations, system interactions, launchers |
Synonyms
Within a declension, synonyms refine the intention without changing the domain.
- agent synonyms:
abstraction(conceptual or structural elements, e.g.,List,Button,Color),simu_real_obj(objects inside a simulation) - worker_agent synonyms:
comp_as_worker(same meaning with a simulation nuance)
These synonyms do not alter the actual domain; they simply provide clearer terminology for developers.
Integration with Existing Components
Clprolf does not replace Service, Repository, or Controller roles. It adds a declension annotation on top of them to enforce consistent boundaries.
Typical mapping
| Component type | Declension |
|---|---|
| Repository / DAO | worker_agent |
| Service | agent |
| Controller | agent (or worker_agent if the architecture treats controllers as purely technical endpoints) |
Inheritance Rules
Clprolf supervises inheritance between components indirectly:
- A worker_agent cannot inherit from an agent (e.g., a Repository cannot inherit from a Service).
- An agent cannot inherit from a technical class.
- Two classes with the same declension may inherit, but a warning is issued if they use different synonyms, indicating differing perspectives.
These rules prevent accidental cross‑domain inheritance and keep responsibilities clear over time.
Comparison with DDD and Clean Architecture
- DDD relies on conventions and team discipline to model the domain and maintain a ubiquitous language.
- Clean Architecture separates domain and technical layers by guidelines, patterns, and project structure.
- Clprolf enforces domain vs. technical roles at the language level, turning architectural boundaries into compiler‑checked guarantees.
Thus, Clprolf works alongside DDD and Clean Architecture, providing additional safety without replacing them.
Approach Comparison
| Approach | Focus | Enforcement |
|---|---|---|
| DDD | Domain modelling, ubiquitous language | Conventions & team discipline |
| Clean Architecture | Separation of domain vs. technical layers | Guidelines, patterns, project structure |
| Clprolf | Declensions: domain vs. technical roles | Compiler‑checked, enforced by the language |
Example Usage (Java)
// Business logic (agent)
@Agent
public class OrderService {
private final OrderRepository repository = new OrderRepository();
public void validateAndStore(String orderId) {
// Business logic
System.out.println("Validating order " + orderId);
// Technical operation delegated to a worker_agent
repository.save(orderId);
}
}
// Technical work (worker_agent)
@Worker_agent
public class OrderRepository {
public void save(String orderId) {
System.out.println("Saving order " + orderId);
}
}
// Illegal inheritance – compilation error
@Worker_agent
public class WrongRepo extends OrderService {
// ❌ A worker_agent cannot inherit from an agent.
}
// Domain object (agent)
@Agent
public class Animal {
public void eat(String food) {
System.out.println("The animal eats " + food);
}
}
// Service using technical workers
@Agent
public class CheckoutService {
private final OrderRepository orders = new OrderRepository();
private final PaymentRepository payments = new PaymentRepository();
public void checkout(String orderId) {
orders.save(orderId);
payments.process(orderId); // Technical operation
}
}
These examples demonstrate that:
- Existing components (Service, Repository, Controller) remain unchanged.
- Adding
@Agentor@Worker_agentclarifies the domain of each class. - Inheritance respects the business ↔ technical separation, preventing accidental mixing.
Benefits
- Clear separation of business and technical logic.
- Compiler‑checked boundaries reduce accidental cross‑domain inheritance.
- Consistent terminology via declension synonyms.
- Compatibility with SOLID principles, DDD, and Clean Architecture.
- Predictable, readable, and maintainable codebase for new developers.
Further Reading
Full Clprolf manual, specifications, and framework source code are available at: