What is Invariants?
In software engineering, an invariant is a business rule or condition that must always remain true throughout the lifetime of an object. It is the "internal truth" of your domain model. If an invariant is violated, the object enters a corrupt state, leading to unpredictable behavior and system failure.
Invariants vs. System Fragility
A Fragile System is characterized by "rigidity" and "unexpected breakage." When you change code in one module, something unrelated breaks elsewhere. This fragility is often the direct result of leaky invariants.
- State Corruption: When invariants aren't enforced, objects can exist in invalid states (e.g., an
Orderwith a negative price). - Logic Duplication: If the object doesn't protect its own rules, every service using that object must implement the same "if" checks. If one developer forgets a check, the system breaks.
- Loss of Trust: Developers lose trust in the data. This leads to "defensive programming," where the code is littered with redundant null checks and validations, making it harder to read and maintain.
C# Example: From Fragile to Robust
As a .NET developer, you likely encounter "Anemic Domain Models" where classes are just collections of public properties. This is a recipe for fragility.
❌ The Fragile Approach (Anemic Model)
In this example, the Product class has no control over its invariants. Any external service can set the price to a negative value or leave the name empty.
✅ The Robust Approach (Encapsulated Invariants)
By using Encapsulation, we ensure that the object is never in an invalid state. The constructor and methods act as "gatekeepers."
Invariants in Domain-Driven Design (DDD)
In your work with DDD, invariants are the primary reason for defining Aggregates. An Aggregate Root is the boundary within which invariants must be satisfied before any state change is committed to the database.
By protecting these boundaries, you transform a fragile, "easy-to-break" system into a robust one where the code itself prevents bugs from happening.