업데이트

This commit is contained in:
jyjang
2020-04-19 16:23:51 +09:00
parent 96631ee507
commit 208af8e68c
2 changed files with 104 additions and 23 deletions

121
README.md
View File

@@ -1,6 +1,28 @@
# 예제 - 음식배달
MSA/DDD/Event Storming/EDA 를 포괄하는 분석/설계/구현/운영 전단계 예제입니다.
본 예제는 MSA/DDD/Event Storming/EDA 를 포괄하는 분석/설계/구현/운영 전단계를 커버하도록 구성한 예제입니다.
이는 클라우드 네이티브 애플리케이션의 개발에 요구되는 체크포인트들을 통과하기 위한 예시 답안을 포함합니다.
- 체크포인트 : https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW
# Table of contents
- [예제 - 음식배달](#---)
- [서비스 시나리오](#-)
- [체크포인트](#)
- [분석/설계](#)
- [구현:](#)
- [DDD 의 적용](#ddd--)
- [폴리글랏 퍼시스턴스](#-)
- [폴리글랏 프로그래밍](#-)
- [동기식 호출 과 Fallback 처리](#---fallback-)
- [비동기식 호출 / 시간적 디커플링 / 장애격리 / 최종 (Eventual) 일관성 테스트](#---------eventual--)
- [운영](#)
- [CI/CD 설정](#cicd-)
- [동기식 호출 / 서킷 브레이킹 / 장애격리](#------)
- [오토스케일 아웃](#-)
- [무정지 재배포](#-)
- [신규 개발 조직의 추가](#---)
## 서비스 시나리오
@@ -29,8 +51,6 @@ MSA/DDD/Event Storming/EDA 를 포괄하는 분석/설계/구현/운영 전단
## 체크포인트
https://workflowy.com/s/assessment-check-po/T5YrzcMewfo4J6LW
- 분석 설계
- 이벤트스토밍:
- 스티커 색상별 객체의 의미를 제대로 이해하여 헥사고날 아키텍처와의 연계 설계에 적절히 반영하고 있는가?
@@ -143,14 +163,6 @@ public class 결제이력 {
private String orderId;
private Double 금액;
@PrePersist
public void onPrePersist(){
결제승인됨 결제승인됨 = new 결제승인됨();
BeanUtils.copyProperties(this, 결제승인됨);
결제승인됨.publish();
}
public Long getId() {
return id;
}
@@ -290,21 +302,22 @@ public interface 결제이력Service {
```
- 동기식 호출에서는 호출 시간에 따른 타임 커플링이 발생하며, 결제 시스템이 장애가 나면 주문도 못받는다는 것을 확인:
```
# 결제 서비스를 잠시 내려놓음
kill (lsof -i)
# 결제 (pay) 서비스를 잠시 내려놓음 (ctrl+c)
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울 #오류
http localhost:8080/주문s 품목=피자 수량=2 주소=서울 #오류
http localhost:8081/orders item=통닭 storeId=1 #Fail
http localhost:8081/orders item=피자 storeId=2 #Fail
#결제서비스 재기동
cd 결제
mvn spring-boot:run
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울 #성공
http localhost:8080/주문s 품목=피자 수량=2 주소=서울 #성공
http localhost:8081/orders item=통닭 storeId=1 #Success
http localhost:8081/orders item=피자 storeId=2 #Success
```
- 또한 과도한 요청시에 서비스 장애가 도미노 처럼 벌어질 수 있다. (서킷브레이커, 폴백 처리는 운영단계에서 설명한다.)
@@ -315,24 +328,86 @@ http localhost:8080/주문s 품목=피자 수량=2 주소=서울 #성공
### 비동기식 호출 / 시간적 디커플링 / 장애격리 / 최종 (Eventual) 일관성 테스트
결제가 이루어진 후에 상점시스템으로 이를 알려주는 행위는 동기식이 아니라 비 동기식으로 처리하여 상점 시스템의 처리를 위하여 결제주문이 블로킹 되지 않아도록 처리한다.
- 이를 위하여 결제이력에 기록을 남긴 후에 곧바로 결제승인이 되었다는 도메인 이벤트를 카프카로 송출한다(Publish)
```
#상점 서비스를 잠시 내려놓음
kill (lsof -i )
package fooddelivery;
@Entity
@Table(name="결제이력_table")
public class 결제이력 {
...
@PrePersist
public void onPrePersist(){
결제승인됨 결제승인됨 = new 결제승인됨();
BeanUtils.copyProperties(this, 결제승인됨);
결제승인됨.publish();
}
}
```
- 상점 서비스에서는 결제승인 이벤트에 대해서 이를 수신하여 자신의 정책을 처리하도록 PolicyHandler 를 구현한다:
```
package fooddelivery;
...
@Service
public class PolicyHandler{
@StreamListener(KafkaProcessor.INPUT)
public void whenever결제승인됨_주문정보받음(@Payload 결제승인됨 결제승인됨){
if(결제승인됨.isMe()){
System.out.println("##### listener 주문정보받음 : " + 결제승인됨.toJson());
// 주문 정보를 받았으니, 요리를 슬슬 시작해야지..
}
}
}
```
실제 구현을 하자면, 카톡 등으로 점주는 노티를 받고, 요리를 마친후, 주문 상태를 UI에 입력할테니, 우선 주문정보를 DB에 받아놓은 후, 이후 처리는 해당 Aggregate 내에서 하면 되겠다.:
```
@Autowired 주문관리Repository 주문관리Repository;
@StreamListener(KafkaProcessor.INPUT)
public void whenever결제승인됨_주문정보받음(@Payload 결제승인됨 결제승인됨){
if(결제승인됨.isMe()){
카톡전송(" 주문이 왔어요! : " + 결제승인됨.toString(), 주문.getStoreId());
주문관리 주문 = new 주문관리();
주문.setId(결제승인됨.getOrderId());
주문관리Repository.save(주문);
}
}
```
상점 시스템은 주문/결제와 완전히 분리되어있으며, 이벤트 수신에 따라 처리되기 때문에, 상점시스템이 유지보수로 인해 잠시 내려간 상태라도 주문을 받는데 문제가 없다:
```
# 상점 서비스 (store) 를 잠시 내려놓음 (ctrl+c)
#주문처리
http localhost:8080/주문s 품목=통닭 수량=1 주소=서울
http localhost:8080/주문s 품목=피자 수량=2 주소=서울
http localhost:8081/orders item=통닭 storeId=1 #Success
http localhost:8081/orders item=피자 storeId=2 #Success
#주문상태 확인
http localhost:8080/주문s # 주문상태 안바뀜 확인
http localhost:8080/orders # 주문상태 안바뀜 확인
#상점 서비스 기동
cd 상점
mvn spring-boot:run
#주문상태 확인
http localhost:8080/주문s # 모든 주문의 상태가 배송됨으로 확인
http localhost:8080/orders # 모든 주문의 상태가 "배송됨"으로 확인
```

View File

@@ -11,11 +11,17 @@ import org.springframework.stereotype.Service;
@Service
public class PolicyHandler{
@Autowired 주문관리Repository 주문관리Repository;
@StreamListener(KafkaProcessor.INPUT)
public void whenever결제승인됨_주문정보받음(@Payload 결제승인됨 결제승인됨){
if(결제승인됨.isMe()){
System.out.println("##### listener 주문정보받음 : " + 결제승인됨.toJson());
주문관리 주문 = new 주문관리();
주문.setId(결제승인됨.getOrderId())
주문관리Repository.save(주문);
}
}
@StreamListener(KafkaProcessor.INPUT)