SEDA(Staged Event-Driven Architecture)는 확장성과 유연성을 극대화하기 위해 복잡한 애플리케이션을 여러 개의 "Stage"(단계)로 나누어 각각의 단계를 이벤트 기반으로 처리하는 소프트웨어 아키텍처 패턴입니다. SEDA의 주요 목표는 고성능, 고확장성, 고가용성을 달성하는 것입니다.
아래는 Spring Boot를 사용하여 SEDA 아키텍처를 구현한 간단한 예제입니다. 여기서는 주문 처리를 세 단계로 나누어 처리합니다: 수신 -> 처리 -> 응답 전송.
import org.springframework.context.ApplicationEvent;
public class OrderEvent extends ApplicationEvent {
private String orderId;
private String product;
private int quantity;
public OrderEvent(Object source, String orderId, String product, int quantity) {
super(source);
this.orderId = orderId;
this.product = product;
this.quantity = quantity;
}
public String getOrderId() {
return orderId;
}
public String getProduct() {
return product;
}
public int getQuantity() {
return quantity;
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
@Autowired
public OrderService(ApplicationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
public void createOrder(String orderId, String product, int quantity) {
OrderEvent event = new OrderEvent(this, orderId, product, quantity);
eventPublisher.publishEvent(event);
}
}
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public interface Stage {
void process(OrderEvent event);
}
@Service
public class ReceiveStage implements Stage {
private final BlockingQueue<OrderEvent> nextStageQueue = new LinkedBlockingQueue<>();
@Override
public void process(OrderEvent event) {
try {
System.out.println("ReceiveStage processing order: " + event.getOrderId());
nextStageQueue.put(event);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public BlockingQueue<OrderEvent> getNextStageQueue() {
return nextStageQueue;
}
}
@Service
public class ProcessStage implements Stage {
private final BlockingQueue<OrderEvent> nextStageQueue = new LinkedBlockingQueue<>();
@Override
public void process(OrderEvent event) {
try {
System.out.println("ProcessStage processing order: " + event.getOrderId());
nextStageQueue.put(event);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
public BlockingQueue<OrderEvent> getNextStageQueue() {
return nextStageQueue;
}
}
@Service
public class SendResponseStage implements Stage {
@Override
public void process(OrderEvent event) {
System.out.println("SendResponseStage sending response for order: " + event.getOrderId());
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;
@Service
@EnableAsync
public class SEDA implements ApplicationListener<OrderEvent> {
private final ReceiveStage receiveStage;
private final ProcessStage processStage;
private final SendResponseStage sendResponseStage;
@Autowired
public SEDA(ReceiveStage receiveStage, ProcessStage processStage, SendResponseStage sendResponseStage) {
this.receiveStage = receiveStage;
this.processStage = processStage;
this.sendResponseStage = sendResponseStage;
}
@Async
@Override
public void onApplicationEvent(OrderEvent event) {
receiveStage.process(event);
new Thread(() -> {
try {
OrderEvent receivedEvent = receiveStage.getNextStageQueue().take();
processStage.process(receivedEvent);
OrderEvent processedEvent = processStage.getNextStageQueue().take();
sendResponseStage.process(processedEvent);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
위의 예제 코드를 모두 작성한 후에는 OrderService를 통해 주문이 생성될 때마다 OrderEvent가 발생하고, 이를 SEDA 클래스에서 단계별로 비동기적으로 처리하는 것을 확인할 수 있습니다.
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class OrderServiceTest {
@Autowired
private OrderService orderService;
@Test
void testCreateOrder() {
orderService.createOrder("ORD-123", "Product A", 2);
// 주문 생성 후 각 단계의 로그 확인
}
}
이 예제는 Spring Boot를 사용하여 SEDA 아키텍처를 구현한 예제입니다. 각 단계는 BlockingQueue를 통해 다음 단계로 이벤트를 전달하며, 이벤트 기반으로 비동기적으로 작업을 처리합니다. 이를 통해 SEDA의 기본 개념을 이해하고 적용할 수 있습니다.