DEV Community

Cover image for How to write more stable and testable code 👉 use encapsulation
Oleksandr Tokariev
Oleksandr Tokariev

Posted on

How to write more stable and testable code 👉 use encapsulation

Encapsulation is often introduced as a basic OOP principle: “hide state, expose behavior.”

But its real value becomes obvious when you start writing tests — especially for domain logic.

Let’s look at a simple finance tracker example: a Wallet that holds a balance and a list of transactions.

The non-encapsulated wallet


In a non-encapsulated design, the wallet is just a data holder. The business rules live elsewhere — for example, in a WalletRepository.

Tests look reasonable at first:

  • adding INCOME(500) increases the balance
  • adding INCOME(0) throws an exception
  • adding an expense larger than the balance is rejected

These tests pass, and everything seems fine.

But there’s a hidden assumption: all callers must always go through the repository.

The moment a caller bypasses it, the model breaks:

  • anyone can append directly to wallet.transactions
  • anyone can set wallet.balanceCents = -50

The wallet can no longer defend its own invariants.

Tests don’t verify the domain — they verify “correct usage” by convention.

This is the real cost of missing encapsulation.

The encapsulated wallet


In the encapsulated version, the rules move into the Wallet itself.

The wallet:

  • keeps its state private
  • exposes read-only views (List<Transaction>)
  • provides a single domain operation (wallet.add(tx))

Now the tests change in an important way. They no longer care who calls the wallet — only what happens when valid or invalid transactions are applied.

Invalid inputs are rejected at the boundary.

Impossible states cannot be constructed.

Bypassing the rules is no longer an option.

The key difference is not the if statements — it’s where they live.

Why this matters

Encapsulation turns your domain objects into the authority over their own correctness.

As a result:

  • tests become simpler and more meaningful
  • refactors break fewer tests
  • invariants are enforced consistently, everywhere

Encapsulation isn’t just about clean design.

It’s a way to make your tests trustworthy — and your domain harder to misuse.

Top comments (0)