Notice
Recent Posts
Recent Comments
Link
«   2026/08   »
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 27 28 29
30 31
Tags
more
Archives
Today
Total
관리 메뉴

나의 개발 일상 기록

[MSA] 주문 프로젝트_OrderDomain 본문

MSA/MSA 구축

[MSA] 주문 프로젝트_OrderDomain

느린 거북이 2021. 9. 16. 16:52

ORDER DOMAIN

 

Order Domain의 경우 Validation을 확인해야 할 부분들이 많았습니다. 결제 금액, 결제 수단, 결제 상태 등 돈과 관련이 되는 도메인이다보니 꼼꼼하게 많은 사항들을 살펴보아야 했습니다. Order Domain은 Order와 OrderItem, OrderItemOptionGroup, OrderItemOption간의 연관관계를 가지고 있음을 생각해볼 수 있습니다. 이 중 Order를 Aggregate Root하여 하위 객체들을 1 : N 관계로 매핑해주어야합니다. Item 메인처럼 객체 생성과정에서 Order뿐만 아니라 하위 객체들의 정합성이 일관되게 지켜질 수 있도록 하여야 합니다. 또한, OneToMany, ManyToOne 양방향 참조로 설정하고 성능과 관계설정의 타협점을 찾을 수 있도록 노력해야 합니다.

 

 

주문의 전체 가격 계산 구현

 

주문한 상품의 전체 금액을 구하기 위해서는 간단히 생각을 해본다면 '(주문한 상품 가격 + 주문한 상품 옵션 가격) * 주문 수량'이 될 것입니다. 주문 총 금액을 구하기 위해해서는 Order Aggregate의 Root인 Order가  계산을 합니다. 주문에 포함된 OrderItem 각각의 가격 총합을 구해야 하며 이는 아래의 로직처럼 구현을 할 수 있습니다.

OrderItem의 가격은 각각 Item과 ItemOption의 가격의 합으로 이루어지기 때문에 아래의 로직처럼 OrderItemOption의 총 금액을 구할 수 있습니다.

 

기존에 상품의 총 금액을 구현할때는 jdbc를 사용하여 quey를 통해 주문 금액의 총 금액을 구하는 방식으로 구현을 하였지만 quey가 복잡할수록 가독성이 떨어지고 분석을 하는데 시간이 오래 걸렸습니다. 그러나 이와 같은 방식으로 구현을 한다면 새로운 개발자가 합류하더라도 한눈에 보기 편할 것이라고 생각이 들었습니다.

 

@Embedded와 @Embeddable

 

@embedded는 하나의 객체 내 다른 Entity 객체의 소유를 표기할 때 사용합니다. 내장될 객체는 @Embeddable로 정의 합니다. Embedde 타입은 예를 들어, 주소라는 데이터를 각각의 String값으로 address1, address2, zipcode로 구분을 한다면 해당 데이터들을 사용하는 클래스에는 필요한 것은 주소 데이터 하나인데 변수가 3개가 늘어나게 됩니다. 클래스는 변수가 많아질수록 해석하기 어려워지고 복잡하게 느껴집니다. 그렇게 때문에 JPA에서는 'Embedded Type'이라고 부릅니다. Embedded Type타입을 사용하면 가독성도 높아지고, 응집력과 재사용성이 높게 디자인을 할 수 있습니다.

 

아래의 코드처럼 주소라는 변수가 필요하여 @Embedded 어노테이션을 사용하여 다른 Entity 객체를 소유합니다.

 

주소라는 변수에 내장될 객체는 아래의 코드처럼 변수들을 선언해주며 클래스 파일에 @Embeddable 어노테이션을 선언해줍니다.

 

결제 처리 프로세스

 

프로세스는 로직의 순서를 어떻게 하느냐에 따라 많은 것이 바뀔수가 있습니다. 결제 처리 프로세스 또한 마찬가지로 생각을 해보았을 때 '주문 정보 가져오기 -> 외부 결제 서비스를 활용하여 결제 진행 -> 주문 상태 완료 처리' 생각해 볼 수 있습니다. 앞서 언급한 로직은 외부 결제 처리가 성공 후 주문 상태 완료 처리에서 오류가 발생할 경우 결제를 취소하고 사용자에게 돈을 다시 환불을 하는 조금은 복잡한 보상 트랜잭션을 구현을 햐여야 할 것입니다.

 

그러나, 로직의 순서를 다음과 같이 변경을 한다면 '주문 정보 가져오기 -> 주문 상태 완료 처리 -> 외부 결제 서비스를 활용하여 결제 진행' 주문 상태 완료에서 Exception이 발생을 하더라도 외부 API는 호출하지 않아도 됩니다. 외부 API에서 Exception이 발생이 되더라도 하나의 트랜잭션으로 묶기 때문에 주문 상태는 rollback이 될 것이기 때문에 보상 트랜잭션에 대한 구현은 따로 하지 않아도 되는 장점이 있습니다.

 

* 보상 트랜잭션

 

MSA환경에서는 하나의 프로세스를 완성하기 위해서는 여러 도메인의 연산을 모두 처리해야하는 경우가 있습니다. 가령 주문 완료의 경우 '물류창고 물품확인 -> 사용자 결제 완료 -> 멤버십 포인트 부여 -> 물품 배송 프로세스'가 있다고 가정한다면 만약 이 중에서 하나의 프로세스를 처리하는 과정에서 에러가 발생할 수 있습니다.

 

monolithic구조에서는 모든 프로세스가 하나의 트랜잭션으로 묶였을 것이기 때문에 rollback처리가 간단하지만 MSA구조에서는 에러 발생 직전의 중간 프로세스에서는 API 호출을 통해 처리가 완료되었을 것이라서 전체 rollback이 쉽지가 않습니다. MSA의 분산 트랜잭션 구조에서 프로세스 rollback을 위해 등장하는 개념이 보상 트랜잭션입니다.

 

보상트랜잭션은 이전에 Commit된 트랜잭션을 취소하는 연산입니다. 만약 결제가 완료된 프로세스는 결제를 취소하는 연산을 발생시키고 포인트를 부여했다면 포인트를 다시 회수하는 식의 연산을 발생시키는 것입니다.

 

문제점 및 고려사항


 - 데이터의 최종 일관성을 구현하는 보정 작업이 실패했을 때 파악하기 쉽지 않을 수 있습니다.
 - 수행 작업이 즉시 실패하지 않고, 차단될 수 있으므로 특정 형태의 제한 시간 메커니즘을 구현해야 할 수도 있습니다.
 - 보정 논리는 쉽게 일반화되지 않으며, Application 마다 다릅니다.
 - 보상 트랜잭션을 수행해도 시스템의 데이터가 반드시 원래 작업 시작 시의 시스템 상태로 돌아가는 것은 아닙니다.

Comments