๋ฉด์ ๊ด: "๋๊ท๋ชจ ์ด์ปค๋จธ์ค ์์คํ ์ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ๋ก ์ค๊ณํด์ฃผ์ธ์. ํนํ ์๋น์ค ๋ถ๋ฆฌ์ ํต์ ์ ๋ต์ ๋ํด ์์ธํ ์ค๋ช ํด์ฃผ์ธ์."
์ง์์: ๋ค, ๋จผ์ ๋ช ๊ฐ์ง ํ์ธํ๊ณ ์ถ์ ์ฌํญ์ด ์์ต๋๋ค.
- ํ์ฌ ์์คํ ์ ๊ท๋ชจ์ ํธ๋ํฝ์ ์ด๋ ์ ๋์ธ๊ฐ์?
- ๊ฐ์ฅ ์ค์ํ ๋น์ฆ๋์ค ์๊ตฌ์ฌํญ์ ๋ฌด์์ธ๊ฐ์?
- ์ฑ๋ฅ์ด๋ ํ์ฅ์ฑ ๊ด๋ จ ํน๋ณํ ์๊ตฌ์ฌํญ์ด ์๋์?
- ํ์ฌ ๊ฒช๊ณ ์๋ ์ฃผ์ ๊ธฐ์ ์ ๋ฌธ์ ์ ์ ๋ฌด์์ธ๊ฐ์?
๋ฉด์ ๊ด:
- ์ผ ํ๊ท ์ฃผ๋ฌธ 10๋ง ๊ฑด, ํผํฌ ์๊ฐ๋๋ ํ๊ท ์ 5๋ฐฐ ์์ค์ ๋๋ค.
- ๋น ๋ฅธ ๊ธฐ๋ฅ ์ถ์์ ์๋น์ค ์์ ์ฑ์ด ๊ฐ์ฅ ์ค์ํฉ๋๋ค.
- ์ฃผ๋ฌธ ์์คํ ์ 99.99% ๊ฐ์ฉ์ฑ์ด ํ์ํ๋ฉฐ, ๊ฒฐ์ ๋ 3์ด ์ด๋ด ์๋ต์ด ํ์ํฉ๋๋ค.
- ํ์ฌ ๋ชจ๋๋ฆฌ์ ์์คํ ์์ ๋ฐฐํฌ๊ฐ ์ด๋ ต๊ณ , ์ผ๋ถ ๊ธฐ๋ฅ์ ์ฅ์ ๊ฐ ์ ์ฒด ์์คํ ์ ์ํฅ์ ์ฃผ๋ ๋ฌธ์ ๊ฐ ์์ต๋๋ค.
์ง์์: ์ดํดํ์ต๋๋ค. ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ๋ฅผ ์ค๊ณํด๋ณด๊ฒ ์ต๋๋ค.
// 1. ๋๋ฉ์ธ ๊ธฐ๋ฐ ์๋น์ค ๋ถ๋ฆฌ
public class ServiceBoundaries {
/*
Core Services:
- Product Service: ์ํ ๊ด๋ฆฌ
- Order Service: ์ฃผ๋ฌธ ์ฒ๋ฆฌ
- Payment Service: ๊ฒฐ์ ์ฒ๋ฆฌ
- Inventory Service: ์ฌ๊ณ ๊ด๋ฆฌ
- User Service: ํ์ ๊ด๋ฆฌ
Supporting Services:
- Notification Service: ์๋ฆผ ์ฒ๋ฆฌ
- Analytics Service: ๋ฐ์ดํฐ ๋ถ์
- Search Service: ๊ฒ์ ๊ธฐ๋ฅ
Infrastructure Services:
- API Gateway
- Service Discovery
- Configuration Service
*/
}
// 2. ์๋น์ค ๋จ์ ์์
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentServiceClient paymentClient;
private final InventoryServiceClient inventoryClient;
@Transactional
public OrderResult createOrder(OrderRequest request) {
// 1. ์ฌ๊ณ ํ์ธ
InventoryResult inventory = inventoryClient.checkInventory(
request.getProductId(), request.getQuantity());
// 2. ๊ฒฐ์ ์ฒ๋ฆฌ
PaymentResult payment = paymentClient.processPayment(
request.getPaymentInfo());
// 3. ์ฃผ๋ฌธ ์์ฑ
Order order = orderRepository.save(
Order.from(request, payment.getTransactionId()));
return OrderResult.success(order);
}
}
// 1. ๋๊ธฐ์ ํต์ (REST)
@FeignClient(name = "payment-service")
public interface PaymentServiceClient {
@PostMapping("/api/payments")
PaymentResult processPayment(@RequestBody PaymentRequest request);
}
// 2. ๋น๋๊ธฐ์ ํต์ (Event-Driven)
@Service
public class OrderEventHandler {
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
public void publishOrderCreatedEvent(Order order) {
OrderEvent event = OrderEvent.builder()
.orderId(order.getId())
.userId(order.getUserId())
.status(OrderStatus.CREATED)
.timestamp(Instant.now())
.build();
kafkaTemplate.send("order-events", event);
}
@KafkaListener(topics = "inventory-events")
public void handleInventoryEvent(InventoryEvent event) {
// ์ฌ๊ณ ์ด๋ฒคํธ ์ฒ๋ฆฌ
}
}
๋ฉด์ ๊ด: ์๋น์ค ๊ฐ ๋ฐ์ดํฐ ์ผ๊ด์ฑ์ ์ด๋ป๊ฒ ๋ณด์ฅํ์๋์?
@Service
public class OrderSagaCoordinator {
// 1. ์ฃผ๋ฌธ ์์ฑ Saga
public OrderResult createOrder(OrderRequest request) {
SagaInstance<OrderContext> saga = sagaManager.create(new OrderContext(request));
return saga.executeSteps(List.of(
// Step 1: ์ฌ๊ณ ํ์ธ
new SagaStep<>(
ctx -> inventoryService.reserve(ctx.getProductId()),
ctx -> inventoryService.cancelReservation(ctx.getProductId())
),
// Step 2: ๊ฒฐ์ ์ฒ๋ฆฌ
new SagaStep<>(
ctx -> paymentService.process(ctx.getPaymentInfo()),
ctx -> paymentService.refund(ctx.getTransactionId())
),
// Step 3: ์ฃผ๋ฌธ ์์ฑ
new SagaStep<>(
ctx -> orderRepository.create(ctx.getOrderDetails()),
ctx -> orderRepository.markAsFailed(ctx.getOrderId())
)
));
}
// 2. Saga ์ํ ๊ด๋ฆฌ
@Getter
public class SagaInstance<T> {
private final String sagaId;
private final T context;
private final Map<Integer, StepStatus> stepStatuses = new HashMap<>();
public void compensate(int fromStep) {
// ๋ณด์ ํธ๋์ญ์
์คํ
for (int i = fromStep; i >= 0; i--) {
steps.get(i).compensate(context);
stepStatuses.put(i, StepStatus.COMPENSATED);
}
}
}
}
@Service
public class EventSourcingHandler {
private final EventStore eventStore;
// 1. ์ด๋ฒคํธ ์ ์ฅ
public void saveEvent(OrderEvent event) {
EventStream stream = eventStore.getStream(event.getAggregateId());
stream.append(event);
eventStore.save(stream);
// ์ด๋ฒคํธ ๋ฐํ
eventPublisher.publish(event);
}
// 2. ์ํ ์ฌ๊ตฌ์ฑ
public Order reconstructOrderState(String orderId) {
EventStream stream = eventStore.getStream(orderId);
Order order = new Order();
stream.getEvents().forEach(event -> {
switch (event.getType()) {
case ORDER_CREATED:
order.apply((OrderCreatedEvent) event);
break;
case ORDER_UPDATED:
order.apply((OrderUpdatedEvent) event);
break;
// ... ๊ธฐํ ์ด๋ฒคํธ ์ฒ๋ฆฌ
}
});
return order;
}
}
// 1. Command ๋ชจ๋ธ
@Service
public class OrderCommandService {
private final EventSourcingHandler eventHandler;
@Transactional
public void createOrder(CreateOrderCommand command) {
OrderCreatedEvent event = OrderCreatedEvent.builder()
.orderId(command.getOrderId())
.userId(command.getUserId())
.items(command.getItems())
.build();
eventHandler.saveEvent(event);
}
}
// 2. Query ๋ชจ๋ธ
@Service
public class OrderQueryService {
private final OrderReadRepository readRepository;
public OrderDetailView getOrderDetails(String orderId) {
return readRepository.findOrderDetails(orderId)
.orElseThrow(() -> new OrderNotFoundException(orderId));
}
// ์ด๋ฒคํธ ๊ตฌ๋
ํ์ฌ ์ฝ๊ธฐ ๋ชจ๋ธ ์
๋ฐ์ดํธ
@EventListener
public void on(OrderCreatedEvent event) {
OrderDetailView view = OrderDetailView.from(event);
readRepository.save(view);
}
}
@Service
public class ResiliencyManager {
// 1. Circuit Breaker ๊ตฌํ
@CircuitBreaker(name = "payment")
public PaymentResult processPayment(PaymentRequest request) {
return paymentClient.processPayment(request);
}
// 2. ์ฌ์๋ ์ ์ฑ
@Retryable(
value = {ServiceTemporaryException.class},
maxAttempts = 3,
backoff = @Backoff(delay = 1000))
public InventoryResult checkInventory(String productId) {
return inventoryClient.checkAvailability(productId);
}
// 3. Fallback ์ฒ๋ฆฌ
@Recover
public PaymentResult paymentFallback(PaymentRequest request) {
// ๋์ฒด ๊ฒฐ์ ์ฒ๋ฆฌ ๋๋ ์คํจ ์ฒ๋ฆฌ
return PaymentResult.failed("Service temporarily unavailable");
}
}
@Configuration
public class ObservabilityConfig {
// 1. ๋ถ์ฐ ์ถ์
@Bean
public Tracer jaegerTracer() {
return new Tracer.Builder("order-service")
.withSampler(new ConstSampler(true))
.withReporter(jaegerReporter())
.build();
}
// 2. ๋ฉํธ๋ฆญ ์์ง
@Bean
public MeterRegistry meterRegistry() {
return new SimpleMeterRegistry();
}
// 3. ๋ก๊ทธ ์ง๊ณ
@Bean
public LogstashAppender logstashAppender() {
LogstashAppender appender = new LogstashAppender();
appender.setDestination("logstash:5000");
appender.setEncoding("UTF-8");
return appender;
}
}
์ด๋ฌํ ์ ๋ต๋ค์ ํตํด:
- ํธ๋์ญ์ ์ ์ผ๊ด์ฑ ๋ณด์ฅ
- ์๋น์ค ๊ฐ ๋์จํ ๊ฒฐํฉ ์ ์ง
- ์ฅ์ ๊ฒฉ๋ฆฌ์ ๋ณต๊ตฌ ๊ฐ๋ฅ
- ์์คํ ๊ฐ์์ฑ ํ๋ณด
๋ฅผ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
๋ฉด์ ๊ด: ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ๋จ์ ๊ณผ ์ด๋ฅผ ๊ทน๋ณตํ๊ธฐ ์ํ ์ ๋ต์ ๋ฌด์์ธ๊ฐ์?
@Configuration
public class ServiceManagementConfig {
// 1. API Gateway๋ฅผ ํตํ ๋ณต์ก์ฑ ์ถ์ํ
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("order-service", r -> r
.path("/api/orders/**")
.filters(f -> f
.circuitBreaker(c -> c.setFallbackUri("/fallback"))
.rateLimit(config -> config.setKeyResolver(userKeyResolver))
.retry(retryConfig))
.uri("lb://order-service"))
.build();
}
// 2. ์๋น์ค ๋์ค์ปค๋ฒ๋ฆฌ
@Bean
public DiscoveryClient discoveryClient() {
return ServiceDiscoveryBuilder.builder(ServiceInstance.class)
.client(curatorFramework)
.basePath("/services")
.watchInstances(true)
.build();
}
}
@Configuration
public class OperationalToolingConfig {
// 1. ์ค์ ์ง์ค์ ์ค์ ๊ด๋ฆฌ
@Bean
public ConfigurationClient configClient() {
return ConfigClientBuilder.create()
.withConfigServer("config-server:8888")
.withFailFast(true)
.withRetry(true)
.build();
}
// 2. ํตํฉ ๋ชจ๋ํฐ๋ง
@Bean
public MetricsRegistry metricsRegistry() {
return new CompositeMeterRegistry()
.add(new PrometheusRegistry())
.add(new GraphiteRegistry())
.add(new ElasticRegistry());
}
// 3. ์๋ํ๋ ๋ฐฐํฌ ํ์ดํ๋ผ์ธ
@Bean
public DeploymentPipeline deploymentPipeline() {
return DeploymentPipeline.builder()
.withCanaryDeployment()
.withBlueGreenDeployment()
.withRollbackStrategy()
.build();
}
}
@Service
public class DataConsistencyManager {
// 1. ์ด๋ฒคํธ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋๊ธฐํ
@EventListener
public void handleDataChangeEvent(DataChangeEvent event) {
switch (event.getType()) {
case USER_UPDATED:
syncUserData(event.getUserId());
break;
case ORDER_STATUS_CHANGED:
syncOrderStatus(event.getOrderId());
break;
}
}
// 2. ๋ฐ์ดํฐ ๋ฒ์ ๋
@Service
public class DataVersionManager {
private final VersionRepository versionRepository;
public void updateData(String key, String value, long version) {
if (!versionRepository.checkAndUpdate(key, version)) {
throw new OptimisticLockException("Version conflict");
}
dataRepository.save(key, value, version + 1);
}
}
}
@Service
public class NetworkResiliencyManager {
// 1. ํ์์์ ๊ด๋ฆฌ
@Bean
public RestTemplate resilientRestTemplate() {
return RestTemplateBuilder.create()
.setConnectTimeout(Duration.ofSeconds(3))
.setReadTimeout(Duration.ofSeconds(5))
.build();
}
// 2. ๋ฒํฌํค๋ ํจํด
@Bean
public ThreadPoolTaskExecutor serviceExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(50);
executor.setRejectedExecutionHandler(new CallerRunsPolicy());
return executor;
}
// 3. ์ํท๋ธ๋ ์ด์ปค
@CircuitBreaker(name = "serviceCall", fallbackMethod = "fallback")
public ServiceResponse callService(ServiceRequest request) {
return serviceClient.call(request);
}
}
@SpringBootTest
public class ServiceIntegrationTests {
// 1. ๊ณ์ฝ ํ
์คํธ
@Test
public void verifyServiceContract() {
// Consumer Driven Contract Testing
ContractVerifier.verify(consumerContract)
.againstProvider(providerStub);
}
// 2. ํตํฉ ํ
์คํธ ํ๊ฒฝ
@TestConfiguration
public class TestConfig {
@Bean
public TestContainers testContainers() {
return new TestContainers()
.withKafka()
.withMongo()
.withRedis();
}
}
// 3. ์นด์ค์ค ์์ง๋์ด๋ง
@Test
public void performChaosTest() {
chaosMonkey.injectLatency()
.withLatency(Duration.ofMillis(500))
.withProbability(0.1);
verify(serviceMetrics.getLatency())
.isLessThan(Duration.ofSeconds(1));
}
}
@Configuration
public class CostOptimizationConfig {
// 1. ์์ ์ฌ์ฉ๋ ๋ชจ๋ํฐ๋ง
@Bean
public ResourceMonitor resourceMonitor() {
return ResourceMonitor.builder()
.withCPUMonitoring()
.withMemoryMonitoring()
.withNetworkMonitoring()
.build();
}
// 2. ์๋ ์ค์ผ์ผ๋ง
@Bean
public AutoScaler autoScaler() {
return AutoScaler.builder()
.withMetricBasedScaling()
.withScheduleBasedScaling()
.withCostOptimization()
.build();
}
// 3. ๋น์ฉ ๋ถ์
@Scheduled(cron = "0 0 0 * * *")
public void analyzeCosts() {
costAnalyzer.generateReport()
.byService()
.byResource()
.byTimeRange()
.export();
}
}
์ด๋ฌํ ์ ๋ต๋ค์ ํตํด ๋ง์ดํฌ๋ก์๋น์ค์ ์ฃผ์ ๋จ์ ๋ค์ ๊ทน๋ณตํ๋ฉด์๋ ์ฅ์ ์ ์ต๋ํ ํ์ฉํ ์ ์์ต๋๋ค. ํต์ฌ์:
- ์ ์ ํ ๋๊ตฌ์ ํ๋ ์์ํฌ ์ ํ
- ์๋ํ๋ ์ด์ ํ๊ฒฝ ๊ตฌ์ถ
- ๊ฒฌ๊ณ ํ ๋ชจ๋ํฐ๋ง๊ณผ ๋ก๊น ์์คํ
- ์ฒด๊ณ์ ์ธ ํ ์คํธ ์ ๋ต
- ํจ์จ์ ์ธ ๋น์ฉ ๊ด๋ฆฌ
๋ฅผ ํตํด ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ ์ฒ์ ๋ณต์ก์ฑ์ ๊ด๋ฆฌ ๊ฐ๋ฅํ ์์ค์ผ๋ก ์ ์งํ๋ ๊ฒ์ ๋๋ค.