Bounded Context가 틀렸다는 주장, 솔직히 절반만 맞음
DDD 15년 한 시니어가 bounded context와 aggregate를 다 버리고 Request Processing Unit과 feature slice로 가자는 글을 한국 백엔드 개발자 입장에서 비판적으로 읽어봤습니다. anemic application 진단은 날카로운데, RPU가 정말 새로운 단위인지, event store를 "그냥 인프라"로 단정해도 되는지에 대한 솔직한 회의.
DDD 15년 한 사람이 결국 다 버렸다는 글이 영어권 백엔드 진영에 슬슬 도는 분위기예요. Bounded context도, aggregate도, CQRS도 전부 잘못된 단위라는 거. 대신 Request Processing Unit(RPU)이라는 새 개념과 feature slice로 가야 한다고요. 글 자체는 깔끔하고 논리도 잘 짜였는데, 다 읽고 나서 좀 갸웃해졌습니다. 진짜 그런가 싶어서 한 번 따져볼게요.
글의 요지부터
저자 주장은 대충 이런 흐름임.
DDD가 본래 의도와 달리 도그마틱해졌다. Ubiquitous language, 도메인 이해 같은 좋은 부분은 살아있지만 bounded context · context mapping · aggregate · entity · value object · repository · domain service ··· 전술적 개념이 끝없이 늘어나면서 결국 트레이닝 산업의 좋은 먹잇감이 됐다는 진단. 강의 한 주가 그냥 채워지는 컨셉들이라고요.
ORM 중심 개발이 도메인 모델을 데이터 모델로 슬쩍 바꿔놓았다. EF Core, JPA/Hibernate를 쓰다 보면 entity 클래스 = ORM 매핑 = DB 스키마가 사실상 한 덩어리가 됩니다. 그래서 "우리는 DDD를 합니다" 하면서도 런타임 동작은 결국 CRUD라는 거. 저자는 이걸 anemic model이 아니라 anemic application이라고 다시 정의해요. 모델이 빈약한 게 아니라 application 자체가 도메인 처리를 안 가지고 있는 상태.
CQRS는 절반만 갔다. 읽기/쓰기 분리는 좋았는데 write 쪽은 여전히 aggregate 중심이라 큰 단위 문제는 안 풀렸다는 진단. (Microsoft 공식 CQRS 가이드도 비슷한 구조로 그려져 있긴 하죠.)
대안은 RPU + feature slice. RPU 하나가 request 하나를 처리—validate, context 빌드, 결과 생성. Command RPU는 상태에 영향을 주고, Query RPU는 상태를 읽고, Action은 상태 없이 동작. Feature slice는 RPU들을 묶어서 외부 요청 하나를 끝까지 처리하는 단위.
Event store는 그냥 인프라. 도메인 추상화가 아니라 그냥 application state 저장소. append + query 두 함수만 있으면 충분하고 테이블 하나로 시작해도 된다는 입장.
요지 끝.
공감 가는 부분—anemic application 진단
여기까진 솔직히 동의 갑니다. 한국 백엔드 씬도 Spring이든 FastAPI든 Express든, 실제 코드 열어보면 router → service → ORM → response 흐름이 압도적이거든요. 그 안에 "도메인 처리"라고 부를 만한 건 거의 없어요. 데이터 검증 정도. 나머진 그냥 SELECT-INSERT-UPDATE 래퍼.
거기에 DDD 어휘를 입혀도 본질이 안 바뀌는 게 진짜 함정입니다. UserService, OrderRepository, ProductEntity—이름이 그럴듯해도 런타임 동작은 그대로 CRUD죠. 저자가 말하는 anemic application이 정확히 이 상태고요. 이 진단은 날카롭다고 봐요.
ORM 드리프트도 백번 동의함. SQLAlchemy 모델을 도메인 모델로 같이 쓰면 Base를 상속해야 하니까 결국 DB 컬럼 = 도메인 속성이 됩니다. 분리하려면 mapper 따로 두고 dataclass 하나 더 만들고... 보일러플레이트가 빠르게 늘어나는데, 그래서 대부분 그냥 ORM 모델을 도메인 객체처럼 써버리잖아요. 그 순간 도메인 처리는 application 밖으로 새어나가요.
갸웃한 지점—RPU가 진짜 새로운 거냐
근데 RPU 설명 읽다 보면 자꾸 기시감이 듭니다. command handler / query handler. Vertical Slice Architecture. Jimmy Bogard가 MediatR로 풀었던 그 패턴이랑 거의 같은 모양이거든요. "한 request를 끝까지 처리하는 작은 유닛"이라는 정의 자체가 새로운 게 아니에요.
저자도 글 안에서 CQRS는 인정해요. 다만 "CQRS는 aggregate를 못 버렸다"가 차별점이라고 합니다. 근데 vertical slice 진영에서 aggregate 안 쓰는 사람들은 이미 많거든요. 그러면 이 글이 보여주는 건 결국 "기존 vertical slice를 RPU + feature slice라는 새 이름으로 다시 부르는 거" 아닌가, 라는 의문이 안 풀리는 거죠.
새 용어가 항상 나쁜 건 아닌데, 새 이름이 받는 무게에 비해 새로움 자체는 좀 가벼워요. 이건 그냥 마케팅임.
Event store가 그렇게 단순한가
"append + query 두 함수면 끝, 테이블 하나로 시작해도 된다"는 부분에서 더 갸웃했습니다.
작은 시스템이면 맞는 말일 수 있는데, event sourcing의 진짜 어려움은 append/query가 아니거든요. 운영 들어가면 마주치는 게 따로 있어요.
- 스키마 진화. 1년 전 이벤트 형식이 지금이랑 다를 때 replay를 어떻게 할 건가
- projection 관리. read model을 어떻게 만들고, 깨졌을 때 어떻게 다시 빌드할 건가
- idempotency와 순서 보장. 분산 환경에서 이벤트 순서 꼬이면 상태가 깨지는데 그 처리는
- snapshot. aggregate 안 쓴다 해도 수년치 이벤트 다 replay해서 현재 상태 만드는 건 결국 비현실적
이 문제들이 안 보이는 글에서 "그냥 인프라"라고 단정하는 건 좀 단단해 보여요. 직접 운영 안 해본 사람한텐 매력적으로 들리는데, 막상 들어가면 다 따로 싸워야 하는 항목들입니다.
저자가 이걸 모르고 쓴 건 아닐 거예요. 글 톤이 "선언문"에 가까워서 디테일을 의도적으로 빼고 큰 그림에 집중한 듯한데, 그 결과로 처방전의 무게감이 가벼워졌다는 게 솔직한 인상입니다.
한국 백엔드 맥락에서
한 가지 더 짚자면, 이 글이 가정하는 청중은 사실 "DDD를 너무 깊이 한 사람"이에요. 영문 엔터프라이즈 진영, 특히 .NET이나 Java 쪽에서는 DDD가 한 시기 거의 디폴트였으니까. 거기서 빠져나오는 서사라면 의미가 큽니다.
근데 한국 백엔드 씬, 특히 스타트업 + Python/Node 쪽에서는 애초에 DDD가 디폴트였던 적이 별로 없어요. FastAPI 프로젝트 열어보면 대부분 처음부터 "router-service-repository" 정도의 얇은 구조잖아요. bounded context까지 가는 코드베이스는 꽤 큰 자바 모놀리스 정도. 그래서 이 글의 처방전이 한국 개발자에게 주는 임팩트는 영어권보다 작을 수 있어요. "버려야 할 게" 애초에 별로 없거든요.
대신 anemic application 진단은 그대로 적용됩니다. DDD를 안 했어도 우리 application이 그냥 CRUD인 건 똑같아요.
잡담—15년 했다는 사람들
이런 글 읽을 때마다 드는 생각인데, "X를 15년 한 사람이 X를 버렸다" 류의 글이 영어권에서 꽤 자주 나옵니다. DDD 버렸다, 마이크로서비스 버렸다, TDD 버렸다, 클린 아키텍처 버렸다, 함수형 버렸다... 거의 장르예요.
물론 진짜 깊이 들어간 사람의 회고는 가치 있어요. 근데 양산되다 보면 "버렸다 서사" 자체가 클릭을 부르는 포맷이 됩니다. 글 읽는 입장에서는 매번 "이번엔 진짜 통찰인지 그냥 새 책 영업 빌드업인지" 한 번 거르고 봐야 한다는 결론에 점점 익숙해지는 중.
단점·한계
- RPU/feature slice 패턴이 진짜 효과를 보려면 팀 규모와 도메인 복잡도가 어느 선 이상이어야 함. 4-5인 스타트업에서 도입한다고 코드가 깔끔해지진 않아요.
- Event store 단순론은 운영 디테일이 빠져 있어서 그대로 따라가면 1-2년 뒤에 부메랑으로 돌아올 가능성이 큼.
- 글이 선언문 톤이라 "어떤 도메인에선 이게 적합하지 않은가"에 대한 솔직한 논의가 빠져 있음. 모든 처방엔 trade-off가 있는데 그게 안 다뤄지면 신뢰가 좀 떨어지죠.
정리하자면
저자 진단—anemic application, ORM 드리프트, DDD가 트레이닝 산업 먹잇감 됐다는 부분—은 동의 갑니다. 처방—RPU + feature slice + 단순한 event store—은 절반만 동의해요. 새 이름값에 비해 새로움이 크지 않고, event store 부분은 운영 디테일을 너무 가볍게 봤다고 봅니다.
근데 이 글에서 가져갈 가치는 분명해요. "내 application은 도메인 처리를 가지고 있는가, 아니면 그냥 DB 앞에서 CRUD 중인가." 이 질문은 DDD를 하든 안 하든 정직하게 던져볼 만합니다. 우리 코드 대부분은 그냥 CRUD고, 그건 사실이에요.
다음에는 FastAPI에서 vertical slice 비슷한 구조 짜본 경험을 한 번 정리해볼까 함. 그건 직접 해본 거라 더 솔직하게 쓸 수 있을 듯요.
댓글
NEXT_PUBLIC_GISCUS_*환경변수 구성 필요)