Skip to main content

Protecting Data Through Structure

Type safety helps prevent invalid states by ensuring that values are used correctly. However, types alone are not always enough to protect the integrity of a system.

Good system design also requires structuring code so that important rules cannot be bypassed. This means controlling how data can be read and modified.

When internal data can be changed freely from anywhere in the system, it becomes easy to accidentally break important rules.

Protecting Business Logic

A common mistake is exposing internal data in ways that allow callers to bypass business logic.

Example: Rules That Can Be Bypassed

Consider a class that represents a bank account.

public class Account
{
public decimal Balance;

public void Withdraw(decimal amount)
{
if (amount > Balance)
throw new Exception("Insufficient funds");

Balance -= amount;
}
}

At first glance this seems reasonable. The Withdraw method ensures that the account cannot go negative.

But the Balance field is public.

This means code elsewhere can bypass the business logic entirely:

account.Balance = -500;

The class attempts to enforce a rule through the Withdraw method, but the public field allows that rule to be ignored.

The structure of the class allows invalid states to exist.

Enforcing Rules Through Structure

A safer design prevents direct modification of the balance.

public class Account
{
public decimal Balance { get; private set; }

public void Withdraw(decimal amount)
{
if (amount > Balance)
throw new Exception("Insufficient funds");

Balance -= amount;
}
}

Now the balance can only change through the methods defined by the class.

This ensures that all changes to the balance go through the same validation rules.

By controlling access to internal data, we make it much harder for the system to enter an invalid state.

Design Principle

When designing classes and models, important rules should be enforced by the structure of the code, not just by convention.

Good structure ensures that:

  • critical data cannot be modified directly
  • validation logic cannot be bypassed
  • invalid states are difficult or impossible to create

This approach complements type safety. Types help ensure that data has the correct shape, while good structure ensures that data changes follow the correct rules.

Questions to Ask When Designing Data Models

When designing classes or data models, consider:

  • Can important data be modified directly from outside the class?
  • Are there rules that callers could accidentally bypass?
  • Does the structure of the class enforce the rules of the system?
  • Could this object enter an invalid state?

If the answer to any of these questions is yes, the structure of the code likely needs to be improved.