extended analysis
This commit is contained in:
30
state_machines/enterprise_order_system/build.gradle
Normal file
30
state_machines/enterprise_order_system/build.gradle
Normal file
@@ -0,0 +1,30 @@
|
||||
plugins {
|
||||
id 'java'
|
||||
id 'org.springframework.boot' version '3.5.3'
|
||||
id 'io.spring.dependency-management' version '1.1.7'
|
||||
}
|
||||
|
||||
group = 'click.kamil'
|
||||
version = '0.0.1-SNAPSHOT'
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(21)
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.springframework.boot:spring-boot-starter'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-web'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-webflux'
|
||||
implementation 'org.springframework.boot:spring-boot-starter-artemis' // For JMS
|
||||
implementation 'org.springframework.boot:spring-boot-starter-amqp' // For RabbitMQ
|
||||
implementation 'org.springframework.statemachine:spring-statemachine-starter:3.2.0'
|
||||
|
||||
compileOnly 'org.projectlombok:lombok'
|
||||
annotationProcessor 'org.projectlombok:lombok'
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package click.kamil.examples.enterprise.api;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
@RequestMapping("/api/enterprise/orders")
|
||||
public interface OrderApi {
|
||||
|
||||
@PostMapping("/place")
|
||||
void placeOrder();
|
||||
|
||||
@PostMapping("/{id}/cancel")
|
||||
void cancelOrder(@PathVariable("id") String id);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package click.kamil.examples.enterprise.api;
|
||||
|
||||
import click.kamil.examples.enterprise.service.OrderService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class OrderController implements OrderApi {
|
||||
|
||||
private final OrderService orderService;
|
||||
|
||||
@Override
|
||||
public void placeOrder() {
|
||||
orderService.place();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelOrder(String id) {
|
||||
orderService.cancel(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package click.kamil.examples.enterprise.api;
|
||||
|
||||
import click.kamil.examples.enterprise.service.ReactivePaymentService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class ReactivePaymentController {
|
||||
|
||||
private final ReactivePaymentService paymentService;
|
||||
|
||||
@PostMapping("/api/enterprise/payments")
|
||||
public Mono<Void> pay(@RequestBody String orderId) {
|
||||
return paymentService.processPayment(orderId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package click.kamil.examples.enterprise.api;
|
||||
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class SecurityInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
stateMachine.sendEvent(OrderEvents.AUDIT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package click.kamil.examples.enterprise.config;
|
||||
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.statemachine.config.EnableStateMachine;
|
||||
import org.springframework.statemachine.config.StateMachineConfigurerAdapter;
|
||||
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
|
||||
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
|
||||
|
||||
@Configuration
|
||||
@EnableStateMachine(name = "enterpriseStateMachine")
|
||||
public class EnterpriseStateMachineConfig extends StateMachineConfigurerAdapter<String, String> {
|
||||
|
||||
@Value("${order.initial.state:NEW}")
|
||||
private String initialState;
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineStateConfigurer<String, String> states) throws Exception {
|
||||
states
|
||||
.withStates()
|
||||
.initial(initialState)
|
||||
.state("PENDING_PAYMENT")
|
||||
.state("PAID")
|
||||
.state("SHIPPED")
|
||||
.choice("CHECK_AVAILABILITY")
|
||||
.end("CANCELLED")
|
||||
.end("RETURNED")
|
||||
.end("DELIVERED");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configure(StateMachineTransitionConfigurer<String, String> transitions) throws Exception {
|
||||
transitions
|
||||
.withExternal()
|
||||
.source("NEW").target("CHECK_AVAILABILITY")
|
||||
.event(OrderEvents.PLACE)
|
||||
.and()
|
||||
.withChoice()
|
||||
.source("CHECK_AVAILABILITY")
|
||||
.first("PENDING_PAYMENT", (c) -> true)
|
||||
.last("CANCELLED")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("PENDING_PAYMENT").target("PAID")
|
||||
.event(OrderEvents.PAY)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("PAID").target("SHIPPED")
|
||||
.event(OrderEvents.SHIP)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("SHIPPED").target("DELIVERED")
|
||||
.event("FINALIZE")
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("PAID").target("CANCELLED")
|
||||
.event(OrderEvents.CANCEL)
|
||||
.and()
|
||||
.withExternal()
|
||||
.source("DELIVERED").target("RETURNED")
|
||||
.event(OrderEvents.RETURN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package click.kamil.examples.enterprise.constants;
|
||||
|
||||
public class OrderEvents {
|
||||
public static final String PLACE = "PLACE_ORDER";
|
||||
public static final String PAY = "PAY_ORDER";
|
||||
public static final String SHIP = "SHIP_ORDER";
|
||||
public static final String CANCEL = "CANCEL_ORDER";
|
||||
public static final String RETURN = "RETURN_ORDER";
|
||||
public static final String AUDIT = "AUDIT_EVENT";
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package click.kamil.examples.enterprise.messaging;
|
||||
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ReturnsRabbitListener {
|
||||
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
|
||||
@RabbitListener(queues = "returns.queue")
|
||||
public void onReturn(String orderId) {
|
||||
stateMachine.sendEvent(OrderEvents.RETURN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package click.kamil.examples.enterprise.messaging;
|
||||
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jms.annotation.JmsListener;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ShippingJmsListener {
|
||||
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
|
||||
@JmsListener(destination = "shipping.queue")
|
||||
public void onShippingReady(String orderId) {
|
||||
stateMachine.sendEvent(OrderEvents.SHIP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package click.kamil.examples.enterprise.service;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class ExternalTaxService {
|
||||
public void calculateTax(String orderId) {
|
||||
// Third-party logic
|
||||
System.out.println("Calculating tax for " + orderId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package click.kamil.examples.enterprise.service;
|
||||
|
||||
public interface OrderService {
|
||||
void place();
|
||||
void cancel(String id);
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package click.kamil.examples.enterprise.service;
|
||||
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
|
||||
@Override
|
||||
public void place() {
|
||||
stateMachine.sendEvent(OrderEvents.PLACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel(String id) {
|
||||
stateMachine.sendEvent(OrderEvents.CANCEL);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package click.kamil.examples.enterprise.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.statemachine.StateMachine;
|
||||
import org.springframework.stereotype.Service;
|
||||
import reactor.core.publisher.Mono;
|
||||
import click.kamil.examples.enterprise.constants.OrderEvents;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ReactivePaymentService {
|
||||
|
||||
private final StateMachine<String, String> stateMachine;
|
||||
|
||||
public Mono<Void> processPayment(String orderId) {
|
||||
return Mono.just(orderId)
|
||||
.doOnSuccess(id -> {
|
||||
stateMachine.sendEvent(OrderEvents.PAY);
|
||||
})
|
||||
.then();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"methodFqn": "ExternalTaxService.calculateTax",
|
||||
"event": "FINALIZE"
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user