Featured image of post [Clean Architecture] 31. 소리치는 아키텍처

[Clean Architecture] 31. 소리치는 아키텍처

소리치는 아키텍처(Screaming Architecture)의 개념을 다룹니다. 아키텍처가 시스템의 유스케이스와 의도를 어떻게 드러내야 하는지, 프레임워크가 아닌 도메인 중심 설계를 설명합니다.

아키텍처는 시스템의 의도를 소리쳐야 한다. 최상위 디렉토리 구조를 보면 이것이 건강 관리 시스템인지, 회계 시스템인지 알 수 있어야 한다.

건축과의 비유

건축물의 청사진을 보면 그것이 무엇인지 바로 알 수 있다.

flowchart LR
    subgraph Buildings [건축물의 청사진]
        HOUSE[집
거실, 침실, 주방] LIB[도서관
열람실, 서고, 대출대] HOSP[병원
진료실, 수술실, 병동] end
건축물청사진에서 보이는 것
거실, 침실, 주방, 욕실
도서관열람실, 서고, 대출대
병원진료실, 수술실, 병동

“건축물의 청사진을 보면 그것이 집인지, 도서관인지, 병원인지 알 수 있다. 소프트웨어 아키텍처도 마찬가지여야 한다.

소프트웨어 아키텍처는?

소프트웨어의 최상위 구조를 보면 무엇을 하는 시스템인지 알 수 있어야 한다.

프레임워크가 소리치면 안 된다

flowchart TB
    subgraph Bad [프레임워크가 소리치는 구조]
        CTRL[controllers/]
        MODEL[models/]
        VIEW[views/]
        SVC[services/]
        REPO[repositories/]
        UTIL[utils/]
    end

나쁜 구조 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
src/
├── controllers/
│   ├── OrderController.java
│   ├── PaymentController.java
│   └── ShippingController.java
├── models/
│   ├── Order.java
│   ├── Payment.java
│   └── Shipment.java
├── views/
│   ├── order.html
│   └── payment.html
├── services/
├── repositories/
└── utils/

이 구조를 보고 무엇을 알 수 있는가?

  • “Spring 앱이구나”
  • “MVC 패턴이구나”
  • “이 시스템이 뭘 하는지는 모르겠다”

도메인이 소리쳐야 한다

flowchart TB
    subgraph Good [도메인이 소리치는 구조]
        ORDER[orders/]
        PAYMENT[payments/]
        SHIP[shipping/]
        INV[inventory/]
    end

좋은 구조 예시:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
src/
├── orders/
│   ├── PlaceOrderUseCase.java
│   ├── CancelOrderUseCase.java
│   ├── Order.java
│   └── OrderRepository.java
├── payments/
│   ├── ProcessPaymentUseCase.java
│   ├── RefundPaymentUseCase.java
│   ├── Payment.java
│   └── PaymentGateway.java
├── shipping/
│   ├── ScheduleDeliveryUseCase.java
│   ├── TrackShipmentUseCase.java
│   └── Shipment.java
└── inventory/
    ├── CheckStockUseCase.java
    └── InventoryItem.java

이 구조를 보면 바로 알 수 있다:

  • “이건 전자상거래 시스템이다”
  • “주문, 결제, 배송, 재고 관리를 한다”

비교: 프레임워크 중심 vs 도메인 중심

관점프레임워크 중심도메인 중심
최상위 폴더controllers, models, viewsorders, payments, shipping
알 수 있는 것사용 기술비즈니스 도메인
소리치는 것“Spring 앱!”“전자상거래 시스템!”
테스트 용이성프레임워크 의존독립적 테스트 가능

Ivar Jacobson의 교훈

객체지향 소프트웨어 공학의 선구자 Ivar Jacobson의 가르침:

“시스템 아키텍처는 유스케이스를 지원해야 한다. 아키텍처의 최상위 요소는 프레임워크가 아니라 유스케이스여야 한다.”

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// 유스케이스가 명확히 드러나는 구조
package com.example.orders;

public class PlaceOrderUseCase {
    private final OrderRepository orderRepository;
    private final PaymentGateway paymentGateway;
    private final InventoryService inventoryService;
    
    public OrderResult execute(PlaceOrderRequest request) {
        // 유스케이스 로직
        Order order = Order.create(request);
        inventoryService.reserve(order.getItems());
        paymentGateway.charge(order.getPayment());
        orderRepository.save(order);
        return OrderResult.success(order);
    }
}

테스트 용이성

소리치는 아키텍처는 테스트하기 쉽다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
// 웹 서버 없이 테스트
@Test
void shouldPlaceOrderSuccessfully() {
    // Given
    OrderRepository repo = new InMemoryOrderRepository();
    PaymentGateway gateway = new MockPaymentGateway();
    InventoryService inventory = new MockInventoryService();
    
    PlaceOrderUseCase useCase = new PlaceOrderUseCase(
        repo, gateway, inventory
    );
    
    // When
    OrderResult result = useCase.execute(new PlaceOrderRequest(...));
    
    // Then
    assertThat(result.isSuccess()).isTrue();
}
flowchart TB
    subgraph Testing [테스트 독립성]
        UC[유스케이스 테스트]
        NO_WEB[웹 서버 불필요]
        NO_DB[DB 불필요]
        FAST[빠른 실행]
    end
    
    UC --> NO_WEB --> NO_DB --> FAST
소리치는 아키텍처의 테스트프레임워크 종속 테스트
웹 서버 없이 테스트웹 서버 필요
DB 없이 테스트실제 DB 필요
밀리초 단위 실행초 단위 실행
유스케이스 단위 테스트통합 테스트만 가능

프레임워크는 도구

flowchart TB
    subgraph Correct [올바른 관점]
        DOMAIN[도메인/비즈니스
핵심] FW[프레임워크
도구] FW --> DOMAIN end

“프레임워크는 사용할 뿐, 시스템을 지배하게 하지 마라.” — Robert C. Martin

프레임워크 중심 vs 도메인 중심

프레임워크 중심 사고도메인 중심 사고
“Rails 앱을 만들자”“전자상거래 앱을 만들자 (Rails 사용)”
“Spring 앱이다”“은행 시스템이다 (Spring 사용)”
“Django 프로젝트”“블로그 플랫폼 (Django 사용)”
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 프레임워크 중심 - 나쁜 예
@RestController
@RequestMapping("/orders")
public class OrderController {
    @PostMapping
    public ResponseEntity<?> createOrder(@RequestBody OrderDTO dto) {
        // 컨트롤러에 비즈니스 로직이 있음
        Order order = new Order(dto.getItems());
        order.calculateTotal();
        orderRepo.save(order);
        return ResponseEntity.ok(order);
    }
}

// 도메인 중심 - 좋은 예
@RestController  // 프레임워크는 외곽에만
public class OrderController {
    private final PlaceOrderUseCase placeOrder;
    
    @PostMapping("/orders")
    public ResponseEntity<?> createOrder(@RequestBody OrderDTO dto) {
        // 컨트롤러는 얇게, 유스케이스에 위임
        OrderResult result = placeOrder.execute(dto.toRequest());
        return ResponseEntity.ok(result.toDTO());
    }
}

아키텍처가 소리쳐야 하는 것

flowchart LR
    ARCH[아키텍처를 보면]
    
    DOMAIN[도메인
무엇을 하는가] UC[유스케이스
어떤 기능이 있는가] ARCH --> DOMAIN ARCH --> UC NOT[프레임워크 X
DB X
웹 X] ARCH -.-> NOT

핵심 요약

원칙설명
소리치는 것도메인과 유스케이스
숨기는 것프레임워크, DB, 웹
최상위 구조비즈니스 기능별
테스트프레임워크 없이 가능

“아키텍처를 보고 이것이 헬스케어 시스템인지, Rails 앱인지 알 수 있어야 한다. 헬스케어 시스템이어야 한다!” — Robert C. Martin

1
2
3
4
5
6
7
8
9
// 좋은 아키텍처의 최상위 구조
healthcare-system/
├── patients/           # 환자 관리
├── appointments/       # 예약 관리
├── prescriptions/      # 처방 관리
├── billing/           # 청구 관리
└── medical-records/    # 의료 기록

// 아키텍처가 소리친다: "이것은 헬스케어 시스템이다!"