Strengthening Domain Logic: Refactoring for Robustness in mediTurn
In the mediTurn project, we recently completed a refactor of our core domain model. As our application has grown, it became clear that relying on simple data structures was making our business logic fragmented and hard to maintain. To address this, we shifted towards a more encapsulated approach that prioritizes domain integrity.
The Problem: Anemic Models
Initially, our models were acting as mere data containers. Business rules were scattered across service layers, leading to a "leaky" architecture where validation logic was duplicated or ignored. Imagine trying to build a complex puzzle where every piece is just a square; without unique shapes, you end up forcing pieces together, which inevitably leads to a fragile structure.
The Strategy: Encapsulation and Domain Integrity
We decided to move the business rules directly into the domain objects. By treating our models as true representations of the business, we ensure that an object is never in an invalid state.
We implemented a pattern where models enforce their own invariants through constructor logic and domain-specific methods:
public class Appointment {
private LocalDateTime startTime;
public Appointment(LocalDateTime startTime) {
if (startTime.isBefore(LocalDateTime.now())) {
throw new InvalidAppointmentDateException("Date cannot be in the past");
}
this.startTime = startTime;
}
public void reschedule(LocalDateTime newTime) {
// Rule enforcement logic here
}
}
Custom Exceptions for Clearer Errors
By introducing custom exceptions, we transitioned from generic error messages to descriptive, domain-aware feedback. This makes debugging significantly faster and provides a clear contract for the UI or API layers consuming these models. When an InvalidAppointmentDateException is thrown, the calling code knows exactly what went wrong without inspecting error codes or deep stacks.
The Results
This refactoring resulted in:
- Predictability: Business rules are centralized.
- Testability: We can now write unit tests for the domain model in isolation.
- Readability: The codebase clearly communicates the intent of the business.
Takeaway
Stop letting your services manage the state of your data. Move your core business logic into your domain models and protect them with custom exceptions. Start by identifying one core entity in your system and refactor it to enforce its own constraints today.
Generated with Gitvlg.com