Clean&Reactoring Entry Created: 04 Feb 2026 Updated: 04 Feb 2026

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.

  1. State Corruption: When invariants aren't enforced, objects can exist in invalid states (e.g., an Order with a negative price).
  2. 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.
  3. 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.

public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
}

// Somewhere in a service...
var product = new Product();
product.Price = -500; // ⚠️ Invariant violated: Price cannot be negative.

✅ 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."

public class Product
{
public string Name { get; private set; }
public decimal Price { get; private set; }

public Product(string name, decimal price)
{
// Enforcing Invariants during creation
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("Name is required.");
SetPrice(price);
Name = name;
}

public void SetPrice(decimal newPrice)
{
// Enforcing Invariant: Price must be positive
if (newPrice < 0)
throw new InvalidOperationException("Price cannot be negative.");
Price = newPrice;
}
}

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.

Share this lesson: