[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-10 22:08:15 +09:00
parent 21ac3ed255
commit 504fd5fb42
3011 changed files with 380280 additions and 206977 deletions
+192 -108
View File
@@ -2,134 +2,218 @@
id: wiki-2026-0508-anaemic-domain-model
title: Anaemic Domain Model
category: 10_Wiki/Topics
status: needs_review
status: verified
canonical_id: self
aliases: [P-REINFORCE-WIKI-80601CA7]
aliases: [빈약한 도메인 모델, transaction script, getter-setter model, data class]
duplicate_of: none
source_trust_level: A
confidence_score: 0.95
tags: [anaemic-domain-model, transaction-script, domain-model, microservice-architecture, domain-driven-design-(ddd), software-engineering]
source_trust_level: B
confidence_score: 0.88
verification_status: applied
tags: [ddd, anti-pattern, anaemic, transaction-script, oop, domain-model, architecture]
raw_sources: []
last_reinforced: 2026-05-02
last_reinforced: 2026-05-10
github_commit: pending
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
tech_stack:
language: unspecified
framework: unspecified
language: Java / C# / TypeScript
framework: DDD / Clean Architecture
---
# [[Anaemic Domain Model]]
# Anaemic Domain Model
## 📌 Brief 소스에 관련 정보가 부족합니다.
(소스 데이터 내 해당 주제에 대한 구체적이고 상세한 정의는 없으며, 독자 댓글 토론의 일부로만 짧게 등장합니다. 아래 내용은 제한된 단서를 바탕으로 작성되었습니다.)
## 📌 한 줄 통찰
> **"매 data 만 의 class + 매 logic 의 service 의 split"**. Martin Fowler 가 anti-pattern 가 — 매 OOP 의 procedural 화. 매 simple CRUD OK 가, 매 complex domain 의 maintainability 망가짐. **DDD 의 Rich Domain Model 가 답**.
Anaemic Domain Model(빈약한 도메인 모델)은 일반적으로 아키텍처 내에서 안티패턴(anti-pattern)으로 간주되며, 트랜잭션 스크립트(Transaction Script)와 동일한 개념으로 언급됩니다 [1]. 규모가 작고 단순한 애플리케이션에서는 유용할 수 있으나, 분산된 마이크로서비스 환경에서 도메인 로직을 구성하는 방식으로는 적합성에 대한 논쟁이 존재합니다 [1, 2].
## 📖 핵심
## 📖 구조화된 지식 (Synthesized Content)
**소스에 관련 정보가 부족합니다.**
### 매 정의
- 매 entity class 가 getter / setter 만.
- 매 business logic 가 service / manager class.
- 매 data ≠ behavior 의 OO 위반.
제공된 소스에서는 Anaemic Domain Model 자체의 메커니즘을 구체적으로 설명하지 않으며, 단지 마이크로서비스 아키텍처(MSA)에서의 활용 여부에 대한 개발자 간의 토론에서만 등장합니다.
### Fowler 의 비판 (2003)
> "It looks like the real thing... but when you look at the behavior, you realize there is hardly any behavior on these objects, making them little more than bags of getters and setters."
* **마이크로서비스 환경에서의 적용 가능성에 대한 의문:** 모놀리식(Monolithic) 아키텍처에서는 복잡성을 다루기 위해 정교한 구조가 필요하지만, 단일 도메인으로 분할된 마이크로서비스 내부에서는 로직이 크지 않기 때문에 Anaemic Model(트랜잭션 스크립트)을 사용하는 것이 충분히 합리적이지 않은지에 대한 의견이 제기된 바 있습니다 [1].
* **분산된 트랜잭션 스크립트에 대한 비판:** 반면, 애플리케이션을 분해하여 여러 마이크로서비스에 걸쳐 '트랜잭션 스크립트 조각'들을 흩뿌려 놓는 것은 좋은 설계가 아니라는 반론이 존재합니다 [2].
* **대안적 접근:** 빈약한 도메인 모델 대신 '잘 표현된 도메인 모델(well represented domain model)'을 구성하는 것이 소프트웨어 경계를 적절히 설계하고 개별 마이크로서비스를 건강한 방향으로 성장시키는 데 더 합리적입니다 [2].
→ 매 procedural 의 disguise.
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
**소스에 관련 정보가 부족합니다.**
### 매 anti-pattern 의 이유
1. **OOP 의 위반**: 매 encapsulation X, data ≠ behavior.
2. **매 invariant 의 violate**: 매 entity 의 invariant 의 service 가 알아야.
3. **Logic 의 분산**: 매 같은 entity 의 logic 의 매 service 의 spread.
4. **Test 어려움**: 매 service 의 entity mock 의 burden.
5. **DDD 의 Bounded Context 의 약화**.
소스 내용에서 파악할 수 있는 유일한 제약 사항은 다음과 같습니다.
* **비즈니스 로직의 파편화 위험:** Anaemic Domain Model 기반의 트랜잭션 스크립트를 마이크로서비스 환경에 적용할 경우, 비즈니스 로직이 각 서비스 단위로 작게 쪼개져 분산되기만 할 뿐, 적절히 그룹화되거나 명확한 도메인 경계를 갖추지 못해 건강한 서비스 확장을 저해할 위험이 있습니다 [2].
### Anaemic vs Rich
## 🔗 지식 연결 (Graph)
### Related Concepts
#### Anaemic
```ts
class Order {
id: string;
items: Item[];
total: number;
status: 'pending' | 'paid' | 'shipped';
// 매 getter / setter 만.
}
#### [설계 철학 및 패턴]
- [[Transaction Script]]
- 연결 이유: 소스에서 Anaemic Domain Model과 동일한 의미 혹은 유사한 명칭으로 언급되었습니다 [1].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 객체지향적인 도메인 모델이 아니라 절차적으로 비즈니스 로직을 처리하는 단순 설계 패턴의 특성을 이해할 수 있습니다.
- [[Domain Model]]
- 연결 이유: Anaemic Domain Model의 안티패턴적 특성을 극복하기 위한 대안으로 '잘 표현된 도메인 모델'의 필요성이 제기되었습니다 [2].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 마이크로서비스의 생성 시점을 결정하고 비즈니스 로직을 응집력 있게 유지하는 기준점을 이해할 수 있습니다.
#### [아키텍처 스타일]
- [[Microservice Architecture]]
- 연결 이유: 소스에서 Anaemic Domain Model을 적용하는 것이 적절한지를 논의하는 핵심 환경적 배경입니다 [1, 2].
- 이 개념을 통해 더 깊게 이해할 수 있는 부분: 아키텍처가 작게 분리되었다고 해서 내부 비즈니스 로직 설계까지 단순화(스크립트화)해도 되는지에 대한 아키텍처적 트레이드오프를 탐구할 수 있습니다.
### Deeper Research Questions
소스에 정보가 매우 부족하여 원론적인 심층 질문을 생성하는 데 한계가 있으나, 소스의 문맥을 바탕으로 다음과 같은 후속 조사 질문을 도출할 수 있습니다.
- Anaemic Domain Model(트랜잭션 스크립트)이 분산 시스템에서 비즈니스 로직의 파편화를 일으키는 구체적인 기술적 원인은 무엇인가?
- 마이크로서비스 내부의 복잡도가 낮은 경우에도 Anaemic Domain Model 대신 완전한 Domain Model을 채택해야 하는 실무적 기준은 어디에 있는가?
- 트랜잭션 스크립트 모델을 사용할 때 발생하는 모듈성 한계가 도메인 주도 설계(DDD)의 Bounded Context와 어떻게 상충하는가?
- 소규모 애플리케이션에서 Anaemic Domain Model을 사용하는 것이 '충분히 좋다(good enough)'고 평가받는 기술적/비용적 근거는 무엇인가?
- '잘 표현된 도메인 모델'을 마이크로서비스 내에 구축할 때, 데이터의 독점적 상태(Exclusive State) 원칙과 상호작용하는 방식은 무엇인가?
### Practical Application Contexts
- **Implementation:** 소스에 관련 정보가 부족합니다. (다만, 마이크로서비스 내부의 코드를 짤 때 도메인 모델링을 생략하고 절차적 스크립트로 짤지 고민하는 상황과 연관됩니다 [1].)
- **System Design:** 소프트웨어 경계를 분할할 때 단순히 코드를 나누는 것에 그치지 않고, 각 마이크로서비스가 비즈니스 로직을 잘 그룹화하여 가지도록 '잘 표현된 도메인 모델'을 설계해야 합니다 [2].
- **Operation / Maintenance:** 소스에 관련 정보가 부족합니다.
- **Learning Path:** 소스에 관련 정보가 부족합니다.
- **My Project Relevance:** 소스에 관련 정보가 부족합니다.
### Adjacent Topics
- [[Domain-Driven Design (DDD)]]
- 확장 방향: Anaemic Domain Model이 초래하는 로직 파편화를 방지하고, 소스에 언급된 "비즈니스 로직의 적절한 그룹화"를 실현하기 위해 도메인 경계를 도출하는 원리(Bounded Context 등)를 연구하는 방향으로 지식을 확장할 수 있습니다 [2-4].
---
*Last updated: 2026-05-02*
## 📌 한 줄 통찰 (The Karpathy Summary)
> *(TODO: 한 문장으로 핵심 통찰을 작성. "X는 Y 조건에서 Z 효과를 낸다" 구조 권장.)*
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
**언제 이 지식을 쓰는가:**
- *(TODO)*
**언제 쓰면 안 되는가:**
- *(TODO)*
## 🧪 검증 상태 (Validation)
- **정보 상태:** needs_review
- **출처 신뢰도:** A
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
## 🧬 중복 검사 (Duplicate Check)
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
- **처리 방식:** UPDATE (자동 정규화)
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
## 🕓 변경 이력 (Changelog)
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|------|-----------|-----------|--------|
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
## 💻 코드 패턴 (Code Patterns)
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
```text
# TODO
class OrderService {
pay(order: Order, amount: number) {
if (order.status !== 'pending') throw new Error();
if (amount < order.total) throw new Error();
order.status = 'paid';
// 매 logic 의 service.
}
}
```
## 🤔 의사결정 기준 (Decision Criteria)
→ 매 invariant 의 service 가 알아야. 매 다른 service 의 같은 logic 반복.
**선택 A를 써야 할 때:**
- *(TODO)*
#### Rich
```ts
class Order {
private status: 'pending' | 'paid' | 'shipped' = 'pending';
pay(amount: Money) {
if (this.status !== 'pending') throw new InvalidOrderState();
if (amount.isLessThan(this.total)) throw new InsufficientPayment();
this.status = 'paid';
this.events.push(new OrderPaid(this.id));
}
ship() {
if (this.status !== 'paid') throw new InvalidOrderState();
this.status = 'shipped';
}
}
```
**선택 B를 써야 할 때:**
- *(TODO)*
→ 매 invariant 의 entity 자체. 매 logic 의 cohesive.
**기본값:**
> *(TODO)*
### 매 OK 가 case
- **CRUD-only**: 매 simple form / report. 매 logic 거의 없음.
- **Microservice 의 small**: 매 single domain 의 작은 service.
- **Reporting / analytics**: 매 read-only.
- **DTO**: 매 transport 의 data 만.
## ❌ 안티패턴 (Anti-Patterns)
### 매 ❌ case
- **Complex domain**: 매 ordering, billing, accounting.
- **매 invariant 의 많음**: 매 entity 의 rule.
- **Long-lived codebase**: 매 maintenance.
- **Team 의 큰**: 매 logic 의 spread → bug.
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
### DDD 의 답
- **Aggregate**: 매 entity 의 root 가 invariant 보호.
- **Value Object**: 매 immutable + behavior.
- **Domain Service**: 매 entity 의 across 의 logic 만.
- **Repository**: 매 persistence.
- **Domain Event**: 매 state change 의 명시.
## 💻 패턴
### Aggregate root (DDD)
```ts
class CartAggregate {
private items: Map<ProductId, CartItem> = new Map();
add(productId: ProductId, qty: number) {
if (qty <= 0) throw new InvalidQuantity();
const existing = this.items.get(productId);
if (existing) existing.increment(qty);
else this.items.set(productId, new CartItem(productId, qty));
}
remove(productId: ProductId) {
if (!this.items.has(productId)) throw new ItemNotFound();
this.items.delete(productId);
}
total(prices: Map<ProductId, Money>): Money {
return [...this.items.values()].reduce(
(sum, item) => sum.add(prices.get(item.productId)!.times(item.qty)),
Money.zero('USD'),
);
}
}
```
### Value Object (immutable + behavior)
```ts
class Money {
constructor(
public readonly amount: bigint,
public readonly currency: string,
) {}
add(other: Money): Money {
if (this.currency !== other.currency) throw new CurrencyMismatch();
return new Money(this.amount + other.amount, this.currency);
}
times(n: number): Money { return new Money(this.amount * BigInt(n), this.currency); }
isLessThan(other: Money): boolean {
if (this.currency !== other.currency) throw new CurrencyMismatch();
return this.amount < other.amount;
}
static zero(currency: string) { return new Money(0n, currency); }
}
```
### Domain event
```ts
class Order {
private events: DomainEvent[] = [];
pay(amount: Money) {
// ...
this.events.push(new OrderPaid(this.id, amount, new Date()));
}
pullEvents(): DomainEvent[] {
const out = this.events;
this.events = [];
return out;
}
}
// Repository 가 save 시 publish.
```
## 🤔 결정 기준
| 상황 | 모델 |
|---|---|
| Simple CRUD | Anaemic OK |
| Complex business rule | Rich (DDD) |
| Microservice (small) | Anaemic OK |
| Microservice (core domain) | Rich |
| DTO / API contract | Anaemic (data only) |
| Long-lived codebase | Rich |
**기본값**: 매 core domain = Rich. 매 supporting = Anaemic 가 OK.
## 🔗 Graph
- 부모: [[Domain-Driven-Design]] · [[Object-Oriented-Design]]
- 변형: [[Transaction-Script]] · [[Active-Record]] · [[Data-Mapper]]
- 응용: [[Aggregate-Root]] · [[Value-Object]] · [[Domain-Event]] · [[Repository-Pattern]]
- Adjacent: [[Bounded-Context]] · [[CQRS]] · [[Event-Sourcing]] · [[Hexagonal-Architecture]]
## 🤖 LLM 활용
**언제**: 매 backend service design 의 review. 매 DDD 의 적용 결정. 매 legacy 의 refactor.
**언제 X**: 매 quick prototype. 매 simple admin tool.
## ❌ 안티패턴
- **모든 domain 의 anaemic**: 매 OOP 가치 X.
- **Service 의 logic 폭발**: 매 god object.
- **Invariant 의 service / controller 분산**: 매 inconsistent.
- **모든 domain 의 rich**: 매 over-engineering. 매 simple CRUD 의 burden.
- **Anaemic 의 ORM 강제**: 매 framework 의 lock-in.
## 🧪 검증 / 중복
- Verified (Fowler 의 article + DDD 책).
- 신뢰도 B.
- Related: [[Transaction-Script]] · [[Domain-Driven-Design]] · [[Aggregate-Root]].
## 🕓 Changelog
| 날짜 | 변경 |
|---|---|
| 2026-05-08 | Phase 1 |
| 2026-05-10 | Manual cleanup — Fowler 비판 + Rich 예제 + Aggregate code |