
발제문에 이렇게 적혀있어서 주문이 당연히 배송ID를 가져야한다고 생각했다.
주문이 생성될 땐 배송 Id를 우선 비워두고, 배송 서비스에서 배송 정보가 생성되면 이벤트 처리를 통해 주문의 배송 Id를 채워줄 수 있다.
주문이 생성될 때 배송 Id를 주문에서 생성해 미리 채워두고 배송에게 넘겨주는 방식을 사용할 수도 있다. 이 때는 정합성을 위해 배송 생성이 실패하면 재시도를 하는 처리가 추가로 필요해진다.

3. 주문 서비스는 배송 정보를 참조할 일이 없다. 그래서 주문에는 배송 Id를 두지 않는다.
주문에 대한 배송 정보를 조회하기 위해서는
배송이 주문Id를 가지고 있고, 배송 서비스에 요청하면 된다. (단일 책임 원리)

수정할 때 이미 완료된 주문이면 안된다.
Order (Aggregate Root)
├── id (UUID)
├── supplierId ← Company Context 참조 (ID만 보관)
├── receiverId ← Company Context 참조 (ID만 보관)
├── productId ← Product Context 참조 (ID만 보관) // 주문 하나당 상품 한 종류만
├── quantity // 상품의 수량
├── deliveryId ← Delivery Context 참조 (ID만 보관)
├── OrderStatus (Value Object)
│ └── | PAID | ORDER_CONFIRMED | CANCELLED
└── requestDetails
슬랙 알림 서비스의 책임을 어디까지 둘 지 결정할 때 다음 두 가지를 생각할 수 있다.

1. 다른 서비스가 메시지 정보를 만들고 알림 서비스에 요청을 한다.
이 경우 알림서비스의 역할은 메시지 전송밖에 안해서 알림서비스의 책임이 너무 적어질 수 있다.

2. 메시지 서비스의 슬랙 API가 다른 플랫폼 API로 확장될 때를 생각해보자.
다양한 메시지 API 중에서는 메시지 템플릿 기능을 같이 가지고있는 경우가 있다.
메시지 조합을 메시지 서비스가 할 땐 알림 서비스의 부담이 커질 수 있다.
결국 책임을 어느 쪽으로 몰아줄지의 문제이고, 두 방법중 확실히 옳거나 더 효율적인 방법이 있는 문제는 아니었다.
그래서 알림 서비스를 맡게 되는 사람은 다른 서비스도 같이 맡게 될것 같아 알림 서비스를 맡은 사람이 팀에서 너무 적게 역할을 맡게 되는 것이 아니고,
알림 서비스가 다른 플랫폼으로도 확장가능할 것을 고려해 알림 서비스는 메시지에 대한 정보를 받아서 전송만 하는 것으로 결정했다.
우리는 앞서서 주문이 배송을 바라보지 않게 했다.
배송이 주문 정보를 가져와서 메시지를 만들어 알림 서비스에 전송을 요청한다

도메인의 책임 명확하고
흐름 자연스럽고
유지보수가 쉽다.
주문 서비스에서 배송정보를 만들게 될 때,
수시로 데이터가 바뀌는 실시간 주문 정보와
배송이 시작될 수 있는 확정된 주문 정보를 구분해야한다.
왜냐하면 주문이 취소되면 배송도 시작되지 않아야 하기 때문이다.
우리는 앞서 00시를 기준으로 최종적으로 주문을 마감을 해 배송을 하기로 정했다.
00시에 상태값을 보고 확정된 주문에 대한 배송을 만들어준다면, 사용자 관점에서 배송상태가 바로 적용되지 않는 스케줄링 소요시간 문제가 생긴다.
00시 주문 확정 이전에
오늘 간 주문 확정된 테이블을 따로 만들어서 배송과 메시지 관리를 해나가는 게 좋을것이라 생각되었다.
Order (Aggregate Root)
├── id (UUID)
├── supplierId ← Company Context 참조 (ID만 보관)
├── receiverId ← Company Context 참조 (ID만 보관)
├── productId ← Product Context 참조 (ID만 보관) // 주문 하나당 상품 한 종류만
├── quantity || // 상품의 수량
├── deliveryId ← Delivery Context 참조 (ID만 보관)
├── OrderStatus (Value Object)
│ └── | PAID | ORDER_CONFIRMED | CANCELLED
└── requestDetails
FixOrder
├── id (UUID)
├── supplierId ← Company Context 참조 (ID만 보관)
├── receiverId ← Company Context 참조 (ID만 보관)
├── productId ← Product Context 참조 (ID만 보관) // 주문 하나당 상품 한 종류만
├── quantity
├── deliveryId ← Delivery Context 참조 (ID만 보관)
└── requestDetails

MSA에서는 하나의 비즈니스 로직이 여러 서비스에 걸쳐 수행되기 때문에,
단일 트랜잭션으로 처리하던 모놀리식 구조와 달리 분산 트랜잭션 문제가 발생한다.
겉보기에는 주문과 배송이 동시에 생성되는 것처럼 보이지만,
주문 임시 생성 → 배송 생성 → 주문 확정
실제로는 위와 같은 순차적인 처리 과정을 거친다.
이 과정에서 중간 단계(예: 배송 생성 실패)에서 문제가 발생할 경우,
데이터 불일치가 발생할 수 있다.
이러한 문제를 해결하기 위해 SAGA 패턴을 사용한다.
각 서비스는 자신의 로컬 트랜잭션만 처리한다.
문제가 발생하면 보상 트랜잭션(Compensation Transaction) 을 통해 이전 작업을 되돌린다.
👉 분산 환경에서 최종 일관성(Eventual Consistency) 을 보장한다.
서비스 내부에서는 문제가 없더라도,
서비스 간 통신 과정에서 네트워크 장애가 발생할 수 있다.
이러한 문제를 해결하기 위해 Inbox/Outbox 패턴을 사용한다.
✔ Outbox 패턴
DB에 이벤트를 함께 저장
이후 메시지 브로커(Kafka)로 안전하게 발행
✔ Inbox 패턴
이미 처리한 이벤트를 기록
중복 처리 방지
“데이터를 가져오는 것”과 “데이터를 바꾸는 것”은 실패했을 때 영향이 완전히 다르기 때문에 서비스 간 통신은 목적에 따라 다음과 같이 구분한다.
1. 조회(Read)
Feign Client (동기 호출) 사용
실시간 데이터 조회에 적합
2. 상태 변경 / 이벤트 전달
Kafka 기반 비동기 이벤트 처리
서비스 간 결합도를 낮추고 확장성 확보
이벤트 카프카 처리 정보가 필요할 때
상품의 재고 필드 -> 재고 객체

각 애그리거트는 도메인, 컨트롤러, 서비스 레포지토리 4계층 구조를 가진다.
그래서 이 경우 주문 확정 테이블을 애그리거트로 나눌 정도로 4계층 구조를 나눌 필요가 있는지 (인프라를 개별적으로 가질 수 있는지)
생각해봐야한다.
Order (Aggregate Root)
├── id (UUID)
├── supplierId ← Company Context 참조 (ID만 보관)
├── receiverId ← Company Context 참조 (ID만 보관)
├── productId ← Product Context 참조 (ID만 보관) // 주문 하나당 상품 한 종류만
├── quantity || // 상품의 수량
├── deliveryId ← Delivery Context 참조 (ID만 보관)
├── OrderStatus (Value Object)
│ └── | PAID | ORDER_CONFIRMED | CANCELLED
└── requestDetails
FixOrder (Aggregate)
├── id (UUID)
├── supplierId ← Company Context 참조 (ID만 보관)
├── receiverId ← Company Context 참조 (ID만 보관)
├── productId ← Product Context 참조 (ID만 보관) // 주문 하나당 상품 한 종류만
├── quantity
├── deliveryId ← Delivery Context 참조 (ID만 보관)
└── requestDetails
+ 컨텍스트 분리
