extended analysis

This commit is contained in:
2026-06-14 16:32:51 +02:00
parent 2251a587d9
commit ab474c0766
125 changed files with 4180 additions and 317 deletions

View 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'
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,6 @@
package click.kamil.examples.enterprise.service;
public interface OrderService {
void place();
void cancel(String id);
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -0,0 +1,6 @@
[
{
"methodFqn": "ExternalTaxService.calculateTax",
"event": "FINALIZE"
}
]