Before SOLID.
Before Clean Architecture.
Before Design Patterns.
There was one fundamental idea:
Objects must protect their own state.
Encapsulation is not about hiding properties.
It is about preserving invariants and controlling state transitions.
If your object can enter an invalid state,
you don’t have encapsulation.
You have a mutable data structure with methods attached.
A Brief Historical Context
Encapsulation was formalized in the 1970s with Simula and later reinforced by Smalltalk.
The motivation was simple:
Procedural systems relied heavily on shared mutable state.
That led to:
- Hidden side effects
- Fragile code
- Tight coupling
- Hard-to-reason systems
Object-Oriented Programming proposed a radical constraint:
Data should only be modified through behavior defined by the object itself.
Encapsulation was born as a protection mechanism.
What Encapsulation Actually Means
Encapsulation is:
- Restricting direct access to internal state
- Exposing behavior instead of raw data
- Enforcing invariants at construction and mutation
- Preventing invalid state transitions
Encapsulation is not:
- Just using
private - Generating getters and setters for everything
- Hiding data without protecting it
Encapsulation is about control.
❌ The Illusion of Encapsulation
Let’s look at a common example.
class BankAccount
{
public float $balance = 0.0;
public function deposit(float $amount): void
{
$this->balance += $amount;
}
public function withdraw(float $amount): void
{
$this->balance -= $amount;
}
}
What’s wrong?
- Public mutable state
- No invariant protection
- Negative balances allowed
- Invalid transitions possible
This is not an object.
It’s a struct with methods.
The Real Problem: Broken Invariants
An invariant is a condition that must always be true.
For a bank account:
- Balance cannot be negative
- Deposit must be positive
- Withdraw cannot exceed balance
With the previous implementation:
$account = new BankAccount();
$account->balance = -1_000_000;
Your domain is corrupted.
Encapsulation failed.
✅ Proper Encapsulation in PHP
final class BankAccount
{
private float $balance;
public function __construct(float $initialBalance)
{
if ($initialBalance < 0) {
throw new InvalidArgumentException(
'Initial balance cannot be negative.'
);
}
$this->balance = $initialBalance;
}
public function deposit(float $amount): void
{
if ($amount <= 0) {
throw new InvalidArgumentException(
'Deposit amount must be positive.'
);
}
$this->balance += $amount;
}
public function withdraw(float $amount): void
{
if ($amount <= 0) {
throw new InvalidArgumentException(
'Withdraw amount must be positive.'
);
}
if ($amount > $this->balance) {
throw new RuntimeException(
'Insufficient funds.'
);
}
$this->balance -= $amount;
}
public function balance(): float
{
return $this->balance;
}
}
Notice the structural improvements:
-
privatestate - Guard clauses
- Controlled transitions
-
finalclass (prevent unsafe inheritance)
Now the object:
✔ Cannot be constructed invalid
✔ Cannot transition to invalid state
✔ Protects its invariants
✔ Exposes behavior, not data
This is encapsulation.
Encapsulation and State Transitions
Encapsulation is fundamentally about controlling transitions.
Instead of:
$account->balance = 500;
We model domain behavior:
$account->deposit(500);
Why does this matter?
Because behavior is extensible.
Inside deposit() we could later:
- Dispatch domain events
- Log transactions
- Validate limits
- Apply fees
- Trigger auditing
- Wrap in transaction boundaries
Encapsulation enables architectural evolution.
Direct state mutation blocks it.
The Getter/Setter Trap
Many developers think this is encapsulation:
private float $balance;
public function getBalance(): float
{
return $this->balance;
}
public function setBalance(float $balance): void
{
$this->balance = $balance;
}
This is not encapsulation.
This is a public property with ceremony.
The invariant is still unprotected.
If you can arbitrarily set state,
your object is still fragile.
Encapsulation in Domain-Driven Design
In DDD, an Aggregate Root must:
- Protect its invariants
- Control all state changes
- Prevent invalid operations
Example of what not to do:
public function setStatus(string $status): void
{
$this->status = $status;
}
Better:
public function approve(): void
{
if ($this->status !== 'pending') {
throw new DomainException('Only pending orders can be approved.');
}
$this->status = 'approved';
}
Behavior-driven transitions preserve consistency.
Encapsulation is what makes aggregates reliable.
Advanced Technique: Immutability with readonly
PHP 8.1 introduced readonly.
For value objects:
final class Money
{
public function __construct(
public readonly int $amount,
public readonly string $currency
) {
if ($amount < 0) {
throw new InvalidArgumentException('Amount cannot be negative.');
}
}
}
Immutable objects are the strongest form of encapsulation.
They:
- Prevent mutation entirely
- Eliminate side effects
- Improve reasoning
- Reduce concurrency issues
Encapsulation + Immutability = Stability.
Common Anti-Patterns
1️⃣ Anemic Domain Model
class User
{
public string $email;
public string $password;
}
All business logic lives in services.
This is procedural code disguised as OOP.
2️⃣ Returning Internal Collections
public function items(): array
{
return $this->items;
}
External code can mutate internal state.
Better:
- Return copies
- Return iterators
- Provide
addItem()/removeItem()methods
The Architectural Impact
Without encapsulation:
- Inheritance becomes dangerous
- Polymorphism becomes unpredictable
- Abstractions leak
- Concurrency issues increase
- Testing becomes harder
Encapsulation is not a syntax feature.
It is an architectural constraint.
Final Insight
The goal of encapsulation is simple:
Make invalid states unrepresentable.
If your object depends on external discipline to remain valid,
it is not encapsulated.
Encapsulation is the first pillar for a reason.
It protects your domain from chaos.
Thanks for reading!
If you have any questions, complaints or tips, you can leave them here in the comments. I will be happy to answer!
😊😊 See you! 😊😊
Support Me
Youtube - WalterNascimentoBarroso
Github - WalterNascimentoBarroso
Codepen - WalterNascimentoBarroso
Top comments (0)