ORDER
SYSTEMUSER SERVICE
API GW
PRODUCT
SYSTEM
SCM
SYSTEM
EXTERNAL
SYSTEMS


MDM
SYSTEMBACK-OFFICE
SCM
SYSTEM



SERVICE
SERVICE
SERVICE
SERVICE
SYSTEM


✔
✔ (customizing)
✔ , 5
✔ (Amazon Web Service)
LOGISTICS SYSTEM
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
✔ Spring Boot (Java 1.8)
✔ AWS Elastic Beanstalk
: https://allegro.tech/2015/01/working-with-legacy-architecture.html
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
• 기능 변경 또는 추가 시 많은 비용이 필요
• 코드 변경시 다양한 부수효과(side effect) 발생
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
.
!"" build.gradle
#"" src
!"" config
!"" main
$   !"" java
$   $   #"" com
$   $      #"" woowahan
$   $      #"" scm
$   $      !"" purchase
$   $         !"" domain
$   $         !"" application
$   $         #"" infrastructure
$   $      !"" delivery
$   $      #"" adapter
$   $         !"" order
$   $         !"" masterdata
$   $         #"" logistics
$   #"" resources
#"" test
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
✔ 부족한 비즈니스 지식과 도메인 이해도를 가지고 바로 시작할 수 있다.
✔ 필요하다면 빠르게 고칠 수 있고, 모델을 성장 시킬 수 있다.
✔ 잘 구성된 모듈은 단일 JVM에서 동작하는 마이크로서비스로 볼 수도 있다.
✔
✔
✔ IDE
LOGISTICS
ADAPTER
DeliveryService
----------------------

+createInvoice(Order)
PurchaseService
----------------------

+createOrder(Order)
WarehousingManagement
-------------------------

+process(OrderComplated)
void process(OrderCompleted orderCompleted) {
PurchaseOrder order = purchaseService.createOrder(..);
logisticsOperations.register(order);
DeliveryInvoice invoice = deliveryService.createInvoice(..);
logisticsOperations.register(invoice);
}
✔
✔
✔ IDE
@Transactional
LOGISTICS
ADAPTER
DeliveryService
----------------------

+createInvoice(Order)
PurchaseService
----------------------

+createOrder(Order)
WarehousingManagement
-------------------------

+process(OrderComplated)
✔
✔
✔ IDE
LOGISTICS
ADAPTER
DeliveryService
----------------------

+createInvoice(Order)
PurchaseService
----------------------

+createOrder(Order)
WarehousingManagement
-------------------------

+process(OrderComplated)
LOGISTICS
ADAPTER
✔ 새로운 기능을 추가하기 위해 기존 코드를 수정해야 함
✔ 모듈간 상호 참조 등으로 순환 의존성이 생기는 등에 부작용
✔ 즉, 모듈간 강결합으로 단일 모듈에 경계가 무너지는 현상
DeliveryService
----------------------

+createInvoice(Order)
PurchaseService
----------------------

+createOrder(Order)
WarehousingManagement
-------------------------

+process(OrderComplated)
InventoryService
----------------------

+adjustStock(Order)
DeliveryService
PurchaseService
InventoryService
Warehousing
Management
DeliveryService
PurchaseService
InventoryService
Warehousing
Management
interface ApplicationEventPublisher {
void publishEvent(ApplicationEvent event);
void publishEvent(Object event);
}
class ApplicationComponent {
@EventListener
void handle(OrderCompleted orderCompleted) { }
}
interface ApplicationListener<E extends ApplicationEvent>
extends EventListener {
void onApplicationEvent(E event);
}
DeliveryService
PurchaseService
class InventoryService {
@EventListener
void handle(OrderCompleted orderCompleted) { }
}
class OrderEventRelayer {
@SqsListener("order-message-queue")
void relayEvent(OrderCompletedMessage message) {
applicationEventPublisher.publishEvent(
message.toOrderCompleted()
);
}
}
LOGISTICS
SYSTEM
LOGISTICS
ADAPTER
RPC Stub RPC Server
SHARED
LIBRARY
HTTP Client HTTP Server
- (RESOURCE) - URI
- (Verb) - HTTP Method
- (Representations)
Messaging API Messaging API
MESSAGING
SYSTEM
PRODUCT
SYSTEM
ORDER
SYSTEM
REMOTE
SYSTEMS
RESTFUL
API

SERVICE
ORDER
ADAPTER
EXTERNAL
ADAPTERS
MESSAGING
SYSTEM
LOGISTICS
SERVICE
DELIVERY
SERVICE
PURCHASE
SERVICE
PRODUCT
SYSTEM
ORDER
SYSTEM
REMOTE
SYSTEMS
RESTFUL
API

SERVICE
ORDER
ADAPTER
EXTERNAL
ADAPTERS
MESSAGING
SYSTEM
LOGISTICS
SERVICE
DELIVERY
SERVICE
PURCHASE
SERVICE
RESTFUL
API

SERVICE
ORDER
ADAPTER
EXTERNAL
ADAPTERS
MESSAGING
SYSTEM
LOGISTICS
SERVICE
DELIVERY
SERVICE
PURCHASE
SERVICE
1) 최소한의 운영 비용으로 사용 할 수 있는가?
2) 메시지 전달 신뢰성을 가지고 있는가?
3) 단일 메시지가 여러 소비자에게 전달될 수 있는가?
4) 수평 확장성을 가지고 있는가?
5) 사용하기 쉬운가?
6) 모니터링 할 수 있는가?
✔ 최소 1회 이상 메시지 전달 보장, 최대 14일 유지
✔ 무제한 확장 가능, 빠른 읽기 처리량
✔ 메시지 풀링 방식, 단일 소비자(경쟁 소비자)
✔ 메시지 순서를 보장하지 않음
✔ 최대 256KB 데이터 전달
✔ AWS SDK 기반 쉬운 API 제공
1) 최소한의 운영 비용으로 사용 할 수 있는가?
2) 메시지 전달 신뢰성을 가지고 있는가?
3) 단일 메시지가 여러 소비자에게 전달될 수 있는가?
4) 수평 확장성을 가지고 있는가?
5) 사용하기 쉬운가?
6) 모니터링 할 수 있는가?
1) 최소한의 운영 비용으로 사용 할 수 있는가?
2) 메시지 전달 신뢰성을 가지고 있는가?
3) 단일 메시지가 여러 소비자에게 전달될 수 있는가?
4) 수평 확장성을 가지고 있는가?
5) 사용하기 쉬운가?
6) 모니터링 할 수 있는가?
✔ 발행/구독 모델 지원(1:1, 1:n)
✔ 무제한 확장 가능
✔ 소비자 유형별 메시지 전달 보장
✔ 메시지 전달 보장을 위해 SQS 사용 권장
✔ 그외 소비자 유형에 대해서는 보장 못함
✔ AWS SDK 기반 쉬운 API 제공
1) 최소한의 운영 비용으로 사용 할 수 있는가?
2) 메시지 전달 신뢰성을 가지고 있는가?
3) 단일 메시지가 여러 소비자에게 전달될 수 있는가?
4) 수평 확장성을 가지고 있는가?
5) 사용하기 쉬운가?
6) 모니터링 할 수 있는가?
✔ 메시지 전달 및 순서를 보장
✔ 이벤트 스트리밍 지원(중복 이벤트 제어 필요)
✔ 샤드(Shard) 개념을 통해 무제한 확장 가능
✔ 자동화된 확장이 아닌, 수동 제어
✔ 3개 리전에 복제, 최대 7시간 동안 데이터 유지
✔ 초당 5회 읽기 제한(샤드 당)
✔ 최대 1MB 데이터 전달
1) 최소한의 운영 비용으로 사용 할 수 있는가?
2) 메시지 전달 신뢰성을 가지고 있는가?
3) 단일 메시지가 여러 소비자에게 전달될 수 있는가?
4) 수평 확장성을 가지고 있는가?
5) 사용하기 쉬운가?
6) 모니터링 할 수 있는가?
✔ 메시지 전달 및 순서를 보장
✔ 점대점, 발행/구독 방식 메시지 기능 지원
✔ 인스턴스 기반 확장 가능
✔ 높은 가용성 및 메시지 안정성
✔ 업계 표준 API 및 프로토콜 호환
✔ AMQP, MQTT, OpenWire, STOMP 지원
✔ JMS(Java Message Service) 지원
PUBLISHER
SUBSCRIBER

#1
SNS TOPIC
SQS #1
SQS #2
SQS #3
SUBSCRIBER

#2
SUBSCRIBER

#3
SENDER RECEIVERSQS
RECEIVER
PUBLISHER
AWS
LAMBDA
SNS
HTTP
ENDPOINT
SQS
PRODUCT
SYSTEM
ORDER
SYSTEM
REMOTE
SYSTEMS
RESTFUL
API

SERVICE
ORDER
ADAPTER
EXTERNAL
ADAPTERS
SNS
LOGISTICS
SERVICE
DELIVERY
SERVICE
PURCHASE
SERVICE
SQS
SQS
ORDER
ADAPTER
EXTERNAL
ADAPTERS
SCM

SNS
SERVICE
SERVICE
SERVICE
SERVICE
ORDER

SERVICE
ORDER

SNS
SERVICE
SQS
SQS
ORDER SYSTEM SCM SYSTEM
SQS
ORDER

SYSTEM
SCM
SYSTEM
class OrderCompletedEvent {
private Order source;
private Date occurredOn;
public Order getSource() { .. }
public void setSource(Order source) { .. }
// getter, setter
}
{
"source" : {
"id" : "2018042129392759283",

"buyer": {},

"deliveryAddress": {},
"orderLines" : []
},
"occurredOn" : 1520393282506
}
- Message
- MessageChannel

- MessageConverter
- MessageHandler
@SqsListener("scm-adapter-order-event-queue")
void handle(OrderCompletedEvent event) {
//
}
MessageSendingOperations operations = new NotificationMessagingTemplate(amazonSNS);
operations.convertAndSend("order-event-stream", OrderCompletedEvent.of(order);
ORDER

SYSTEM
SCM
SYSTEM
@StreamListener(target = Sink.INPUT, condition = "payload.eventType == 'OrderCompletedEvent'")
public void handle(@Payload OrderCompletedEvent event) {
// handle event
}
@StreamListener(target=Sink.INPUT, condition="payload.eventType == 'OrderCanceledEvent'")
public void handle(@Payload OrderCanceledEvent event) {
// handle event
}
ORDER

SYSTEM
SCM
SYSTEM
ORDER

SYSTEM
SCM
SYSTEM
eventMessagePublisher.publishEvent(domainEvent);
@EventListener
void handle(DomainEvent event) { }
/
/
/
{
Spring Messaging
Woowahan Messaging AWS
Woowahan Messaging
Spring Cloud AWS
interface DomainEventPublisher {
void publishEvent(DomainEvent event);
}
interface DomainEventSubscriber {
void onEvent(DomainEvent event);
}
interface DomainEventPublisher {
void publishEvent(DomainEvent event);
}
class AmazonSNSDomainEventPublisher implements DomainEventPublisher {
private final NotificationMessagingTemplate messagingTemplate;
private final String destinationName;
@Override
public void publishEvent(DomainEvent event) {
Map<String, Object> headers = new HashMap<>(); {
headers.put(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON_VALUE);
}
EventEnvelope<DomainEvent> payload = new EventEnvelope<>(event);
messagingTemplate.convertAndSend(destinationName, payload, headers);
}
}
interface DomainEventSubscriber {
void onEvent(DomainEvent event);
}
class AmazonSQSDomainEventSubscriber implements DomainEventSubscriber {
private ApplicationEventPublisher applicationEventPublisher;
private ObjectMapper objectMapper;
@SqsListener("scm-adapter-order-event-queue")
public void receive(@NotificationMessage JsonNode payload) {
EventEnvelope eventEnvelope = objectMapper.treeToValue(payload, EventEnvelope.class);
if (DomainEvent.class.isAssignableFrom(eventEnvelope.getEvent().getClass())) {
onEvent((DomainEvent) eventEnvelope.getEvent());
}
}
@Override
public void onEvent(DomainEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
@EventListener
void handle(OrderCompletedEvent event) { }
class OrderCompletedEvent {
private Order source;
private Date occurredOn;
public Order getSource() { .. }
public void setSource(Order source) { .. }
// getter, setter
}
{
"source" : {
"id" : "2018042129392759283",
"orderLines" : []
},
"occurredOn" : 1520393282506
}
class OrderCompletedEvent {
private Order source;
private Date occurredOn;
public Order getSource() { .. }
public void setSource(Order source) { .. }
// getter, setter
}
{
"event" : {
"@eventType" : "woowabros.order.domain.event.OrderCompletedEvent",
"source" : {
"id" : "2018042129392759283",
"orderLines" : []
},
"occurredOn" : 1520393282506
}
}
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enableDefaultTyping();
OrderCompletedEvent event = OrderCompletedEvent.of(Order.createTemporaryOrder());
EventEnvelope envelope = EventEnvelope.wrap(event);
String orderCompletedEventJSON = objectMapper.writeValueAsString(envelope);
JavaType javaType = objectMapper.constructType(EventEnvelope.class);
EventEnvelope envelope = objectMapper.readValue(orderCompletedEventJSON, javaType);
OrderCompletedEvent event = (OrderCompletedEvent) envelope.getEvent();
✔
✔
✔ , (EIP )
✔ CQRS
✔ ,
✔
✔
✔ Event Sourcing, etc
✔
✔
✔ (SQS)
✔ Amazon Kinesis
EXTERNAL
SYSTEMS
PRODUCT
SYSTEM
ORDER
SYSTEM
EVENT
STREAM
ORDER
SYSTEMUSER SERVICE
API GW
PRODUCT
SYSTEM
SCM
SYSTEM
EXTERNAL
SYSTEMS


MDM
SYSTEMBACK-OFFICE
SCM
SYSTEM
ORDER
SYSTEMUSER SERVICE
API GW
PRODUCT
SYSTEM
SCM
SYSTEM
EXTERNAL
SYSTEMS


MDM
SYSTEMBACK-OFFICE
SCM
SYSTEM
✔ , (Implementing Domain-Driven Design)
✔ , (Domain-Driven Design Distilled)
✔ , 5.0 2/e(Spring 5.0 Microservices - Second Edition)

이벤트 기반 분산 시스템을 향한 여정