본문으로 건너뛰기

도메인 모델 문서 작성 가이드

Template

문서 순서

도메인 모델은 다음 순서로 작성:

  1. Domain Events (내부/외부 이벤트)
  2. Entities (엔티티 속성)
  3. Enums (열거형)
  4. Value Objects (값 객체)
  5. Aggregates (집합체 상세)
  6. Error Codes (에러 코드)

Domain Events 작성

Internal Events vs External Events:

Internal Events (내부 이벤트):

  • 해당 도메인/컨텍스트 내에서만 발생하고 처리
  • 같은 Context 내 다른 Aggregate나 Service가 구독
  • 예: CallCompleted → CallBannerService (같은 Support Context)

External Events (외부 이벤트):

  • Outbound: 다른 Context로 발행하는 이벤트
  • Inbound: 다른 Context에서 받아서 처리하는 이벤트
  • 예: CallCompletedNotification → Notification Context (다른 Context)

작성 형식:

**EventName**
- Payload: `field1`, `field2`, `field3`
- Trigger: 발생 조건
- Publisher: 발행 주체
- Subscribers: 구독자들

작성 팁:

  • Payload는 필드명만 나열 (타입은 생략 가능)
  • Trigger는 언제 발생하는지 간단히
  • Publisher는 Aggregate명
  • Subscribers는 쉼표로 구분

Entities 작성

작성 형식:

**EntityName** (Aggregate Root 여부 표시)
- `propertyName`: 설명 (타입)

속성 표현:

  • 개념 수준으로 작성 (구현 상세 X)
  • 타입은 괄호 안에 간단히
  • Enum이나 VO 타입은 이름으로 참조

좋은 예:

**AppUserOutboundCallPlan** (Aggregate Root)
- `id`: 콜 계획 고유 식별자
- `userId`: 대상 회원 ID
- `status`: 콜 상태 (CallStatus)

나쁜 예:

**AppUserOutboundCallPlan**
- `id`: UUID String ❌ 너무 구현 상세
- `user_id`: Long ❌ 컬럼명 스타일
- `status_code`: Integer ❌ 의미 불명확

Enums 작성

언제 사용하나:

  • 정해진 값 집합 (상태, 타입, 카테고리 등)
  • 값의 의미가 명확하고 제한적일 때
  • 예: CallStatus, CompletionReason, NotificationType

작성 형식:

**EnumName** - 설명
- `VALUE1`: 설명
- `VALUE2`: 설명

작성 예시:

**CallStatus** - 콜의 상태
- `Draft`: 초안 (생성 직후)
- `Active`: 활성 (처리 중)
- `Completed`: 완료
- `Cancelled`: 취소

Value Objects 작성

Entity vs Value Object 구분:

구분EntityValue Object
식별ID로 식별속성 값으로 식별
변경상태 변경 가능불변 (교체만 가능)
예시Order, UserMoney, Address, PhoneNumber

작성 형식:

**VOName**
- `property1`: 설명 (타입)
- `property2`: 설명 (타입)
- 동등성: 비교 기준

동등성 기준:

  • 어떤 필드들이 같으면 같은 객체인지 명시
  • 예: "content 값이 동일하면 같은 객체"
  • 예: "number와 countryCode가 모두 동일하면 같은 객체"

작성 예시:

**PhoneNumber**
- `number`: 전화번호 (String, 숫자만)
- `countryCode`: 국가 코드 (String)
- 동등성: number와 countryCode가 모두 동일하면 같은 객체

Aggregates 작성

구현 중심 접근:

바운디드 컨텍스트 문서에서 이미 Aggregate의 책임과 개념을 정리했으므로, 도메인 모델에서는 실제 구현에 필요한 메서드와 Repository를 정리합니다.

작성 구조:

**AggregateName**

**Methods:**
- 메서드 목록 (상태 변경)
- 메서드 목록 (조회)

**Repository:**
- 저장소 메서드

**Events Published:**
- 발행하는 이벤트 목록

Methods:

  • Aggregate의 상태를 변경하는 메서드

  • 메서드명(파라미터) 형태로 간단히

  • 예: completeCall(completedBy, reason)

  • 비즈니스 로직의 진입점

  • Aggregate의 상태를 조회하는 메서드

  • 상태 변경 없이 정보만 반환

  • 예: getCallStatus(), isCompletable()

Repository:

  • Aggregate를 저장/조회하는 메서드
  • CRUD 기본 + 도메인 특화 조회
  • 예: findByUserId(), findActiveCallsByUser()

Events Published:

  • 이 Aggregate가 발행하는 이벤트
  • Domain Events 섹션과 연결
  • 예: CallCompleted, MemoAdded

작성 예시:

**AppUserOutboundCallPlan**

**Methods:**
- `createCallPlan(userId, ...)`
- `completeCall(completedBy, reason)`
- `cancelCall(cancelledBy, reason)`
- `getCallStatus()`
- `isCompletable()`

**Repository:**
- `findById(id)`
- `findByUserId(userId)`
- `save(callPlan)`

**Events Published:**
- `CallCompleted`
- `CallCancelled`

작성 팁:

  • Methods는 동사로 시작 (create, complete, cancel, add...)
  • Query Methods는 get/is/has로 시작
  • Repository는 find/save/delete
  • 구현 상세(리턴 타입, 예외)는 생략

Domain Services 작성 (선택)

언제 Domain Service가 필요한가:

Domain Service는 다음 경우에만 추가:

  1. 여러 Aggregate에 걸친 로직
    • 예: 콜 할당 로직 (사용자 정보 + 콜 계획 확인)
  2. 외부 시스템과의 도메인 로직
    • 예: 외부 인증 서비스 호출
  3. 복잡한 계산 로직
    • 예: 가격 계산 (여러 정책 적용)

작성 형식:

## 6. Domain Services (선택)

**ServiceName**
- `methodName(params)`: 설명 [관련 Aggregate들]

작성 예시:

## 6. Domain Services

**CallAssignmentService**
- `assignCallToOperator(callId, operatorId)`: 콜을 운영자에게 할당 [CallPlan, Operator]
- `findAvailableOperator()`: 사용 가능한 운영자 찾기 [Operator]

**PriceCalculationService**
- `calculateDiscount(userId, amount)`: 사용자별 할인율 계산 [User, Order]
- `applyPromotionRules(orderId)`: 프로모션 규칙 적용 [Order, Promotion]

Aggregate 표시:

  • 대괄호 [...] 안에 관련 Aggregate 나열
  • 여러 Aggregate일 경우 쉼표로 구분
  • 이 메서드가 어떤 Aggregate들과 협업하는지 명확히

Domain Service vs Aggregate 판단:

  • 하나의 Aggregate에만 속한다 → Aggregate 메서드로
  • 여러 Aggregate 조율한다 → Domain Service로
  • 트랜잭션 관리가 필요하다 → Application Service로 (별도 문서)

주의사항:

  • Domain Service는 최소한으로 유지
  • 가능하면 Aggregate 메서드로 해결
  • 남용하면 빈약한 도메인 모델(Anemic Domain Model)이 됨

Error Codes 작성

코드 체계:

  • 4자리 숫자 (도메인 prefix 없음)
  • 4XXX: 클라이언트 오류
  • 5XXX: 서버 오류

작성 형식:

**4xx - Client Errors**
- `4001` ERROR_NAME (HTTP Status): 설명

**5xx - Server Errors**
- `5001` ERROR_NAME (HTTP Status): 설명

HTTP Status 매핑:

  • 400: 잘못된 요청 (유효성 실패)
  • 403: 권한 없음
  • 404: 리소스 없음
  • 409: 충돌 (이미 처리됨, 상태 불일치)
  • 500: 내부 서버 오류

작성 예시:

**4xx - Client Errors**
- `4001` CALL_ALREADY_COMPLETED (409): 이미 완료된 콜
- `4002` INVALID_CALL_STATUS (400): 유효하지 않은 상태 전이
- `4003` UNAUTHORIZED_ACTION (403): 권한 없음

**5xx - Server Errors**
- `5001` INTERNAL_ERROR (500): 내부 서버 오류

작성 순서 및 흐름

도메인 모델은 다음 흐름으로 작성:

  1. 이벤트 스토밍에서 도출 → Domain Events
  2. Aggregate 매핑에서 도출 → Entities, Aggregates
  3. 속성 분석 → Enums, Value Objects
  4. 비즈니스 규칙 연계 → Error Codes

작성 팁:

  • 한 번에 완성하려 하지 말고 반복적으로 정제
  • Aggregate부터 작성하면 Entity/VO 구조가 명확해짐
  • Error Codes는 비즈니스 규칙과 1:1 매핑

  • 500: 내부 서버 오류

Support 도메인 예시

요구사항 → 이벤트 스토밍 흐름 예시

요구사항:

SPT-FR-BE-006: 운영자가 콜을 완료 처리할 수 있어야 한다.

이벤트 스토밍:

  • Command: CompleteOutboundCall
  • Actor: OperationUser
  • Event: OutboundCallCompleted
  • Aggregate: AppUserOutboundCallPlan

이벤트 스토밍 → 비즈니스 규칙 흐름 예시

정책:

OutboundCallCompletionRequested → 이미 완료된 콜이면 거부

비즈니스 규칙:

Rule NameAggregateEnforcementViolation ResponseRelated Req
완료된 콜 재처리 방지AppUserOutboundCallPlanCommand 실행 시거부 이벤트SPT-FR-BE-008

비즈니스 규칙 → 에러 코드 흐름 예시

비즈니스 규칙:

완료된 콜 재처리 방지

에러 코드:

Error CodeError NameHTTP StatusDescriptionTrigger ConditionRelated Rule
SPT-4001CALL_ALREADY_COMPLETED409이미 완료된 콜완료된 콜에 완료 요청 시완료된 콜 재처리 방지