Practical Guide to Getting Started with gRPC and Spring Boot

Published on December 7, 2024

As I’ve been exploring gRPC during my self-directed learning journey, it really caught my interest and prompted me to share what I’ve learned through a series of articles. This first one will start simply by building a basic e-commerce app to get started with gRPC.

While I’ll do my best to explain key concepts, I may not cover every detail, as the official gRPC documentation provides excellent resources for deeper exploration. I also plan to write follow-up articles to discuss some of the more important and advanced concepts.

Regarding the e-commerce app, I’ve used a microservices architecture to make it more engaging 😃 while keeping the implementation straightforward and not overly complex.

Now let’s get started! 💪

Microservices Architecture

Let’s check out the architecture of the e-commerce app below.

The relationship diagram illustrates the interactions between various services in the system:

Checkout Service

  • Communicates with inventory-service to validate the purchase quantities.
  • Interacts with item-service to retrieve item prices and compute the order amount.
  • Calls shipping-service to calculate the shipping fee based on the shipping address, purchase amount, and item dimensions.
  • Contacts user-service to verify the user's balance.

Order Service

  • Notifies inventory-service to update item inventory.
  • Updates the user’s balance via user-service.
  • Creates a new order entry in the database.

Tracking Service

  • Queries the order table to retrieve the shipping status for a given order.

In this architecture, the inventory-service, item-service, shipping-service, and user-service operate as gRPC servers. The checkout-service and order-service act as gRPC clients, consuming these services and exposing REST APIs for testing and external interactions.

Database Setup

I’m using MySQL as the database for this app. There are several ways to set up MySQL locally — you can install it directly on your machine or use a MySQL Docker image, among other options.

The app's database structure is shown below, and the schema initialization file is available here.

Application Setup

I’m creating a multi-module Spring Boot project following these steps:

mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=grpc-ecommerce-platform -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
cd grpc-ecommerce-platform

Update `jar` to `pom` in pom.xml file

mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=proto -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=order-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=inventory-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=item-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=shipping-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=tracking-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=user-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
mvn archetype:generate -DgroupId=github.io.truongbn -DartifactId=checkout-service -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

The app folder structure is as follows:

grpc-ecommerce-platform/
├── checkout-service/ # Handles order validation and payment
├── inventory-service/ # Manages stock levels and availability
├── item-service/ # Provides item details and pricing
├── order-service/ # Processes and tracks orders
├── proto/ # Contains .proto files for gRPC definitions
├── shipping-service/ # Calculates shipping costs and logistics
├── tracking-service/ # Tracks shipment status
└── user-service/ # Manages user accounts and balances

Protocol Buffers Creation

gRPC leverages Protocol Buffers, Google’s robust and open-source tool for serializing structured data. To begin, I first define the data structure to be serialized in the.proto files, which are specifically organized within proto module.

The dependencies for this module are specified in its pom.xml file.

You can find all the .proto files here.

Once everything is set up, run the following command in the proto module.

mvn clean compile

Let’s see what we have in the target folder.

protoc-plugins is responsible for reading the proto files and generating the corresponding Java source code in the generated-sources/protobuf/java directory, which will be used inside the app.

gRPC Servers Implementation

Let’s start building the inventory service by defining its dependencies in the pom.xml file.

You can find the full implementation of the inventory service here, but first, let me explain some key concepts.

I configure the service properties in the application.yml file, including the following gRPC configuration:

grpc:
server:
port: 2222

This configuration set up the gRPC server on port 2222, where it will handle all incoming gRPC requests.

The main business logic of the service is implemented inside InventoryService Java class.

@GrpcService
@RequiredArgsConstructor
public class InventoryService extends InventoryServiceGrpc.InventoryServiceImplBase {
private final InventoryRepository inventoryRepository;
@Override
public void getItemInventory(InventoryInformationRequest request,
StreamObserver responseObserver) {
var inventory = ...;
var inventoryInformation = InventoryInformation.newBuilder()
.setItemId(inventory.getItemId()).setQuantities(inventory.getQuantities()).build();
responseObserver.onNext(inventoryInformation);
responseObserver.onCompleted();
}

@Override
public void syncInventory(InventorySyncRequest request,
StreamObserver responseObserver) {
var res = ...;
var syncResponse = InventorySyncResponse.newBuilder().setResult(res == 1).build();
responseObserver.onNext(syncResponse);
responseObserver.onCompleted();
}
}

Allow me to provide a brief explanation of how this class works.

First, I annotate it with @GrpcService annotation, which tells Spring Boot to automatically recognize it as a gRPC service. The methods in the class that handle gRPC requests are exposed as part of the service, and Spring manages the service’s lifecycle and dependencies.

Regarding the actual business logic for inventory in the gRPC server. If you remember inside inventory-service.proto file, I created a proto service with 2 methods:

  1. GetItemInventory: Allows clients to request inventory information for a specific item by sending an InventoryInformationRequest. The response is an InventoryInformation message.
  2. SyncInventory: Used for synchronizing inventory data. Clients send an InventorySyncRequest with the item ID and purchased quantities. The service responds with an InventorySyncResponse.

These methods define how the inventory service should interact with clients.

You might wonder why InventoryService implement InventoryServiceGrpc.InventoryServiceImplBase?

The answer is that in the generated Java classes from the .proto file, InventoryServiceGrpc.InventoryServiceImplBase is an abstract class with empty implementations of the RPC methods. To provide the actual functionality, the InventoryService class extends this base class and overrides the relevant methods (getItemInventory and syncInventory in this case).

The implementation of the other gRPC services is quite similar. You can find them in the corresponding links below:

gRPC Clients Implementation

Let’s start building the checkout service by defining its dependencies in the pom.xml file.

You can find the full implementation of the checkout service here, but first, let me explain some key concepts.

The checkout service calls gRPC servers and exposes a REST API for external interaction. Here’s how I configured its application.yml file:

grpc:
client:
# inventory-service
inventory-service:
address: static://localhost:2222
negotiationType: PLAINTEXT
# inventory-service
item-service:
address: static://localhost:3333
negotiationType: PLAINTEXT
# shipping-service
shipping-service:
address: static://localhost:5555
negotiationType: PLAINTEXT
# user-service
user-service:
address: static://localhost:7777
negotiationType: PLAINTEXT
  • grpc.client is used to set up the gRPC client to connect to various services (inventory, item, shipping, and user).
  • address specifies the gRPC server’s address and port to which the client should connect.
  • negotiationType defines the type of communication between the client and server. For more details, refer to the official website.

Next, let’s explore how the checkout service interacts with a gRPC server, using the inventory service as an example.

@Service
public class InventoryService {
@GrpcClient("inventory-service")
private InventoryServiceGrpc.InventoryServiceBlockingStub inventoryClient;
public InventoryInformation getInventoryInformation(String itemId) {
var request = InventoryInformationRequest.newBuilder().setItemId(itemId).build();
return inventoryClient.getItemInventory(request);
}
}

This class demonstrates how to communicate with the gRPC inventory-service.

The key annotation @GrpcClient("inventory-service") tells Spring Boot to inject a gRPC client stub InventoryServiceBlockingStub that connects to the service named inventory-service.

The name inventory-service corresponds to the client configuration defined in the application.yml file under the grpc.client section. It determines the address and connection type (e.g., localhost:2222, PLAINTEXT) for the gRPC server. gRPC also supports other client stubs, for more details, refer to the official website.

Interacting with other gRPC servers (e.g., item-service, shipping-service, and user-service) follows a similar approach.

Similarly, implementing gRPC clients for services like order-service involves a similar process.

Test the app with Swagger

To test this app, start the services in the following order:

  • Inventory Service: Execute InventoryApplication.main().
  • Item Service: Execute ItemServiceApplication.main().
  • User Service: Execute UserServiceApplication.main().
  • Shipping Service: Execute ShippingServiceApplication.main().
  • Checkout Service: Execute CheckoutServiceApplication.main().
  • Order Service: Execute OrderServiceApplication.main().

Once all services are up and running, visit http://localhost:1111/swagger-ui/index.html to test the checkout service.

Try out POST:/checkout/order”, the order should be checked out successfully 😍

Now visit http://localhost:4444/swagger-ui/index.html to test the order service.

Try out POST:/orders”, the order should be purchased successfully 😍

We’ve just wrapped up implementing a simple e-commerce app to explore some key gRPC concepts and how it integrates with Spring Boot. It’s a good starting point for learning gRPC. I hope it’s working as expected you guys!

The completed source code can be found in this GitHub repository: https://github.com/buingoctruong/grpc-ecommerce-platform

I would love to hear your thoughts!

Thank you for reading, and goodbye!