If you create a getter, shout "I'm breaking encapsulation" through its name.
Core Principles
For domain model getters, breachEncapsulationOf Add a prefix to make explicit that encapsulation is being broken.
| Approach | Characteristics | Effect |
|---|---|---|
Normal getter (getName()) |
Easy to use casually | Prone to abuse |
Explicit getter (breachEncapsulationOfName()) |
Awareness of "breaking it" when used | Suppresses abuse |
Why This Naming Convention Is Needed
Dilemma
- Tell Don't Ask Principle: Don't use getters; command the object
- Real-world Constraints: Getters are needed for persistence and JSON conversion
- Problem: If a getter exists, it ends up being used in business logic too
Solution
By giving getters "long, conspicuous names":
- Make the developer aware each time it's used that "this is an exceptional use"
- Make it easier to spot during code review
- Make it detectable by static analysis tools
Naming Patterns
Basic Form
breachEncapsulationOf<PropertyName>()
Examples by Language
// Java
public String breachEncapsulationOfName() { return this.name; }
public Money breachEncapsulationOfPrice() { return this.price; }
// Kotlin
fun breachEncapsulationOfName(): String = name
fun breachEncapsulationOfPrice(): Money = price
// TypeScript
breachEncapsulationOfName(): string { return this.name; }
breachEncapsulationOfPrice(): Money { return this.price; }
# Python
def breach_encapsulation_of_name(self) -> str:
return self._name
// Go
func (u *User) BreachEncapsulationOfName() string { return u.name }
// Rust
pub fn breach_encapsulation_of_name(&self) -> &str { &self.name }
Applicability Decision Flow
getterが必要か?
↓
├─ NO → getterを作らない(Tell Don't Ask)
│
└─ YES → 対象は?
│
├─ 値オブジェクト → 通常のアクセサでOK
│ (イミュータブルかつ振る舞いが限定的なため)
│ 例: Money.amount(), UserId.value()
│
└─ エンティティ → breachEncapsulationOf を使用
│
└─ なぜ必要?
├─ 永続化/シリアライズ → ✅ 許容
├─ 表示/UI → ✅ 許容
├─ テスト → ✅ 許容
└─ ビジネスロジック → ❌ Tell パターンに変換
Value Objects vs Entities
| Type | Characteristics | Getter Policy |
|---|---|---|
| Value Object | Immutable, identified by equality | Normal accessors allowed (amount(), value()) |
| Entity | Mutable, identified by ID | breachEncapsulationOf Use |
Reason: Since a value object's internal state does not change, even if a getter is exposed, the Ask pattern of "get state → judge externally → update" is unlikely to occur.
Anti-pattern Detection
Warn when the following patterns are found:
// ❌ breachEncapsulationOf + if → Tell Don't Ask違反
if (user.breachEncapsulationOfAge() >= 18) {
// ロジック
}
// ❌ breachEncapsulationOf + 計算 → ロジックが外部に漏れている
total = item.breachEncapsulationOfPrice() * item.breachEncapsulationOfQuantity();
// ❌ 連鎖呼び出し → デメテルの法則違反
order.breachEncapsulationOfCustomer().breachEncapsulationOfAddress().getCity();
Acceptable Use Cases
1. Persistence Layer (Repository Implementation)
// ✅ 永続化のためのアクセスは許容
public UserEntity toEntity(User user) {
return new UserEntity(
user.breachEncapsulationOfId(),
user.breachEncapsulationOfName(),
user.breachEncapsulationOfEmail()
);
}
2. JSON/XML serialization
// ✅ DTOへの変換は許容
toJson(): UserJson {
return {
id: this.breachEncapsulationOfId(),
name: this.breachEncapsulationOfName()
};
}
3. Assertions in tests
// ✅ テストでの検証は許容
@Test
void shouldChangeName() {
user.rename("New Name");
assertEquals("New Name", user.breachEncapsulationOfName());
}
4. Debug/Log Output
# ✅ デバッグ目的は許容
logger.debug(f"User: {user.breach_encapsulation_of_name()}")
Implementation Guidelines
1. Domain Model Side
public class User {
private final UserId id;
private String name;
private Email email;
// ❌ 通常のgetterは作らない
// public String getName() { return name; }
// ✅ カプセル化を破ることを明示
public String breachEncapsulationOfName() {
return name;
}
// ✅ ビジネスロジックは振る舞いとして提供
public void rename(String newName) {
validateName(newName);
this.name = newName;
}
public boolean hasName(String name) {
return this.name.equals(name);
}
}
2. Use in the Infrastructure Layer
// リポジトリ実装
public class JpaUserRepository implements UserRepository {
@Override
public void save(User user) {
UserEntity entity = new UserEntity();
entity.setId(user.breachEncapsulationOfId().value());
entity.setName(user.breachEncapsulationOfName());
entity.setEmail(user.breachEncapsulationOfEmail().value());
jpa.save(entity);
}
}
Code Review Perspectives
| Check Item | Response |
|---|---|
breachEncapsulationOf + if |
Suggest conversion to the Tell pattern |
breachEncapsulationOf + calculation |
Move the calculation logic into the object |
| Use in the domain layer | Warn if it is for anything other than persistence/serialization |
| Chained calls | Suggest adding a delegation method |
Normal getter (getName()) |
breachEncapsulationOf Suggest changing to |
Integration with Static Analysis
Custom Lint Rule Example
# 検出ルール
1. breachEncapsulationOf の後に if/switch が続く → 警告
2. ドメイン層で breachEncapsulationOf を呼び出している → 警告
3. 通常の get プレフィックスがドメインモデルにある → 警告
Related Skills
| Skill | Relationship |
|---|---|
| tell-dont-ask | Prerequisite for this skill. Prioritize designs that do not use getters |
| first-class-collection | Apply the same principle to collection encapsulation |
| domain-building-blocks | Consistency with value object design |
References
- Katojun "Getter/Setter for Domain Objects" (2018)
- https://blog.j5ik2o.me/entry/2018/08/14/134125
- The original source for this skill. The historical degradation of encapsulation and the proposal of the breachEncapsulationOf naming convention
Detailed Guidelines
For detailed implementation patterns by language, refer to references/patterns.md .
Related Skills (Recommended for Concurrent Reading)
When using this skill, also refer to the following skills:
tell-dont-ask: Foundational principles for why getters should be avoidedlaw-of-demeter: Structural principles that getter chaining violatesfirst-class-collection: Collection encapsulation patterns