한 줄 정의

도메인 영역별로 기능성이 그룹화된 단일 배포 단위 의 아키텍처 스타일입니다.

쉽게 말하면

모놀리스의 단순함은 유지하되, 내부를 도메인별 모듈로 칸막이를 친 구조입니다. 하나의 JAR/WAR로 배포하지만 내부적으로는 주문, 결제, 배송 같은 비즈니스 영역이 명확히 분리되어 있습니다.

마이크로서비스처럼 네트워크를 넘나들 필요 없이, 모놀리스 안에서 도메인 간 경계를 확보하는 실용적인 선택지입니다.

왜 이렇게 설계했는가?

해결하는 문제

전통적인 계층형 아키텍처는 기술적 관심사(표현, 비즈니스, 영속성)로 코드를 나누기 때문에 하나의 도메인 변경이 여러 계층에 걸쳐 수정을 요구합니다. 또한 계층 간 경계가 느슨해서 시간이 지날수록 코드가 뒤엉키는 진흙잡탕(Big Ball of Mud) 으로 전락하기 쉽습니다.

이게 없다면

도메인 분할 없이 모놀리스를 운영하면, 시스템이 커질수록 모듈 간 상호 의존성이 폭발적으로 증가합니다. 결국 한 줄 수정에도 어디가 깨질지 예측할 수 없는 비정형 모놀리스(unstructured monolith) 가 됩니다. DDD의 인기와 함께 2020년 이후 이 스타일이 크게 주목받은 이유이기도 합니다.

비정형 모놀리스란?

비정형 모놀리스(unstructured monolith) 는 상호 의존성이 너무 높아서 분석하거나 분해할 수 없는 코드를 가진 모놀리스형 아키텍처를 말합니다.

토폴로지

모듈형 모놀리스의 동형 구조(isomorphic shape)는 도메인 영역별로 기능성이 그룹화된 단일 배포 단위 입니다. 기본적으로 모놀리스 아키텍처이므로 WAR, EAR, 단일 어셈블리 같은 단일 소프트웨어 단위로 배포됩니다.

graph TB
    subgraph "단일 배포 단위"
        subgraph "주문 접수"
            M1[컴포넌트]
            M2[컴포넌트]
        end
        subgraph "재고 관리"
            M3[컴포넌트]
            M4[컴포넌트]
        end
        subgraph "결제 처리"
            M5[컴포넌트]
            M6[컴포넌트]
        end
        subgraph "주문 이행"
            M7[컴포넌트]
            M8[컴포넌트]
        end
    end

컴포넌트의 이름공간은 기술적 관심사가 아니라 도메인 관심사 를 반영합니다. 예를 들어 고객 프로필 유지보수 컴포넌트는 com.app.customer.profile 이름공간으로 대표됩니다. 세 번째 노드가 기술적 관심사(표현 계층 등)가 아니라 도메인 관심사를 나타내는 것이 핵심입니다.

컴포넌트의 복잡성에 따라 기술적 관심사를 추가해서 이름공간을 더 분할할 수도 있습니다. 예를 들어 com.app.customer.profile.presentation이나 com.app.customer.profile.business는 기술적 관심사에 따른 분할입니다.

핵심 내용

구조적 옵션

모듈을 조직화하는 방법은 크게 두 가지입니다.

모놀리스 구조

모든 모듈과 컴포넌트를 단일한 코드 저장소 에 포함하는 가장 간단한 아키텍처입니다. 모듈과 컴포넌트는 이름공간이나 디렉터리 구조로 구분됩니다. 배포 시에도 모든 모듈이 소프트웨어의 단일 모놀리스 단위로 결합합니다.

com.orderentry.orderplacement    → 주문 접수, 재고 관리, 주문 이행
com.orderentry.inventorymanagement
com.orderentry.paymentprocessing → 결제 처리, 고객 알림
com.orderentry.notification
com.orderentry.fulfillment       → 배송
com.orderentry.shipping

모놀리스 구조는 코드가 한곳에 있어서 유지보수, 테스트, 배포가 쉽지만, 각 모듈의 경계를 유지하려면 엄격한 거버넌스가 필요합니다. 개발자들이 모듈 간에 너무 많은 코드를 재사용하거나 과도한 통신을 허용하면 진흙잡탕으로 전락할 수 있습니다.

모듈형 구조

각 모듈이 완결적이고 독립적인 요소(JAR 파일이나 DLL 파일 등)로 나타나며, 배포 시에는 이 요소들이 하나의 배포 단위로 통합됩니다.

order_placement.jar → [JAR][JAR][JAR][JAR][JAR]
payment.jar         → [JAR][JAR][JAR][JAR][JAR]
                                       shipping.jar

이 구조의 장점은 각 모듈이 완결적(self-contained) 이라서 팀마다 개별 모듈을 독립적으로 작업할 수 있다는 것입니다. 하지만 서로 의존하는 모듈들이 통신해야 하는 경우에는 모놀리스 구조 접근법이 더 효과적입니다.

모듈형 구조에서 과도하게 모듈 간 통신이 발생하면 DLL 지옥 안티패턴(자바 플랫폼에서는 JAR 지옥 안티패턴)이 발생합니다. 다른 모듈의 클래스를 사용하려면 그 클래스의 참조(reference)가 필요한데, 이것이 없으면 코드가 컴파일되지 않기 때문입니다. 이는 해당 모듈들 사이에 컴파일 시점 의존성(compile-time dependency) 을 만들어야 함을 의미합니다.

일반적인 해결책은 모듈들이 별도의 JAR 또는 DLL 파일에 있는 하나의 인터페이스 클래스를 공유하게 하는 것입니다. 그러면 각 모듈을 다른 모듈과는 독립적으로 컴파일할 수 있습니다.

모듈 간 통신

모듈형 모놀리스에서 모듈 간 통신은 전혀 바람직하지 않지만, 현실적으로 모듈 간 통신이 필요한 경우가 많습니다. 예를 들어 OrderPlacement 모듈이 주문된 품목의 재고를 조정하고 추가 처리를 수행하려면 InventoryManagement 모듈과 통신해야 하고, 주문에 대한 결제를 적용하려면 PaymentProcessing 모듈과도 통신해야 합니다.

동급 간 접근법

가장 간단한 해결책은 모듈들이 직접 통신하는 동급 간(peer-to-peer) 통신입니다. 한 모듈의 클래스 파일이 다른 모듈의 클래스를 인스턴스화하고, 그 클래스의 메서드들을 호출해서 작업을 수행합니다.

graph LR
    A[주문 접수] --> B[재고 관리]
    A --> C[결제 처리]

모놀리스 방식의 한 가지 문제점은 개발자가 다른 모듈에 있는 클래스를 인스턴스화하기가 너무 편하다 는 점입니다. 이 때문에 잘 구조화된 아키텍처가 진흙잡탕 안티패턴으로 변하기 쉽습니다.

중재자 접근법

중재자(mediator) 접근법은 중재자 컴포넌트를 추가해서 모듈 간 추상화 계층을 형성함으로써 모듈의 결합도를 낮춥니다. 중재자는 모듈들에서 요청을 받아서 적절한 모듈로 전달하는 오케스트레이터(조정자) 역할을 합니다.

graph TB
    Med[중재자] --> A[주문 접수]
    Med --> B[결제 처리]
    Med --> C[재고 관리]

중재자가 모듈들 사이의 결합을 제거하지만, 각 모듈이 사실상 중재자와 결합한다는 점을 알아야 합니다. 모든 결합과 의존성을 제거하지는 않지만, 아키텍처를 단순화하고 모듈을 서로 독립적으로 유지하는 데에는 도움이 됩니다.

한 가지 주목할 점은, 어떤 모듈의 기능성을 호출하기 위한 일종의 API나 인터페이스를 필요로 하는 것은 그 모듈에 의존하는 다른 모듈들이 아니라 중재자라는 것입니다.

데이터 토폴로지

모듈형 모놀리스 아키텍처는 일반적으로 단일 소프트웨어 단위로 배포되므로, 모놀리스 데이터베이스 토폴로지 에 의존하는 것이 보통입니다. 단일 데이터베이스를 이용해서 모듈들이 데이터를 공유하면 모듈 간 통신이 줄어듭니다.

그러나 모듈들이 서로 독립적이고 각자 특화된 기능을 수행한다면, 아키텍처 자체는 모놀리스라고 해도 모듈들이 자신만의 컨텍스트 데이터를 담은 개별 데이터베이스 를 둘 수도 있습니다.

graph TB
    subgraph "모놀리스 데이터 토폴로지"
        direction TB
        subgraph M1["모듈 그룹"]
            A1[모듈] 
            A2[모듈]
            A3[모듈]
        end
        DB1[(데이터베이스)]
        M1 --> DB1
    end

    subgraph "분리된 데이터 토폴로지"
        direction TB
        subgraph M2["모듈 A"]
            B1[모듈]
            B2[모듈]
        end
        subgraph M3["모듈 B"]
            B3[모듈]
            B4[모듈]
        end
        DB2[(데이터베이스 A)]
        DB3[(데이터베이스 B)]
        M2 --> DB2
        M3 --> DB3
    end

클라우드 고려 사항

모듈형 모놀리스 아키텍처를 클라우드 환경에 배포하는 것도 가능합니다(특히 소규모 시스템의 경우). 하지만 일반적으로 이 아키텍처 스타일은 클라우드 배포에 잘 맞지 않습니다. 모놀리스의 특성상 클라우드 환경이 제공하는 온디맨드 프로비저닝(on-demand provisioning) 의 이점을 활용하기 어렵기 때문입니다.

그렇긴 하지만, 이 아키텍처 스타일로 구현된 소규모 시스템이 파일 저장소, 데이터베이스, 메시징 같은 여러 클라우드 서비스를 활용할 수는 있습니다.

일반적인 위험

모듈형 모놀리스 아키텍처 스타일의 주된 위험은 시스템이 너무 커져서 제대로 유지보수, 테스트, 배포하기 어려워질 수 있다는 점입니다. “너무 크다”는 기준은 시스템마다 다르지만, 시스템이 너무 커졌을 때 나타나는 경고 신호를 알아둘 필요가 있습니다.

  • 변경 사항을 적용하는 데 시간이 너무 오래 걸립니다
  • 시스템의 한 부분을 변경하면 다른 부분이 예기치 않게 오작동합니다
  • 변경 사항을 적용할 때 팀들이 서로의 작업에 방해가 됩니다
  • 시스템을 시동하는 데 너무 오래 걸립니다

또 다른 위험은 코드를 과도하게 재사용하는 것입니다. 코드 재사용은 소프트웨어 개발의 필수적인 부분이지만, 이 아키텍처 스타일에서 코드를 지나치게 재사용하면 모듈 경계가 모호해지며, 아키텍처가 비정형 모놀리스(unstructured monolith) 라는 위험한 영역으로 들어갑니다.

과도한 모듈 간 통신도 이 아키텍처 스타일의 위험입니다. 이상적으로는 모듈이 독립적이고 완결적이어야 하지만, 일부 모듈이 다른 모듈과 통신하는 것은 일반적이며 필수적입니다. 그러나 모듈 간 상호 통신이 너무 많다면, 이는 애초에 도메인이 제대로 정의되지 않았을 수 있음을 나타내는 좋은 징후입니다.

거버넌스

모듈형 모놀리스의 주된 구성요소는 모듈 이며, 각각의 모듈은 특정한 도메인 또는 하위 도메인에 대응됩니다. 일반적으로 디렉터리 구조나 이름공간 같은 요소(자바 플랫폼에서는 패키지 구조)로 나타납니다. 따라서 거버넌스 자동화를 위해 아키텍트가 가장 먼저 시도할 만한 것 중 하나는 아키텍처에 쓰이는 모듈들이 준수해야 할 사항들을 정의하고 확인하는 것입니다.

모듈 유효성 검사

아키텍트가 자동화된 거버넌스 검사를 작성하기 위해 사용할 수 있는 도구로는 자바 플랫폼에는 ArchUnit, .NET 플랫폼에는 NetArchTest, 파이썬에는 PyTestArch, 타입스크립트와 자바스크립트에는 TSArch가 있습니다.

모듈 정의 검증

시스템에 정의된 각 모듈에 해당하는 이름공간 중 하나에 속하는지 확인하는 의사 코드입니다.

# 이 이름공간들은 시스템의 모듈들에 대응된다.
LIST module_list = {
    com.orderentry.orderplacement,
    com.orderentry.inventorymanagement,
    com.orderentry.paymentprocessing,
    com.orderentry.notification,
    com.orderentry.fulfillment,
    com.orderentry.shipping
}

# 시스템에 실제로 있는 이름공간 목록을 얻는다.
LIST namespace_list = get_all_namespaces(root_directory)

# 모든 이름공간이 정의된 모듈 이름공간 중 하나로 시작하는지 확인한다.
FOREACH namespace IN namespace_list {
    IF NOT namespace.starts_with(module_list) {
        send_alert(namespace)
    }
}

개발자가 미리 정의된 모듈과 해당 이름공간(또는 디렉터리) 바깥에 어떤 상위 이름공간이나 디렉터리를 생성했다면, 해당 소스 코드가 아키텍처를 준수하지 않았다는 경고를 받게 됩니다.

의존성 개수 제한

모듈 간 통신량을 제한하는 것도 중요합니다. 각 소스 파일의 참조(reference) 개수를 세서 전체 의존요소 개수가 최대치(예: 5개)를 넘는지 확인하는 의사 코드입니다.

LIST module_list = { ... }  # 모듈 목록

MAP module_source_file_map
FOREACH module IN module_list {
    LIST source_file_list = get_source_files(module)
    ADD source_file_list TO module_source_file_map
}

# 각 소스 파일의 참조 개수를 세서 전체 의존요소 개수가
# 5를 넘으면 경고를 보낸다.
FOREACH module, source_file_list IN module_source_file_map {
    FOREACH source_file IN source_file_list {
        incoming_count = used_by_other_module(source_file, module_source_file_map)
        outgoing_count = uses_other_module(source_file)
        total_count = incoming_count + outgoing_count
    }
    IF total_count > 5 {
        send_alert(module, total_count)
    }
}

특정 모듈 간 의존성 제한

자동화된 거버넌스의 마지막 형태는 모듈들의 독립성을 유지하기 위해 특정 모듈들 사이의 통신을 제한 하는 것입니다. 예를 들어 OrderPlacement 모듈은 Shipping 모듈과 통신하면 안 됩니다. ArchUnit 자바 코드 예시입니다.

public void order_placement_cannot_access_shipping() {
    noClasses().that()
        .resideInAPackage("..com.orderentry.orderplacement..")
        .should().accessClassesThat()
        .resideInAPackage("..com.orderentry.shipping..")
        .check(myClasses);
}

팀 토폴로지 고려 사항

모듈형 모놀리스는 도메인 분할 아키텍처로 간주되므로, 팀 또한 도메인 영역별로(예를 들어 전문성을 갖춘 CFT들로) 정렬될 때 가장 효과적입니다.

팀 유형적합도이유
스트림 정렬 팀 (기능 전담 팀)높음시스템의 처음부터 끝까지 흐름을 소유하며, 모듈형 모놀리스의 자기완결적 형태와 잘 맞습니다
활성화 팀 (역량 코칭 팀)높음모듈성이 높고 관심사들이 잘 분리되어 새로운 모듈을 도입하기 쉽습니다
난해한 하위시스템 팀 (전문 도메인 팀)적합각 모듈은 해당 도메인에 기반한 특정 역할을 수행하므로, 난해한 하위시스템 팀 토폴로지와 잘 맞습니다
플랫폼 팀 (공통 기반 팀)적합높은 수준의 모듈성 덕분에 공통 도구, 서비스, API, 작업을 유용하게 사용할 수 있습니다

반대로 기술적 범주별로 조직된 팀(UI 팀, 백엔드 팀, 데이터베이스 팀 등)은 이 아키텍처 스타일과 잘 맞지 않습니다(도메인 분할 방식 때문에). 도메인 기반 요구사항이 발생하면 도메인 중심의 CFT(교차 기능 팀)가 표현 로직부터 데이터베이스에 이르기까지 해당 기능 전체를 함께 작업할 수 있습니다.

장단점

아키텍처 특성평가
전반적인 비용매우 낮음
분할 방식도메인
퀀텀 개수1
단순성★★★★★
모듈성★★☆☆☆
유지보수성★★☆☆☆
테스트성★★☆☆☆
배포성★★☆☆☆
진화성★★☆☆☆
반응성★★★☆☆
확장성★☆☆☆☆
탄력성★☆☆☆☆
내결함성★☆☆☆☆

모듈형 모놀리스는 도메인 분할 아키텍처에 속합니다. 일반적으로 모놀리스 형태로 구현되고 배포되므로, 아키텍처 퀀텀 수는 일반적으로 1 입니다.

단순성 ★★★★★

분산 아키텍처 스타일과 관련된 복잡성이 전혀 없습니다. 네트워크 통신, 분산 트랜잭션, 서비스 디스커버리 같은 고민거리가 없어 이해하고 구축하기 쉽습니다.

모듈성 ★★☆☆☆

도메인과 서브도메인 단위로 모듈이 나뉘어 관심사 분리가 이루어집니다. 다만 물리적으로는 하나의 배포 단위이므로 모듈 경계가 약해질 위험이 상존합니다.

배포성·테스트성 ★★☆☆☆

모놀리스이므로 작은 변경에도 전체를 다시 빌드·배포·테스트해야 하는 의례(ceremony) 비용이 큽니다. 배포 빈도가 낮고 배포 위험도 높습니다. 그래도 모듈 경계가 있어 계층형 아키텍처보다는 영향 범위 파악이 수월합니다.

확장성·탄력성 ★☆☆☆☆

애플리케이션 전체를 하나의 단위로만 확장할 수 있습니다. 특정 모듈만 독립적으로 스케일 아웃하려면 다중 스케일링·내부 메시징 같은 기법을 억지로 동원해야 하는데, 이 아키텍처의 성격과 맞지 않습니다.

내결함성 ★☆☆☆☆

한 모듈에서 발생한 메모리 부족 오류가 애플리케이션 전체를 죽입니다. 장애 격리가 불가능하고, 평균 복구 시간(MTTR) 이 길며 시동 시간도 분 단위라 전반적인 가용성이 떨어집니다.

적합한 상황

사용하면 좋은 경우

  • 비용이 낮고 단순하고 시간이 빠듯할 때: 분산 아키텍처에 비해 구축 비용과 복잡성이 현저히 낮습니다
  • 새로운 시스템을 시작할 때: 아키텍처 방향이 아직 불분명하다면, 분산 아키텍처로 바로 뛰어드는 것보다는 일단 모듈형 모놀리스로 시작하고 나중에 서비스 기반이나 마이크로서비스 같은 분산 아키텍처 스타일로 전환하는 것이 효과적입니다
  • 도메인 중심 CFT 같은 팀에게도 좋은 선택: 모듈성과 관심사 분리 덕분에 각 팀은 다른 도메인 팀과의 조율을 최소화하면서 아키텍처의 특정 모듈을 처음부터 끝까지 집중적으로 작업할 수 있습니다
  • 도메인 기반 변경이 대부분인 경우: 고객의 점 목록 항목에 만료 날짜를 추가하는 등의 변경
  • DDD를 사용하는 팀: 모듈형 모놀리스는 도메인 분할 아키텍처이므로, DDD와 잘 맞습니다

사용하지 말아야 할 경우

  • 확장성, 탄력성, 가용성, 내결함성, 반응성, 성능 같은 운영 특성의 수준이 높아야 할 때: 대부분의 모놀리스 아키텍처가 그렇듯이 이런 요구사항을 충족하기에 역부족입니다
  • 변경 사항의 대부분이 기술 지향적인 경우: 예를 들어 사용자 인터페이스나 데이터베이스 기술을 지속적으로 교체해야 하는 상황이 이에 해당합니다. 도메인 분할 방식이므로, 그런 변경은 모든 모듈에 영향을 미칩니다
  • 도메인 팀들 사이에서 소통과 조율이 상당히 많이 요구되는 경우: 변경시 도메인 팀들 간의 소통과 조율이 상당히 많이 요구되는 경우 계층형 아키텍처가 훨씬 더 나은 선택입니다

예시: EasyMeals

이지밀즈(EasyMeals) 는 배달 기반의 소규모 레스토랑의 주문 및 관리 시스템입니다. 소규모 지역 음식점이므로 확장성이나 반응성이 높을 필요 없고, 예산이 제한적이며, 정교한 소프트웨어 시스템을 만드는 데 큰 비용을 들일 수도 없습니다. 이런 비즈니스 문제의 성격상 모듈형 모놀리스가 좋은 선택입니다.

graph TB
    Client1[HTTP 클라이언트<br/>고객] --> OrderPlace[주문 접수]
    Client1 --> OrderView[주문 조회]
    Client2[HTTP 클라이언트<br/>식당 직원] --> Recipe[레시피]
    
    OrderPlace --> Payment[결제 처리]
    OrderPlace --> Delivery[배송]
    OrderPlace --> Inventory[식재료 재고]
    
    Payment --> DB[(데이터베이스)]
    Delivery --> DB
    Inventory --> DB
    OrderPlace --> DB
    OrderView --> DB
    Recipe --> DB

이 시스템의 각 모듈을 나타내는 이름공간들입니다.

모듈이름공간
PlaceOrdercom.easymeals.placeorder
PaymentProcessingcom.easymeals.payment
PrepareOrdercom.easymeals.prepareorder
Deliverycom.easymeals.delivery
Recipescom.easymeals.recipes
IngredientsInventorycom.easymeals.inventory

각 모듈은 하나 이상의 컴포넌트 로 이루어집니다(모듈과 컴포넌트가 1대다 관계임을 보여줍니다).

PlaceOrder 모듈

고객은 메뉴를 확인하고, 원하는 항목을 선택하고, 고객 이름/주소/결제 정보를 입력해서 주문을 제출합니다.

com.easymeals.placeorder.menu
com.easymeals.placeorder.shoppingcart
com.easymeals.placeorder.customerdata
com.easymeals.placeorder.paymentdata
com.easymeals.placeorder.checkout
PaymentProcessing 모듈

결제 처리를 담당합니다. 신용카드, 직불카드, 페이팔을 지원합니다. 아키텍처의 높은 모듈성 덕분에 멤버십 포인트 같은 추가 결제 유형을 추가하기도 쉽습니다.

com.easymeals.payment.creditcard
com.easymeals.payment.debitcard
com.easymeals.payment.paypal
PrepareOrder 모듈

결제 처리가 완료되면 PlaceOrder 모듈은 PrepareOrder 모듈과 통신합니다. 전체 주문 내용을 주방 직원에게 표시하고, 조리가 끝나면 주방 직원은 주문을 배달 준비 완료 상태로 설정합니다.

com.easymeals.prepareorder.displayorder
com.easymeals.prepareorder.ready
Delivery 모듈

주문에 배달 담당자를 배정하고 배달 주소를 알려줍니다. 배달을 마친 배달 담당자는 이 모듈을 통해 주문을 배달 완료 상태로 설정합니다. 이에 의해 해당 주문의 수명 주기(lifecycle)가 끝납니다.

com.easymeals.delivery.assign
com.easymeals.delivery.issues
com.easymeals.delivery.complete
Recipes 모듈

요리사와 관리자가 메뉴 항목을 추가하거나 관리하는 데 필요한 기능을 제공합니다. 이 모듈을 통해서 각 메뉴 항목에 대한 재료 목록과 계량 정보를 관리하는 것도 가능합니다.

com.easymeals.recipes.view
com.easymeals.recipes.maintenance
IngredientsInventory 모듈

메뉴에 있는 레시피에 필요한 재료가 충분히 확보되어 있는지 확인합니다. 이 모듈은 다른 모듈보다 다소 복잡한데, 주간 재료 조달 과정을 자동화하고 판매량을 예측하는 정교한 AI 컴포넌트가 포함되어 있기 때문입니다.

com.easymeals.inventory.maintenance
com.easymeals.inventory.forecasting
com.easymeals.inventory.ordering
com.easymeals.inventory.suppliers
com.easymeals.inventory.invoices

이 예시는 모듈형 모놀리스의 단순성과 높은 모듈성 덕분에, 버그를 수정하거나 새로운 기능을 추가하기 위해 해당 코드를 찾아서 고치거나 기능을 추가하기가 비교적 쉽다는 점을 잘 보여줍니다.

내 생각

  • 마이크로서비스가 유행이라고 해서 처음부터 분산 아키텍처로 시작하면, 네트워크 복잡성 · 분산 트랜잭션 · 운영 오버헤드라는 대가를 초기부터 치르게 됩니다. “확실하지 않으면 모듈형 모놀리스로 시작하라” 는 조언은 실무에서 매우 유효합니다. 도메인 경계가 충분히 검증된 후에 필요한 모듈만 서비스로 분리하는 것이 훨씬 안전합니다.
  • 거버넌스 자동화(ArchUnit 등)가 없으면 “도메인별로 나눴다”는 것은 그저 디렉터리 구조에 불과합니다. 시간이 지나면 개발자들이 편의상 다른 모듈의 내부 클래스를 직접 참조하기 시작하고, 결국 비정형 모놀리스로 회귀합니다. 빌드 파이프라인에 아키텍처 테스트를 포함시키는 것 이 이 스타일의 생존 조건이라고 봅니다.
  • 단일 배포 단위라는 점에서 오는 제약(확장성, 내결함성)은 분명하지만, 대부분의 초기 스타트업이나 사내 도구 수준의 시스템에서는 이 제약이 실제 병목이 되는 경우가 드뭅니다. 오히려 불필요한 분산이 만드는 운영 비용이 더 큰 문제인 경우가 많습니다.

더 알아볼 것

  • 모듈형 모놀리스에서 마이크로서비스로 점진적 전환 사례 (Strangler Fig 패턴)
  • Spring Modulith — 스프링에서 모듈형 모놀리스를 공식 지원하는 프로젝트

관련 개념

출처

  • Fundamentals of Software Architecture 2nd Edition, Ch.11 — Mark Richards, Neal Ford