본문 바로가기
MSA (Micro Service Architecture)/Legacy to Domain-Driven Platform

[MSA 말고 Modular Monolith] 3편 — 도메인 설계: Bounded Context 와 Aggregate 정의

by kellis 2026. 7. 1.

 


이 시리즈는 글쓴이 본인이 수행한 레거시 서비스를 리뉴얼 전환한 과정에서 수행한 의사 결정 사항들을 정리한 글입니다. 
실제 운영 중인 PHP 레거시 에듀테크 플랫폼을 Python + React + K3S 환경으로 전환하는 과정에서 
아키텍처 설계와 인프라 구성에 대한 내용이 포함되어 있습니다. 

 

목차

 

 

 

 


 

2편에서는 전략적 설계(Strategic Design)을 다루었다. 큰 도메인을 어디에서 나눌 것인지, 경계를 어떻게 그을지를 다루었고, 6가지 기준으로 도메인 후보를 도출했다. 

 

이번 화에서는 그 경계 안으로 들어가 전술적 설계(Tactical Design)을 살펴보려 한다. 

 

2편 전략적 설계가 어디에 벽을 세울 것인가 (Bounded Context) 였다면, 

3편 전술적 설계는 벽 안의 방을 어떻게 꾸밀 것인가 (Aggregate, Entity, ...)

 

에 가깝다. 

 

핵심은 두 가지다. 

  1. DDD의 전술적 building block들(Entity, Value Object, Aggregate, Domain Event)이 각각 무엇이고 언제 사용하는가.
  2. 그중 가장 어렵고 중요한 ! Aggregate를 어떻게 설계하는가 

 

 


 

 

1. 도메인 후보를 Bounded Context 로 확정하다

먼저 2편에서 도출한 후보를 가지고 최종 경계를 확정했다. 6가지 기준을 이용하여 재검토 후 아래와 같이 8개 Bounded Context로 수렴시켰다. 

Bounded Context 책임 성격(Subdomain 분류)
Identity 인증, 계정, 토큰, 권한 Generic
Academy 학원, 교사, 학생, 교실 Supporting
Content 교재, 단원, 문항 Supporting
Assessment 출제, 시험지, 라이브러리 Core
Learning 학습, 채점, 추천, 리포트 Core
Billing 결제, 청구 Generic
Notification 이벤트 기반 알림 (SSE / FCM) Generic
Support 공지, FAQ, 문의 Generic

 

변경된 사항에 대한 판단은 아래와 같다

 

  • "조직" + "교실/수강" = Academy 
    • 기준 2 (함께 조회) 와 기준 3 (생명주기)을 적용하니, 교실은 학원 없이 존재할 수 없고, 항상 함께 다뤄졌다. 둘을 나누자니 경계를 넘나드는 조회가 너무 많아질 것 같아 Academy로 통합했다. 
  • Assessment 와 Learning 의 분리 
    • "출제" 와 "학습" 은 비슷해보이지만 책임 주체 (기준 6)가 다르다.
    • 출제는 교사가 시험지를 만드는 행위 (Assessment)
    • 학습은 학생이 문제를 풀고 결과가 쌓이는 행위 (Learning) 
    • 변경 이유도 다르다 (기준1) 
  • 리포트는 Learning에 흡수 
    • 리포트 및 통계는 학습 데이터의 파생물이라 독립 컨텍스트로 두지 않고 Learning의 읽기 모델로 넣었다. 
  • Billing 
    • 결제 (payment)·청구 (billing) 모두 담당한다. 주로 정기 구독 결제가 대부분이라 Billing으로 컨텍스트명을 정했다.

 


 

 

2. 전술적 설계의 Building Blocks

경계를 확정했으니, 이제 각 경계 을 채울 차례이다. DDD는 경계 안을 모델링하는 기본재료들을 정의해두었다. 

 

Entity 

식별자(ID)로 구분되며, 시간이 지나도 동일성이 유지되는 객체

하나의 학생은 이름이 바뀌어도, 학년이 올라가도 동일한 학생이다. 속성이 변해도 ID를 통해 "같다"는 것을 안다. 이것이 Entity이다.

 

Value Object

식별자가 없고, 값 자체로 정의되는 객체

예를 들어, "점수 85점" 이라거나 "기간 2026-07-01" 같은 것들을 의미한다. ID가 필요하지 않는 값.

값이 같으면 같은 것으로 취급한다. 두개의 "85점"은 구별할 이유가 없다. 

Value Object는 불변(immutable)로 다루는 것이 원칙이다. 변경이 필요할 경우 새 값으로 교체한다. 

 

Domain Service

특정 Entity에 속한다고 보기 어려운 도메인 로직

예를 들어, "학생의 학습 데이터를 분석하고 다음 학습을 추천"하는 로직은 Student라고도, Study라고 보기에도 딱 들어맞지 않는다. 여러 객체에 걸친 이러한 로직을 Domain Service로 둔다.

 

Domain Event

도메인에서 일어난 의미있는 사건

"시험이 배정되었다", "학습이 완료되었다." "결제가 완료되었다." 같은 것들을 의미한다. 

가장 중요한 개념이며, Cross DB 문제를 도메인 간 직접 호출 대신 이 Domain Event를 이용해 풀어내고자 한다. 

 

Aggregate

하나로 다루어지는 도메인 객체들의 묶음

가장 중요한 개념으로 다음 장에서 바로 이어 설명해보겠다. 

 

 


 

 

3. Aggregate — 가장 중요하고 가장 어려운 것

Aggregate 란?

Aggregate는 하나의 단위로 다루어지는 도메인 객체들의 묶음이다. 여러 Entity와 Value Object가 모여 하나의 일관성 단위를 이룬다. 

각 Aggregate에는 Aggregate Root(루트)가 하나 있다. 

외부에서 이 묶음에 접근할 수 있는 유일한 입구이고, 내부의 다른 객체들은 반드시 루트를 통해서만 접근한다.

 

 

핵심 : Aggregate는 "일관성 경계"

Aggregate를 이해하기 위해서는 아래 문장을 알아야 한다. 

Aggregate의 경계는 일관성 경계 (Consistency Boundary) 이다.

 

Aggregate 안의 모든 규칙(불변식, invariant)은 하나의 트랜잭션 안에서 항상 참이어야 한다는 뜻이다. 묶음 안의 무언가가 바뀌면, 그 즉시 전체가 해당 규칙을 만족해야 한다. 

 

 

Vaughn Vernon은 "Effective Aggregate Design"에서 이 원칙을 정리했다. 

 

규칙 1. 진짜 불변식을 일관성 경계 안에 모델링하라 

함께 + 항상 참 이어야 하는 규칙들을 한 Aggregate로 묶는다.

 

규칙 2. 작게 설꼐하라

Aggregate가 크면 트랜잭션 충돌 혹은 성능 문제가 생긴다.

 

규칙 3. 다른 Aggregate는 ID로만 참조하라

객체 직접 참조가 아닌 식별자로 참조한다.

 

규칙 4. 경계 밖은 결과적 일관석(Eventual Consistency)

다른 Aggregate 변경은 Domain Event 로 비동기 처리한다. 

 

 

불변식(Invariant)이 경계를 결정한다.

관계가 있다고 한 Aggregate로 묶는게 아니다. 도메인 모델에서는 거의 모든 것이 다른 무언가와 연결되어 있다. 관계는 묶음의 이유가 되지 못한다.

묶음을 만드는 진짜 이유는 함께 지켜져야 하는 규칙(불변식) 이다.

경계는 Entity 사이의 관계가 아니라, 불변식을 중심으로 긋는다.

 

 

예를들어, "수강(Enrollment)"은 "강좌(Course)"에 속하는 것처럼 보인다. 관계만 놓고 보면 Course Aggregate 안에 Enrollment를 넣으면 될 것 같다. 그러나 Enrollment에는 Course와 무관한 자기만의 규칙이 있다. 그렇다면 별도 Aggregate로 두는게 맞다. 

관계가 아닌 규칙이 판단 기준이다. 

 

 

하나의 트랜잭션 = 하나의 Aggregate

규칙 2~4를 한 문장으로 요약하면 이러하다.

하나의 트랜잭션에서는 하나의 Aggregate만 변경한다.

 

두 개의 Aggregate를 한 트랜잭션에서 동시에 바꿔야 한다면, 이는 경계를 잘못 그었다는 신호다. 서로 다른 Aggregate는 서로 다른 일관성 단위이므로, 한쪽이 바뀐 사실을 다른 한 쪽은 이벤트로 알게 된다 (이를 결과적 일관성 이라고 한다)

 

 

 


 

 

4. Core 도메인에 Aggregate 적용하기

이론을 직접 적용해보자. Core인 Assessment와 Learning을 예로 들어보겠다.

(이는 실제 테이블 명이 아닌 개념 모델이라는 점을 잊지 말자. )

 

 

Assessment 도메인 : 시험지 Aggregate

교사가 시험지를 만드는 과정을 살펴보자. 

시험지(Exam)는 여러 문항(Question)으로 구성되고, 배점이나 문항 수 같은 규칙들이 존재한다. 

여기서 불변식을 찾아보자.

시험지의 불변식 (항상 참이어야 하는 규칙)
   - 시험지의 총점 = 모든 문항 배점의 합
   - 문항이 0개인 시험지는 "출제 완료" 상태가 될 수 없다
   - 배정된 시험지의 문항은 변경 불가 

 

이 규칙들은 시험지 하나가 바뀔때마다 즉시 만족해야한다. 문항을 추가했는데 총점이 안 맞는 중간 상태가 잠깐이라도 허용되면 안된다. 그러니 이것들은 모두 하나의 일관성 경계 안에 있어야 한다. 

 

문항(Question)은 시험지(Exam)를 통해서만 추가/수정된다. exam.addQuestion(...) 과 같은 형태처럼 루트를 거친다. 직접 Question을 건드릴 수 없게 해서, 총점 규칙이 항상 지켜지도록 강제한다. 

그리고 출제하는 교사(Teacher)는 이 Aggreagate 안에 넣지 않는다.Teacher는 Academy 도메인의 Aggregate이다. 규칙 3에 따라 ID로만 참조한다.

 

 

 

Learning 도메인 : 학습 진행 Aggregate

학생이 문제를 푸는 쪽은 어떤지 살펴보자. 학습 진행에는 이런 규칙이 있다.

학습 진행의 불변식 (항상 참이어야 하는 규칙)
   - 진도율은 0~100% 범위를 벗어날 수 없다.
   - 채점 완료 전에는 점수가 확정되지 않는다.
   - 제출된 답안은 수정할 수 없다.

 

이 규칙들도 학습 진행 하나의 단위 안에서 지켜져야 한다. 

여기서도 학생(Student)과 시험지(Exam)는 다른 도메인의 Aggregate이다. ID로만 참조한다. 

 

 

 

왜 이렇게 나누는가

Assessment의 Exam과 Learning의 StudyProgress는 분명 연결되어 있다. 그러나 둘은 다른 일관성 경계다.

시험지를 완성한다                      → Exam Aggregate의 일관성
시험지를 풀고 진도가 쌓인다       → StudyProgress Aggregate의 일관성

 

시험지의 완성과 학습 진행은 같은 트랜잭션에서 처리할 일이 아니다. 교사가 시험지를 만드는 시점과 학생이 푸는 시점은 다르기 때문이다. 그래서 둘을 한 트랜잭션에 묶지 않고, 이벤트로 연결한다.

 

 

 


 

 

5. 도메인 간 통신: Cross DB 문제를 이벤트로 풀다

이제 1편에서 제기한 가장 큰 문제로 돌아온다. 레거시의 Cross DB Access — 서비스가 서로의 DB를 직접 참조하던 그 문제.

전술적 설계는 이것을 두 가지 규칙으로 풀어낸다.

 

 

Aggregate 간 — ID 참조 

같은 도메인 안에서도 Aggregate끼리는 객체로 직접 참조하지 않고 ID로만 참조한다(규칙 3)

이렇게 하면 Aggregate가 비대해지지 않고, 각자 독립적으로 로드 ·저장된다.

 

도메인 간 — Domain Event  

서로 다른 도메인끼리는 직접 호출하지 않는다. 대신 이벤트를 발행하고, 관심있는 도메인이 구독한다.

 

레거시의 "교사가 시험출제 → 학생에게 알림"을 예로 보자.

 

Assessment 도메인은 Notification의 존재를 몰라도 된다. 그냥 "시험이 배정됐다"는 사실만 외친다. 누가 그걸 듣고 무엇을 하든 Assessment의 관심사가 아니다. 결합이 끊어지는 것이다.

 

이것이 규칙 4(경계 밖은 결과적 일관성)의 실제 모습이다. 시험 배정과 알림 전송은 하나의 트랜잭션이 아니다. 시험이 배정되고, 그 후에 알림이 전달된다. 약간의 시차는 허용된다. 

 

주요 이벤트의 흐름을 정리하면 아래와 같다. 

exam.assigned             Assessment   → Notification (학생에게 출제 알림)
study.completed           Learning         → Notification (교사에게 완료 알림)
payment.completed     Billing              → Notification (결제 완료 알림)

 

 

 


 

 

6. 전체 논리 구조

 

핵심은 도메인이 서로의 DB를 직접 보지 않는다는 것이다. 같은 Aggregate 안은 강한 일관성, 도메인 사이는 이벤트를 통한 결과적 일관성. 이 두 층위가 Cross DB 문제를 구조적으로 해소한다. 

 

 

 

 


 

 

마치며

이번 글에서 다룬 것을 정리하면 이러하다. 

 

✓  도메인 후보를 Bounded Context로 확정 

✓ 전술적 Building Blocks ( Entity / VO / Domain Service / Event ) 

✓  Aggregate = 일관성 경계 라는 핵심 개념

✓ 불변식이 경계를 결정한다 (관계가 아니라)

✓ Vernon의 Aggregate 설계 4규칙

✓ Core 도메인(Assessment/Learning) 에 Aggregate 적용

✓ 도메인 간 통신 : ID 참조 + Domain Event

 

가장 중요한 것은

Aggregate의 경계는 일관성 경계이다. 관계가 아니라 불변식으로 긋는다. 
하나의 트랜잭션에서는 하나의 Aggregate만 바꾼다. 나머지는 이벤트로.

 

 

2편(전략적 설계)과 3편(전술적 설계)으로 DDD 설계의 큰 그림을 마쳤다.

어디에 경계를 긋고(2편),

그 안을 어떻게 채우는지(3편)

 

까지 살펴보았다.

 

이제 드디어 다음 글부터는 실제 작업에 들어간다. 설계한 것을 구현하기 위해 개발 환경 세팅부터 시작한다. 

 

 

** 참고 자료

 

 

Domain-Driven Design (Eric Evans, 2003) — 전술적 설계(Tactical Design)의 원전

 

Implementing Domain-Driven Design (Vaughn Vernon, 2013) — Aggregate·Domain Event 설계의 상세

 

Effective Aggregate Design (Vaughn Vernon, 2011) — Aggregate 설계 4규칙: https://www.dddcommunity.org/library/vernon_2011/

 

Effective Aggregate Design by Vaughn Vernon

Effective Aggregate Design by Vaughn Vernon Posted on: 10-1-2011 Aggregates are one of the more challenging aspects of tactical modeling. Developers often end up with large clusters of objects that do not give good performance and scalability. In this thre

www.dddcommunity.org

 

DDD Aggregate — Martin Fowler: https://martinfowler.com/bliki/DDD_Aggregate.html

 

bliki: D D D_ Aggregate

A pattern from Domain-Driven Design describing a cluster of domain objects that can be treated as a single unit for persistant storage and transactions.

martinfowler.com

 

Domain Event — Martin Fowler: https://martinfowler.com/eaaDev/DomainEvent.html

 

Domain Event

Captures the memory of something interesting which affects the domain

martinfowler.com

 

 

댓글