From b54ca0c2dd40460d8a5a2c36dec545cbf719db2d Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Thu, 19 Feb 2026 18:28:08 +0000
Subject: [PATCH 1/3] Update dependency
org.springframework.boot:spring-boot-starter-parent to v4
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 0a98890..d40b337 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,7 +17,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.5.11
+ 4.0.3
From b96aafa7b8c053a0922f2c217b91f107e435490f Mon Sep 17 00:00:00 2001
From: piomin
Date: Sat, 7 Mar 2026 09:42:23 +0100
Subject: [PATCH 2/3] manual fixes
---
.../services/account/AccountApplication.java | 16 +++---------
.../account/controller/AccountController.java | 25 ++++++-------------
.../account/service/AccountService.java | 6 ++---
.../services/account/OrderReceiverTest.java | 7 +++---
order-service/pom.xml | 10 ++++++++
.../services/order/OrderControllerTest.java | 4 ++-
pom.xml | 2 +-
.../services/product/ProductApplication.java | 11 +++-----
.../product/controller/ProductController.java | 6 ++---
.../product/service/ProductService.java | 6 ++---
.../services/product/OrderReceiverTest.java | 5 ++--
11 files changed, 39 insertions(+), 59 deletions(-)
diff --git a/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java b/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
index 5b9b8c6..1eea994 100644
--- a/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
+++ b/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
@@ -1,7 +1,5 @@
package pl.piomin.services.account;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -10,6 +8,7 @@
import org.springframework.context.annotation.Bean;
import pl.piomin.services.account.service.AccountService;
import pl.piomin.services.messaging.Order;
+import tools.jackson.databind.ObjectMapper;
import java.util.function.Consumer;
@@ -30,18 +29,9 @@ public static void main(String[] args) {
@Bean
public Consumer input() {
return order -> {
- try {
- LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
- service.process(order);
- } catch (JsonProcessingException e) {
- LOGGER.error("Error deserializing", e);
- }
+ LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
+ service.process(order);
};
}
-
-// @Bean
-// public Sampler defaultSampler() {
-// return new AlwaysSampler();
-// }
}
diff --git a/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java b/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
index 301ca9b..1fffe1c 100644
--- a/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
+++ b/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
@@ -1,24 +1,15 @@
package pl.piomin.services.account.controller;
-import java.util.Collections;
-import java.util.List;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.DeleteMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.PutMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RestController;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
+import org.springframework.web.bind.annotation.*;
import pl.piomin.services.account.model.Account;
import pl.piomin.services.account.repository.AccountRepository;
+import tools.jackson.databind.ObjectMapper;
+
+import java.util.Collections;
+import java.util.List;
@RestController
public class AccountController {
@@ -41,7 +32,7 @@ public Account update(@RequestBody Account account) {
}
@PutMapping("/withdraw/{id}/{amount}")
- public Account withdraw(@PathVariable("id") Long id, @PathVariable("amount") int amount) throws JsonProcessingException {
+ public Account withdraw(@PathVariable("id") Long id, @PathVariable int amount) {
Account account = repository.findById(id);
LOGGER.info("Account found: {}", mapper.writeValueAsString(account));
account.setBalance(account.getBalance() - amount);
@@ -50,12 +41,12 @@ public Account withdraw(@PathVariable("id") Long id, @PathVariable("amount") int
}
@GetMapping("/{id}")
- public Account findById(@PathVariable("id") Long id) {
+ public Account findById(@PathVariable Long id) {
return repository.findById(id);
}
@GetMapping("/customer/{customerId}")
- public List findByCustomerId(@PathVariable("customerId") Long customerId) {
+ public List findByCustomerId(@PathVariable Long customerId) {
return repository.findByCustomer(customerId);
}
diff --git a/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java b/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
index 52bb0e6..708f118 100644
--- a/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
+++ b/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
@@ -7,14 +7,12 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import pl.piomin.services.account.messaging.OrderSender;
import pl.piomin.services.account.model.Account;
import pl.piomin.services.account.repository.AccountRepository;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
+import tools.jackson.databind.ObjectMapper;
@Service
public class AccountService {
@@ -28,7 +26,7 @@ public class AccountService {
@Autowired
OrderSender orderSender;
- public void process(final Order order) throws JsonProcessingException {
+ public void process(final Order order) {
LOGGER.info("Order processed: {}", mapper.writeValueAsString(order));
List accounts = accountRepository.findByCustomer(order.getCustomerId());
Account account = accounts.get(0);
diff --git a/account-service/src/test/java/pl/piomin/services/account/OrderReceiverTest.java b/account-service/src/test/java/pl/piomin/services/account/OrderReceiverTest.java
index 9ffa6bf..06b6caf 100644
--- a/account-service/src/test/java/pl/piomin/services/account/OrderReceiverTest.java
+++ b/account-service/src/test/java/pl/piomin/services/account/OrderReceiverTest.java
@@ -1,7 +1,5 @@
package pl.piomin.services.account;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,6 +12,7 @@
import org.springframework.messaging.Message;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
+import tools.jackson.databind.ObjectMapper;
import java.util.Collections;
@@ -34,7 +33,7 @@ public class OrderReceiverTest {
private ObjectMapper mapper;
@Test
- public void testAccepted() throws JsonProcessingException {
+ public void testAccepted() {
Order o = new Order();
o.setId(1L);
o.setAccountId(1L);
@@ -52,7 +51,7 @@ public void testAccepted() throws JsonProcessingException {
}
@Test
- public void testRejected() throws JsonProcessingException {
+ public void testRejected() {
Order o = new Order();
o.setId(1L);
o.setAccountId(1L);
diff --git a/order-service/pom.xml b/order-service/pom.xml
index e4c497b..b6f8af7 100644
--- a/order-service/pom.xml
+++ b/order-service/pom.xml
@@ -72,6 +72,16 @@
junit-jupiter
test
+
+ org.springframework.boot
+ spring-boot-resttestclient
+ test
+
+
+ org.springframework.boot
+ spring-boot-restclient
+ test
+
diff --git a/order-service/src/test/java/pl/piomin/services/order/OrderControllerTest.java b/order-service/src/test/java/pl/piomin/services/order/OrderControllerTest.java
index edb616d..d405538 100644
--- a/order-service/src/test/java/pl/piomin/services/order/OrderControllerTest.java
+++ b/order-service/src/test/java/pl/piomin/services/order/OrderControllerTest.java
@@ -2,8 +2,9 @@
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.resttestclient.TestRestTemplate;
+import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.testcontainers.containers.RabbitMQContainer;
import org.testcontainers.junit.jupiter.Container;
@@ -19,6 +20,7 @@
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Testcontainers
+@AutoConfigureTestRestTemplate
public class OrderControllerTest {
@Autowired
diff --git a/pom.xml b/pom.xml
index d40b337..afb57ad 100644
--- a/pom.xml
+++ b/pom.xml
@@ -26,7 +26,7 @@
org.springframework.cloud
spring-cloud-dependencies
- 2025.0.0
+ 2025.1.1
pom
import
diff --git a/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java b/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
index 62f28b3..9ba15ec 100644
--- a/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
+++ b/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
@@ -1,7 +1,5 @@
package pl.piomin.services.product;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -11,6 +9,7 @@
import org.springframework.web.filter.CommonsRequestLoggingFilter;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.product.service.ProductService;
+import tools.jackson.databind.ObjectMapper;
import java.util.function.Consumer;
@@ -31,12 +30,8 @@ public static void main(String[] args) {
@Bean
public Consumer input() {
return order -> {
- try {
- LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
- service.process(order);
- } catch (JsonProcessingException e) {
- LOGGER.error("Error deserializing", e);
- }
+ LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
+ service.process(order);
};
}
diff --git a/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java b/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
index dba41a8..0655efe 100644
--- a/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
+++ b/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
@@ -14,11 +14,9 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import pl.piomin.services.product.model.Product;
import pl.piomin.services.product.repository.ProductRepository;
+import tools.jackson.databind.ObjectMapper;
@RestController
public class ProductController {
@@ -46,7 +44,7 @@ public Product findById(@PathVariable("id") Long id) {
}
@PostMapping("/ids")
- public List find(@RequestBody List ids) throws JsonProcessingException {
+ public List find(@RequestBody List ids) {
List products = repository.find(ids);
LOGGER.info("Products found: {}", mapper.writeValueAsString(Collections.singletonMap("count", products.size())));
return products;
diff --git a/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java b/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
index 629d571..7ac1a84 100644
--- a/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
+++ b/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
@@ -7,14 +7,12 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
import pl.piomin.services.product.messaging.OrderSender;
import pl.piomin.services.product.model.Product;
import pl.piomin.services.product.repository.ProductRepository;
+import tools.jackson.databind.ObjectMapper;
@Service
public class ProductService {
@@ -28,7 +26,7 @@ public class ProductService {
@Autowired
OrderSender orderSender;
- public void process(final Order order) throws JsonProcessingException {
+ public void process(final Order order) {
LOGGER.info("Order processed: {}", mapper.writeValueAsString(order));
for (Long productId : order.getProductIds()) {
Product product = productRepository.findById(productId);
diff --git a/product-service/src/test/java/pl/piomin/services/product/OrderReceiverTest.java b/product-service/src/test/java/pl/piomin/services/product/OrderReceiverTest.java
index 96ba257..ebfee9b 100644
--- a/product-service/src/test/java/pl/piomin/services/product/OrderReceiverTest.java
+++ b/product-service/src/test/java/pl/piomin/services/product/OrderReceiverTest.java
@@ -1,7 +1,5 @@
package pl.piomin.services.product;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -14,6 +12,7 @@
import org.springframework.messaging.Message;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
+import tools.jackson.databind.ObjectMapper;
import java.util.Collections;
@@ -34,7 +33,7 @@ public class OrderReceiverTest {
private ObjectMapper mapper;
@Test
- public void testProcessing() throws JsonProcessingException {
+ public void testProcessing() {
Order o = new Order();
o.setId(1L);
o.setAccountId(1L);
From 8d35afa22b2c917ac2712fb0ad76e881e601646d Mon Sep 17 00:00:00 2001
From: piomin
Date: Sat, 7 Mar 2026 09:49:03 +0100
Subject: [PATCH 3/3] agent fixes
---
.../services/account/AccountApplication.java | 8 +-
.../account/controller/AccountController.java | 8 +-
.../account/messaging/OrderSender.java | 8 +-
.../account/service/AccountService.java | 12 +-
.../services/order/OrderApplication.java | 19 +-
.../order/controller/OrderController.java | 17 +-
.../services/order/messaging/OrderSender.java | 8 +-
.../services/order/service/OrderService.java | 13 +-
.../services/product/ProductApplication.java | 10 +-
.../product/controller/ProductController.java | 8 +-
.../product/messaging/OrderSender.java | 8 +-
.../product/service/ProductService.java | 12 +-
readme.md | 287 +++++++++++++++++-
13 files changed, 351 insertions(+), 67 deletions(-)
diff --git a/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java b/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
index 1eea994..819c5e8 100644
--- a/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
+++ b/account-service/src/main/java/pl/piomin/services/account/AccountApplication.java
@@ -2,7 +2,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@@ -19,8 +18,11 @@ public class AccountApplication {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- AccountService service;
+ private final AccountService service;
+
+ public AccountApplication(AccountService service) {
+ this.service = service;
+ }
public static void main(String[] args) {
SpringApplication.run(AccountApplication.class, args);
diff --git a/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java b/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
index 1fffe1c..b7b1108 100644
--- a/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
+++ b/account-service/src/main/java/pl/piomin/services/account/controller/AccountController.java
@@ -2,7 +2,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import pl.piomin.services.account.model.Account;
import pl.piomin.services.account.repository.AccountRepository;
@@ -18,8 +17,11 @@ public class AccountController {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- AccountRepository repository;
+ private final AccountRepository repository;
+
+ public AccountController(AccountRepository repository) {
+ this.repository = repository;
+ }
@PostMapping
public Account add(@RequestBody Account account) {
diff --git a/account-service/src/main/java/pl/piomin/services/account/messaging/OrderSender.java b/account-service/src/main/java/pl/piomin/services/account/messaging/OrderSender.java
index 1a16588..05b9edf 100644
--- a/account-service/src/main/java/pl/piomin/services/account/messaging/OrderSender.java
+++ b/account-service/src/main/java/pl/piomin/services/account/messaging/OrderSender.java
@@ -1,6 +1,5 @@
package pl.piomin.services.account.messaging;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.stereotype.Service;
@@ -9,8 +8,11 @@
@Service
public class OrderSender {
- @Autowired
- private StreamBridge source;
+ private final StreamBridge source;
+
+ public OrderSender(StreamBridge source) {
+ this.source = source;
+ }
public boolean send(Order order) {
return this.source.send("output", MessageBuilder.withPayload(order).build());
diff --git a/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java b/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
index 708f118..fcc3218 100644
--- a/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
+++ b/account-service/src/main/java/pl/piomin/services/account/service/AccountService.java
@@ -4,7 +4,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pl.piomin.services.account.messaging.OrderSender;
@@ -21,10 +20,13 @@ public class AccountService {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- AccountRepository accountRepository;
- @Autowired
- OrderSender orderSender;
+ private final AccountRepository accountRepository;
+ private final OrderSender orderSender;
+
+ public AccountService(AccountRepository accountRepository, OrderSender orderSender) {
+ this.accountRepository = accountRepository;
+ this.orderSender = orderSender;
+ }
public void process(final Order order) {
LOGGER.info("Order processed: {}", mapper.writeValueAsString(order));
diff --git a/order-service/src/main/java/pl/piomin/services/order/OrderApplication.java b/order-service/src/main/java/pl/piomin/services/order/OrderApplication.java
index 8ba407e..f7401e4 100644
--- a/order-service/src/main/java/pl/piomin/services/order/OrderApplication.java
+++ b/order-service/src/main/java/pl/piomin/services/order/OrderApplication.java
@@ -1,10 +1,8 @@
package pl.piomin.services.order;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@@ -20,8 +18,11 @@ public class OrderApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(OrderApplication.class);
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- OrderService service;
+ private final OrderService service;
+
+ public OrderApplication(OrderService service) {
+ this.service = service;
+ }
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
@@ -30,12 +31,8 @@ public static void main(String[] args) {
@Bean
public Consumer input() {
return order -> {
- try {
- LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
- service.process(order);
- } catch (JsonProcessingException e) {
- LOGGER.error("Error deserializing", e);
- }
+ LOGGER.info("Order received: {}", mapper.writeValueAsString(order));
+ service.process(order);
};
}
diff --git a/order-service/src/main/java/pl/piomin/services/order/controller/OrderController.java b/order-service/src/main/java/pl/piomin/services/order/controller/OrderController.java
index 8f6d44f..ba39fe2 100644
--- a/order-service/src/main/java/pl/piomin/services/order/controller/OrderController.java
+++ b/order-service/src/main/java/pl/piomin/services/order/controller/OrderController.java
@@ -4,15 +4,13 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
@@ -26,13 +24,16 @@ public class OrderController {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- OrderRepository repository;
- @Autowired
- OrderSender sender;
+ private final OrderRepository repository;
+ private final OrderSender sender;
+
+ public OrderController(OrderRepository repository, OrderSender sender) {
+ this.repository = repository;
+ this.sender = sender;
+ }
@PostMapping("/")
- public Order process(@RequestBody Order order) throws JsonProcessingException {
+ public Order process(@RequestBody Order order) {
Order o = repository.add(order);
LOGGER.info("Order saved: {}", mapper.writeValueAsString(order));
boolean isSent = sender.send(o);
diff --git a/order-service/src/main/java/pl/piomin/services/order/messaging/OrderSender.java b/order-service/src/main/java/pl/piomin/services/order/messaging/OrderSender.java
index 0f6466c..15172c9 100644
--- a/order-service/src/main/java/pl/piomin/services/order/messaging/OrderSender.java
+++ b/order-service/src/main/java/pl/piomin/services/order/messaging/OrderSender.java
@@ -1,6 +1,5 @@
package pl.piomin.services.order.messaging;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.stereotype.Service;
@@ -9,8 +8,11 @@
@Service
public class OrderSender {
- @Autowired
- private StreamBridge source;
+ private final StreamBridge source;
+
+ public OrderSender(StreamBridge source) {
+ this.source = source;
+ }
public boolean send(Order order) {
return this.source.send("output", MessageBuilder.withPayload(order).build());
diff --git a/order-service/src/main/java/pl/piomin/services/order/service/OrderService.java b/order-service/src/main/java/pl/piomin/services/order/service/OrderService.java
index 4962f5a..df7d57e 100644
--- a/order-service/src/main/java/pl/piomin/services/order/service/OrderService.java
+++ b/order-service/src/main/java/pl/piomin/services/order/service/OrderService.java
@@ -2,11 +2,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import tools.jackson.databind.ObjectMapper;
import pl.piomin.services.messaging.Order;
import pl.piomin.services.messaging.OrderStatus;
@@ -19,10 +17,13 @@ public class OrderService {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- OrderRepository repository;
+ private final OrderRepository repository;
+
+ public OrderService(OrderRepository repository) {
+ this.repository = repository;
+ }
- public void process(final Order order) throws JsonProcessingException {
+ public void process(final Order order) {
LOGGER.info("Order processed: {}", mapper.writeValueAsString(order));
Order o = repository.findById(order.getId());
if (o.getStatus() != OrderStatus.REJECTED) {
diff --git a/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java b/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
index 9ba15ec..e0f6242 100644
--- a/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
+++ b/product-service/src/main/java/pl/piomin/services/product/ProductApplication.java
@@ -2,7 +2,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@@ -20,9 +19,12 @@ public class ProductApplication {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- ProductService service;
-
+ private final ProductService service;
+
+ public ProductApplication(ProductService service) {
+ this.service = service;
+ }
+
public static void main(String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
diff --git a/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java b/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
index 0655efe..60ce978 100644
--- a/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
+++ b/product-service/src/main/java/pl/piomin/services/product/controller/ProductController.java
@@ -5,7 +5,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -25,8 +24,11 @@ public class ProductController {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- ProductRepository repository;
+ private final ProductRepository repository;
+
+ public ProductController(ProductRepository repository) {
+ this.repository = repository;
+ }
@PostMapping
public Product add(@RequestBody Product product) {
diff --git a/product-service/src/main/java/pl/piomin/services/product/messaging/OrderSender.java b/product-service/src/main/java/pl/piomin/services/product/messaging/OrderSender.java
index e666530..ce04c09 100644
--- a/product-service/src/main/java/pl/piomin/services/product/messaging/OrderSender.java
+++ b/product-service/src/main/java/pl/piomin/services/product/messaging/OrderSender.java
@@ -1,6 +1,5 @@
package pl.piomin.services.product.messaging;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.stereotype.Service;
@@ -9,8 +8,11 @@
@Service
public class OrderSender {
- @Autowired
- private StreamBridge source;
+ private final StreamBridge source;
+
+ public OrderSender(StreamBridge source) {
+ this.source = source;
+ }
public boolean send(Order order) {
return this.source.send("output", MessageBuilder.withPayload(order).build());
diff --git a/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java b/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
index 7ac1a84..5541da9 100644
--- a/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
+++ b/product-service/src/main/java/pl/piomin/services/product/service/ProductService.java
@@ -4,7 +4,6 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import pl.piomin.services.messaging.Order;
@@ -21,10 +20,13 @@ public class ProductService {
private ObjectMapper mapper = new ObjectMapper();
- @Autowired
- ProductRepository productRepository;
- @Autowired
- OrderSender orderSender;
+ private final ProductRepository productRepository;
+ private final OrderSender orderSender;
+
+ public ProductService(ProductRepository productRepository, OrderSender orderSender) {
+ this.productRepository = productRepository;
+ this.orderSender = orderSender;
+ }
public void process(final Order order) {
LOGGER.info("Order processed: {}", mapper.writeValueAsString(order));
diff --git a/readme.md b/readme.md
index 7dcbc73..bc19685 100644
--- a/readme.md
+++ b/readme.md
@@ -1,10 +1,277 @@
-## Building and testing message-driven microservices using Spring Cloud Stream [](https://twitter.com/piotr_minkowski)
-
-[](https://circleci.com/gh/piomin/sample-message-driven-microservices)
-
-[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
-[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
-[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
-[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
-
-Detailed description can be found here: [Building and testing message-driven microservices using Spring Cloud Stream](https://piotrminkowski.com/2018/06/15/building-and-testing-message-driven-microservices-using-spring-cloud-stream/)
+## Building and testing message-driven microservices using Spring Cloud Stream [](https://twitter.com/piotr_minkowski)
+
+[](https://circleci.com/gh/piomin/sample-message-driven-microservices)
+
+[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
+[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
+[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
+[](https://sonarcloud.io/dashboard?id=piomin_sample-message-driven-microservices)
+
+Detailed description can be found here: [Building and testing message-driven microservices using Spring Cloud Stream](https://piotrminkowski.com/2018/06/15/building-and-testing-message-driven-microservices-using-spring-cloud-stream/)
+
+
+## Table of Contents
+- [Architecture Overview](#architecture-overview)
+- [Technology Stack](#technology-stack)
+- [Services Description](#services-description)
+- [Message Flow](#message-flow)
+- [Prerequisites](#prerequisites)
+- [Running the Applications](#running-the-applications)
+- [API Endpoints](#api-endpoints)
+- [Testing](#testing)
+- [Configuration](#configuration)
+- [Monitoring and Management](#monitoring-and-management)
+
+## Architecture Overview
+
+This project demonstrates a message-driven microservices architecture using Spring Cloud Stream with RabbitMQ as the message broker. The system consists of three core business services that communicate asynchronously through message queues.
+
+```mermaid
+graph TB
+ subgraph "Client Layer"
+ C["Client/API Consumer"]
+ end
+ subgraph "Microservices"
+ OS["Order Service
:8090"]
+ AS["Account Service
:8091"]
+ PS["Product Service
:8093"]
+ end
+ subgraph "Message Broker"
+ RMQ["RabbitMQ
:5672"]
+ OIN["orders-in queue"]
+ OOUT["orders-out queue"]
+ end
+ subgraph "Common"
+ MC["messaging-common
Shared DTOs"]
+ end
+ C --> OS
+ C --> AS
+ C --> PS
+ OS --> OIN
+ OIN --> AS
+ OIN --> PS
+ AS --> OOUT
+ PS --> OOUT
+ OOUT --> OS
+ OS -.-> MC
+ AS -.-> MC
+ PS -.-> MC
+```
+
+## Technology Stack
+
+- **Java**: 21
+- **Spring Boot**: 3.5.0
+- **Spring Cloud**: 2025.0.0
+- **Spring Cloud Stream**: Message-driven microservices framework
+- **RabbitMQ**: Message broker for asynchronous communication
+- **Maven**: Build and dependency management
+- **Docker & Docker Compose**: Containerization and orchestration
+- **Testcontainers**: Integration testing with real dependencies
+- **CircleCI**: Continuous Integration
+- **SonarCloud**: Code quality and security analysis
+- **JaCoCo**: Code coverage analysis
+
+## Services Description
+
+### Order Service (port 8090)
+The central orchestrator service that manages customer orders. It receives order requests via REST API and publishes order events.
+
+**Key Features:**
+- REST API for order management
+- Order event publishing with customer-based partitioning
+- Order status tracking and updates
+
+### Account Service (port 8091)
+Manages customer accounts and financial transactions. It listens for order events and processes withdrawals.
+
+**Key Features:**
+- Account management REST API
+- Partitioned message consumption
+- Account balance withdrawals
+
+### Product Service (port 8093)
+Handles product catalog and inventory. It processes order events to update availability.
+
+**Key Features:**
+- Product management REST API
+- Partitioned message consumption
+- Inventory tracking
+
+### Messaging Common
+Shared library with common DTOs and enums for consistent message contracts.
+
+**Components:**
+- `Order` – Order data model
+- `OrderStatus` – Enumeration (NEW, PROCESSING, ACCEPTED, DONE, REJECTED)
+
+## Message Flow
+
+The services communicate through two main message destinations:
+
+1. **orders-in**: Order Service publishes new orders
+2. **orders-out**: Account and Product Services publish processing results
+
+**Message Flow Pattern:**
+```
+1. Client creates order → Order Service
+2. Order Service publishes order → orders-in queue
+3. Account Service processes withdrawal → orders-out queue
+4. Product Service updates inventory → orders-out queue
+5. Order Service receives updates → Final order status
+```
+
+**Partitioning Strategy:**
+- Partitioned by `customerId` for ordered processing
+- Supports multiple instances per service with load balancing
+
+## Prerequisites
+
+- **Java 21** or higher
+- **Maven 3.6+**
+- **Docker & Docker Compose**
+- **Git**
+
+## Running the Applications
+
+### 1. Clone the Repository
+```bash
+git clone https://github.com/piomin/sample-message-driven-microservices.git
+cd sample-message-driven-microservices
+```
+
+### 2. Start RabbitMQ
+```bash
+docker-compose up -d
+```
+- RabbitMQ AMQP on 5672
+- Management UI on http://localhost:15672 (guest/guest)
+
+### 3. Build the Project
+```bash
+mvn clean compile
+```
+
+### 4. Start the Services
+**Option A: Separate terminals**
+```bash
+cd order-service && mvn spring-boot:run
+cd account-service && mvn spring-boot:run
+cd product-service && mvn spring-boot:run
+```
+**Option B: Background**
+```bash
+cd order-service && mvn spring-boot:run &
+cd account-service && mvn spring-boot:run &
+cd product-service && mvn spring-boot:run &
+```
+
+### 5. Verify Health
+```bash
+curl http://localhost:8090/actuator/health
+curl http://localhost:8091/actuator/health
+curl http://localhost:8093/actuator/health
+```
+
+### Multiple Instances (Partitioned)
+```bash
+SPRING_PROFILES_ACTIVE=instance1 mvn spring-boot:run -pl account-service
+SPRING_PROFILES_ACTIVE=instance2 mvn spring-boot:run -pl account-service
+
+SPRING_PROFILES_ACTIVE=instance1 mvn spring-boot:run -pl product-service
+SPRING_PROFILES_ACTIVE=instance2 mvn spring-boot:run -pl product-service
+```
+
+## API Endpoints
+
+### Order Service (http://localhost:8090)
+| Method | Endpoint | Description |
+|--------|---------------------------------|----------------------------|
+| POST | `/orders` | Create a new order |
+| PUT | `/orders` | Update an order |
+| GET | `/orders/{id}` | Get order by ID |
+| GET | `/orders/customer/{customerId}` | Get orders by customer ID |
+
+### Account Service (http://localhost:8091)
+| Method | Endpoint | Description |
+|--------|------------------------------------------|-------------------------------|
+| POST | `/accounts` | Create a new account |
+| PUT | `/accounts` | Update an account |
+| PUT | `/accounts/withdraw/{id}/{amount}` | Withdraw amount from account |
+| GET | `/accounts/{id}` | Get account by ID |
+| GET | `/accounts/customer/{customerId}` | Get accounts by customer ID |
+
+### Product Service (http://localhost:8093)
+| Method | Endpoint | Description |
+|--------|----------------------|-------------------------|
+| POST | `/products` | Create a new product |
+| PUT | `/products` | Update a product |
+| GET | `/products/{id}` | Get product by ID |
+| GET | `/products/ids` | Get products by IDs |
+
+### Example API Usage
+```bash
+curl -X POST http://localhost:8091/accounts \
+ -H "Content-Type: application/json" \
+ -d '{"customerId":1,"balance":1000}'
+
+curl -X POST http://localhost:8093/products \
+ -H "Content-Type: application/json" \
+ -d '{"name":"Sample","price":50,"count":100}'
+
+curl -X POST http://localhost:8090/orders \
+ -H "Content-Type: application/json" \
+ -d '{"customerId":1,"productIds":[1],"price":50}'
+```
+
+## Testing
+
+### Running Tests
+```bash
+mvn test
+mvn test -pl order-service
+mvn test -pl account-service
+mvn test -pl product-service
+```
+
+### Test Structure
+- `OrderControllerTest` – Order Service REST endpoints
+- `OrderReceiverTest` – Account Service message handling
+- `OrderReceiverTest` – Product Service message handling
+
+### Code Coverage
+```bash
+mvn jacoco:report
+```
+Coverage reports in `target/site/jacoco/index.html` per module.
+
+## Configuration
+
+### Environment Variables
+| Variable | Default | Description |
+|--------------------------|--------------------|------------------------------------|
+| `PORT` | service-specific | Override default service port |
+| `SPRING_PROFILES_ACTIVE` | | Activate specific Spring profiles |
+
+### RabbitMQ Configuration
+- Host: localhost
+- Port: 5672
+- Username: guest
+- Password: guest
+
+### Service Discovery
+Eureka available at http://localhost:8761/eureka/ (optional)
+
+### Message Destinations
+- **orders-in**: Direct exchange for new orders
+- **orders-out**: Topic exchange for processing results
+- **Partitioning**: 2 partitions by `customerId`
+
+## Monitoring and Management
+
+All services expose Actuator endpoints:
+- Health: `/actuator/health`
+- Metrics: `/actuator/metrics`
+- Stream bindings: `/actuator/bindings`
+
+**RabbitMQ Management UI:** http://localhost:15672 (guest/guest)