Set up a ProductService
microservice with Spring Boot. This microservice will handle product information and connect to its own MySQL database, following best practices in modularity and separation.
- Ensure the MicroservicesProject workspace is open in VS Code.
- Inside the workspace directory, create a new folder named
ProductService
.
- Open the Command Palette (View > Command Palette or
Ctrl+Shift+P
). - Select Spring Initializr: Generate a Maven Project.
- Configure the options as follows:
- Group Id:
com.microservices
- Artifact Id:
product-service
- Name:
ProductService
- Add dependencies: Spring Web, MySQL Driver, Spring Data JPA, MySQL Driver.
- Save the project in the
ProductService
folder.
- Group Id:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
- In
ProductService/src/main/resources
, openapplication.properties
. - Set up the following properties to configure the application’s port and MySQL database:
server.port=8082 spring.datasource.username=root spring.datasource.url=jdbc:mysql://localhost:3307/product_db spring.datasource.password=Test1234! spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
- In the
com.microservices.product_service
package, create a foldermodel
and a new class calledProduct.java
. - Define the
Product
entity:package com.microservices.product_service.model; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @Entity public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; private double price; // Getters and Setters public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } }
- In the same package, create a folder
repository
and a new interfaceProductRepository.java
:package com.microservices.product_service.repository; import org.springframework.data.jpa.repository.JpaRepository; import com.microservices.product_service.model.Product; public interface ProductRepository extends JpaRepository<Product, Long> { }
- This repository will handle database operations for the
Product
entity.
-
Create a folder
service
and a classProductService.java
:package com.microservices.product_service.service; import com.microservices.product_service.model.Product; import com.microservices.product_service.repository.ProductRepository; import org.springframework.stereotype.Service; import java.util.List; @Service public class ProductService { private final ProductRepository productRepository; public ProductService(ProductRepository productRepository) { this.productRepository = productRepository; } public List<Product> getAllProducts() { return productRepository.findAll(); } public Product createProduct(Product product) { return productRepository.save(product); } }
- Create a folder
controller
and a classProductController.java
:package com.microservices.product_service.controller; import com.microservices.product_service.model.Product; import com.microservices.product_service.service.ProductService; import org.springframework.web.bind.annotation.*; import java.util.List; @RestController @RequestMapping("/api/products") public class ProductController { private final ProductService productService; public ProductController(ProductService productService) { this.productService = productService; } @GetMapping public List<Product> getAllProducts() { return productService.getAllProducts(); } @PostMapping public Product createProduct(@RequestBody Product product) { return productService.createProduct(product); } }
- In the
ProductService
directory, create aDockerfile
:FROM openjdk:17 EXPOSE 8082 ADD target/product-service-0.0.1-SNAPSHOT.jar product-service.jar ENTRYPOINT ["java", "-jar", "product-service.jar"]
- Do a build
./mvnw clean package # Linux / Mac
- Build the Docker image for ProductService:
docker build -t product-service .
- In the MicroservicesProject directory, open
docker-compose.yml
. - Add the following configuration for ProductService:
version: '3.1' services: # MySQL for UserService mysql-user: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: Test1234! MYSQL_DATABASE: user_db ports: - "3306:3306" # MySQL for ProductService mysql-product: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: Test1234! MYSQL_DATABASE: product_db ports: - "3307:3306" # Expose port 3307 on host, but map to 3306 in the container # UserService user-service: build: ./UserService environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql-user:3306/user_db SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: Test1234! SPRING_JPA_HIBERNATE_DDL_AUTO: update ports: - "8081:8081" depends_on: - mysql-user # ProductService product-service: build: ./ProductService environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql-product:3306/product_db SPRING_DATASOURCE_USERNAME: root SPRING_DATASOURCE_PASSWORD: Test1234! SPRING_JPA_HIBERNATE_DDL_AUTO: update ports: - "8082:8082" depends_on: - mysql-product
- Start the MySQL container and ProductService together:
docker-compose up -d
- Verify that ProductService can connect to its MySQL database.
- Use Postman to test
GET
andPOST
endpoints with Docker running.
- Create a folder
exception
and a classProductNotFoundException.java
:
package com.microservices.product_service.exception;
public class ProductNotFoundException extends RuntimeException {
public ProductNotFoundException(String message) {
super(message);
}
}
- Add custom exception handling in
ProductController
for resource not found cases:@ExceptionHandler(ProductNotFoundException.class) public ResponseEntity<String> handleProductNotFound(ProductNotFoundException ex) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage()); }
- Emphasize the importance of keeping each service modular, with its own database and Docker configuration.
- Review what was covered in this lab:
- Created and configured ProductService.
- Dockerized ProductService and connected it to a separate MySQL database.
- Tested endpoints, exception handling, and confirmed modularity.
-
Additional Product Fields:
- Add fields such as
category
,stockQuantity
, andsupplier
to theProduct
entity. - Ensure they are included in the
ProductController
endpoints.
- Add fields such as
-
Filter Products by Category:
- Add a new endpoint in
ProductController
to filter products by category (e.g.,GET /api/products/category/{category}
). - Practice writing and testing a custom query for this.
- Add a new endpoint in
-
Docker Practice:
- Build the Docker image for
ProductService
with a different tag version, likeproduct-service:v2
. - Verify that both versions of the container can run simultaneously and test accessing each.
- Build the Docker image for