0% found this document useful (0 votes)
2 views38 pages

Advanced Spring

The document outlines the structure and components of three microservices: auth-service, product-service, and order-service. Each service includes entities, repositories, services, controllers, and exception handling, with specific functionalities such as user authentication, product management, and order processing. The auth-service handles user registration and login, the product-service manages product details, and the order-service facilitates order creation and retrieval using both RestTemplate and WebClient.

Uploaded by

gloryrp58
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views38 pages

Advanced Spring

The document outlines the structure and components of three microservices: auth-service, product-service, and order-service. Each service includes entities, repositories, services, controllers, and exception handling, with specific functionalities such as user authentication, product management, and order processing. The auth-service handles user registration and login, the product-service manages product details, and the order-service facilitates order creation and retrieval using both RestTemplate and WebClient.

Uploaded by

gloryrp58
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Start with auth-service

[Link]
a. [Link]
package [Link];
public record AuthRequest(
String username,
String password
) {}
b. [Link]
package [Link];
public record AuthResponse(
String token,
String type
) {}

[Link]
a. [Link]
package [Link];
import [Link].*;

@Entity
@Table(name = "users")
public class AppUser {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
@Column(unique = true)
private String username;
private String password;
private String role = "ROLE_USER";
public Long getId() {
return id;
}
public void setId(Long id) {
[Link] = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
[Link] = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
[Link] = password;
}
public String getRole() {
return role;
}
public void setRole(String role) {
[Link] = role;
}
}

3. repository
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Repository
public interface UserRepository extends JpaRepository<AppUser, Long> {
Optional<AppUser> findByUsername(String username);
boolean existsByUsername(String username);
}

4. security
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Service
public class JwtService {
@Value("${[Link]}")
private String secret;
private Key getSigningKey() {
return [Link]([Link]());
}
public String generateToken(String username) {
return [Link]()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date([Link]() + 86400000))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String extractUsername(String token) {
return extractAllClaims(token).getSubject();
}
public boolean validateToken(String token, String username) {
return [Link](extractUsername(token))
&& !extractAllClaims(token).getExpiration().before(new Date());
}
private Claims extractAllClaims(String token) {
return [Link]()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
}

5. service
a. [Link]
package [Link];
import
[Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Service
public class AuthService {
private final UserRepository userRepository;
private final JwtService jwtService;
private final BCryptPasswordEncoder passwordEncoder = new
BCryptPasswordEncoder();
public AuthService(UserRepository userRepository, JwtService
jwtService) {
[Link] = userRepository;
[Link] = jwtService;
}
public String register(AuthRequest request) {
if ([Link]([Link]())) {
throw new RuntimeException("Username already exists");
}
AppUser user = new AppUser();
[Link]([Link]());
[Link]([Link]([Link]()));
[Link](user);
return "User Registered Successfully";
}
public AuthResponse login(AuthRequest request) {
AppUser user = [Link]([Link]())
.orElseThrow(() -> new RuntimeException("Invalid Username"));
if (![Link]([Link](),
[Link]())) {
throw new RuntimeException("Invalid Password");
}
String token = [Link]([Link]());
return new AuthResponse(token, "Bearer");
}
}

6. controller
a. [Link]
package [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
import [Link];

@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
public AuthController(AuthService authService) {
[Link] = authService;
}
@PostMapping("/register")
public ResponseEntity<String> register(@RequestBody AuthRequest
request) {
return [Link]([Link](request));
}
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody
AuthRequest request) {
return [Link]([Link](request));
}
}

7. exception
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<String>
handleRuntimeException(RuntimeException ex) {
return new ResponseEntity<>([Link](),
HttpStatus.BAD_REQUEST);
}
}

8. [Link]
package [Link];
import [Link];
import [Link];
import
[Link];
@SpringBootApplication
@EnableDiscoveryClient
public class AuthServiceApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
Next product-service

[Link]
a. [Link](only focus on annotations)
package [Link];
import [Link].*;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private String name;
private Double price;
private Integer stock;
public Long getId() {
return id;
}
public void setId(Long id) {
[Link] = id;
}
public String getName() {
return name;
}
public void setName(String name) {
[Link] = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
[Link] = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
[Link] = stock;
}
}

2. repository
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>
{
}

3. service
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
[Link] = repository;
}
public Product save(Product product) {
return [Link](product);
}
public List<Product> findAll() {
return [Link]();
}
public Product findById(Long id) {
return [Link](id)
.orElseThrow(() -> new RuntimeException("Product not found"));
}
public Product updateStock(Long id, Integer stock) {
Product product = findById(id);
[Link](stock);
return [Link](product);
}
}

4. controller
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
@RestController
@RequestMapping("/products")
public class ProductController {

private final ProductService service;


public ProductController(ProductService service) {
[Link] = service;
}
@PostMapping
public ResponseEntity<Product> addProduct(@RequestBody Product
product) {
return [Link]([Link](product));
}
@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
return [Link]([Link]());
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
return [Link]([Link](id));
}
@PutMapping("/{id}/stock/{stock}")
public ResponseEntity<Product> updateStock(@PathVariable Long id,
@PathVariable Integer stock) {
return [Link]([Link](id, stock));
}
}

5. exception
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<String> handle(RuntimeException ex) {
return new ResponseEntity<>([Link](),
HttpStatus.BAD_REQUEST);
}
}

[Link]
package [Link];

import [Link];
import [Link];
import
[Link];

@SpringBootApplication
@EnableDiscoveryClient
public class ProductServiceApplication {

public static void main(String[] args) {


[Link]([Link], args);
}
}

Next order-service
1. config
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
b. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Configuration
public class WebClientConfig {
@Bean
@LoadBalanced
public [Link] webClientBuilder() {
return [Link]();
}
}

2. [Link]
package [Link];
import [Link];
import [Link];
import
[Link];
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}

3. service
a. [Link]
package [Link];

import [Link];

import [Link];
import [Link];
import [Link];

import [Link];
import [Link];
import [Link];
import [Link];

@Service
public class OrderService {
private final OrderRepository repository;
private final RestTemplate restTemplate;
private final [Link] webClientBuilder;

private static final String PRODUCT_SERVICE = "[Link]


SERVICE/products";

public OrderService(OrderRepository repository,


RestTemplate restTemplate,
[Link] webClientBuilder) {
[Link] = repository;
[Link] = restTemplate;
[Link] = webClientBuilder;
}

// REST TEMPLATE
public CustomerOrder
createOrderUsingRestTemplate(CreateOrderRequest request) {

ProductDto product;

try {
product = [Link](
PRODUCT_SERVICE + "/" + [Link](),
[Link]);

} catch (Exception ex) {


throw new RuntimeException("Product Service unavailable");
}

if (product == null) {
throw new RuntimeException("Invalid Product ID");
}

if ([Link]() < [Link]()) {


throw new RuntimeException("Insufficient Stock");
}

[Link](
PRODUCT_SERVICE + "/"
+ [Link]()
+ "/stock/"
+ ([Link]() - [Link]()));

CustomerOrder order = new CustomerOrder();

[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]() * [Link]());

return [Link](order);
}

// WEBCLIENT
public CustomerOrder
createOrderUsingWebClient(CreateOrderRequest request) {

ProductDto product;

try {
product = [Link]()
.get()
.uri(PRODUCT_SERVICE + "/" + [Link]())
.retrieve()
.bodyToMono([Link])
.block();

} catch (Exception ex) {


throw new RuntimeException("Product Service unavailable");
}

if (product == null) {
throw new RuntimeException("Invalid Product ID");
}

if ([Link]() < [Link]()) {


throw new RuntimeException("Insufficient Stock");
}
[Link]()
.put()
.uri(PRODUCT_SERVICE + "/"
+ [Link]()
+ "/stock/"
+ ([Link]() - [Link]()))
.retrieve()
.bodyToMono([Link])
.block();

CustomerOrder order = new CustomerOrder();

[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]());
[Link]([Link]() * [Link]());

return [Link](order);
}

public List<CustomerOrder> getAllOrders() {


return [Link]();
}
}
4. controller
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link].*;
import [Link];
import [Link];
import [Link];
@RestController
@RequestMapping("/orders")
public class OrderController {
private final OrderService service;
public OrderController(OrderService service) {
[Link] = service;
}
@PostMapping("/rest-template")
public ResponseEntity<CustomerOrder>
createOrderUsingRestTemplate(
@RequestBody CreateOrderRequest request) {

return [Link](
[Link](request));
}

@PostMapping("/webclient")
public ResponseEntity<CustomerOrder> createOrderUsingWebClient(
@RequestBody CreateOrderRequest request) {

return [Link](
[Link](request));
}

@GetMapping
public ResponseEntity<List<CustomerOrder>> getAllOrders() {
return [Link]([Link]());
}
}

5. exception
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler([Link])
public ResponseEntity<String>
handleRuntimeException(RuntimeException ex) {
return new ResponseEntity<>(
[Link](),
HttpStatus.BAD_REQUEST);
}
}

[Link]
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
@Entity
@Table(name = "orders")
public class CustomerOrder {
@Id
@GeneratedValue(strategy = [Link])
private Long id;
private Long productId;
private String productName;
private Double price;
private Integer quantity;
private Double totalPrice;
public CustomerOrder() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
[Link] = id;
}
public Long getProductId() {
return productId;
}
public void setProductId(Long productId) {
[Link] = productId;
}
public String getProductName() {
return productName;
}
public void setProductName(String productName) {
[Link] = productName;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
[Link] = price;
}
public Integer getQuantity() {
return quantity;
}
public void setQuantity(Integer quantity) {
[Link] = quantity;
}
public Double getTotalPrice() {
return totalPrice;
}
public void setTotalPrice(Double totalPrice) {
[Link] = totalPrice;
}
}

7. repository
a. [Link]
package [Link];
import [Link];
import [Link];
import [Link];
@Repository
public interface OrderRepository extends JpaRepository<CustomerOrder,
Long> {
}

[Link]
a. [Link]
package [Link];
public record CreateOrderRequest(
Long productId,
Integer quantity
){
}
b. [Link]
package [Link];
public class ProductDto {
private Long id;
private String name;
private Double price;
private Integer stock;
public ProductDto() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
[Link] = id;
}
public String getName() {
return name;
}
public void setName(String name) {
[Link] = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
[Link] = price;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
[Link] = stock;
}
}

Next Api Gateway


1. [Link]
package [Link];
import [Link];
import [Link];
import
[Link];

@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
[Link]([Link], args);
}
}
2. security
a. [Link]
package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Component
public class JwtUtil {
@Value("${[Link]}")
private String secret;
public Claims validateToken(String token) {
Key key =
[Link]([Link](StandardCharsets.UTF_8));
return [Link]()
.verifyWith(([Link]) key)
.build()
.parseSignedClaims(token)
.getPayload();
}
}

3. filter
a. [Link]
package [Link];

import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];
import [Link];

@Component
public class JwtAuthenticationFilter implements GlobalFilter, Ordered {
private final JwtUtil jwtUtil;
public JwtAuthenticationFilter(JwtUtil jwtUtil) {
[Link] = jwtUtil;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange,
GatewayFilterChain chain) {
String path = [Link]().getURI().getPath();
// Public APIs
if ([Link]("/auth")) {
return [Link](exchange);
}
String header =

[Link]().getHeaders().getFirst([Link]
N);
if (header == null || ![Link]("Bearer ")) {

[Link]().setStatusCode([Link]);
return [Link]().setComplete();
}
String token = [Link](7);
try {
Claims claims = [Link](token);
} catch (Exception e) {

[Link]().setStatusCode([Link]);
return [Link]().setComplete();
}
return [Link](exchange);
}
@Override
public int getOrder() {
return -1;
}
}

4. [Link]
server:
port: 8080
spring:
application:
name: API-GATEWAY
cloud:
gateway:
routes:
- id: auth-service
uri: lb://AUTH-SERVICE
predicates:
- Path=/auth/**
- id: product-service
uri: lb://PRODUCT-SERVICE
predicates:
- Path=/products/**

- id: order-service
uri: lb://ORDER-SERVICE
predicates:
- Path=/orders/**
eureka:
client:
service-url:
defaultZone: [Link]
jwt:
secret: my-super-secret-key-for-jwt-demo-change-in-production-
123456789

[Link]
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="[Link]
xmlns:xsi="[Link]
xsi:schemaLocation="[Link]
[Link]

<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.15</version>
<relativePath/>
</parent>

<groupId>[Link]</groupId>
<artifactId>api-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>api-gateway</name>

<properties>
<[Link]>17</[Link]>
<[Link]>2025.0.0</[Link]>
</properties>
<dependencies>

<!-- Spring Cloud Gateway -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-starter-gateway-server-webflux</artifactId>
</dependency>

<!-- Eureka Client -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

<!-- Load Balancer -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

<!-- Actuator -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<!-- Zipkin -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
</dependency>

<dependency>
<groupId>[Link].reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
</dependency>

<!-- JWT -->


<dependency>
<groupId>[Link]</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.7</version>
</dependency>

<dependency>
<groupId>[Link]</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.7</version>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>[Link]</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.7</version>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${[Link]}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<build>
<plugins>

<plugin>
<groupId>[Link]</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

Finally, [Link]
package [Link];
import [Link];
import [Link];
import
[Link];

@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServerApplication {

public static void main(String[] args) {


[Link]([Link], args);
}
}

[Link]
# E-Commerce System
## Overview
This project is a Spring Boot Microservices-based E-Commerce System
developed using Spring Cloud.
## Microservices
- Auth Service
- Product Service
- Order Service
- Eureka Discovery Server
- Spring Cloud API Gateway
## Technologies Used
- Java 17
- Spring Boot
- Spring Cloud
- Spring Data JPA
- Spring Security
- JWT Authentication
- Spring Cloud Gateway
- Eureka Discovery Server
- MySQL
- RestTemplate
- WebClient
- Spring Boot Actuator
- Zipkin

## Features
- User Registration and Login
- JWT-based Authentication
- Product Management
- Order Management
- Service Discovery using Eureka
- API Gateway Routing
- Load Balancing
- Distributed Tracing with Zipkin
- Application Monitoring using Spring Boot Actuator

## Microservices Ports

| Service | Port |
|---------|------|
| Discovery Server | 8761 |
| API Gateway | 8080 |
| Auth Service | 8081 |
| Product Service | 8082 |
| Order Service | 8083 |
## Running the Project
1. Start Discovery Server.
2. Start Auth Service.
3. Start Product Service.
4. Start Order Service.
5. Start API Gateway.
6. Access the services through the API Gateway.

You might also like