Example 6, Build a simple golang e-commerce microservices framework step by step using tool
Tags: go, grpc, gin, microservices
Published: 2023-11-07 06:30:31

By taking a simple e-commerce microservice cluster as an example, the product details page contains product information, inventory information, and product evaluation information. These data are scattered in different microservices. The grpc gateway service assembles the required data and returns it to the product details page, as shown in the following figure. Click to see the full microservice cluster code [micro-cluster-demo](https://github.com/zhufuyi/sponge_examples/tree/main/6_micro-cluster).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k328o97i344oq6106lo4.png)


### Dependencies

- Prepare a proto files:
  - The [comment.proto](https://github.com/zhufuyi/sponge_examples/blob/main/6_micro-cluster/comment/api/comment/v1/comment.proto) file defines grpc methods to obtain comment data based on the product ID and is used to generate the comment grpc service code.  
  - The [inventory.proto](https://github.com/zhufuyi/sponge_examples/blob/main/6_micro-cluster/inventory/api/inventory/v1/inventory.proto) file defines grpc methods to obtain inventory data based on the product ID and is used to generate the inventory grpc service code.
  - The [product.proto](https://github.com/zhufuyi/sponge_examples/blob/main/6_micro-cluster/product/api/product/v1/product.proto) file defines grpc methods to obtain product details based on the product ID and is used to generate the product grpc service code.
  - The [shop_gw.proto](https://github.com/zhufuyi/sponge_examples/blob/main/6_micro-cluster/shop_gw/api/shop_gw/v1/shop_gw.proto) file defines grpc methods to assemble the data required by the product details page based on the product ID and is used to generate the shop grpc gateway service code.
- Install the tool [sponge](https://github.com/zhufuyi/sponge/blob/main/assets/install-en.md).

After installing the "sponge" tool, execute the following command to open the UI interface:

```bash
sponge run
```

<br>

### Quickly generate and start comment, inventory, and product microservices

#### Generate comment, inventory, and product microservice code

Enter the Sponge UI interface, click the left menu bar 【Protobuf】-->【create microservice project】, fill in the respective parameters of the comment, inventory, and product, and generate the comment, inventory, and product service code respectively.  

The microservice framework uses [grpc](https://github.com/grpc/grpc-go) and also contains common service governance function codes, build deployment scripts, etc. 

Quickly create a comment service as shown below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l6f0hkc3erjk9156fquh.png)
 
Quickly create an inventory service as shown below: 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g2paqmi1qq5oyhl6ozvs.png)

Quickly create a product service as shown below:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dygs34p03794hcliymu5.png)
  
Open three terminals, one for each of the comment, inventory, and product services.

<br>

#### Start the comment service

Switch to the comment directory and perform the following steps:
(1) Generate api interface code (internal/service directory), grpc client test code (internal/service directory), api interface error code (internal/ecode directory), generate pb.go code (api/comment/v1 directory)

```bash
make proto
```  

(2) Open `internal/service/comment.go`, which is the generated template code. There is a line of panic code prompting you to fill in the business logic code. Fill in the business logic here, for example, fill in the return value:

```go
	return &commentV1.ListByProductIDReply{
		Total:     11,
		ProductID: 1,
		CommentDetails: []*commentV1.CommentDetail{
			{
				Id:       1,
				Username: "Mr Zhang",
				Content:  "good",
			},
			{
				Id:       2,
				Username: "Mr Li",
				Content:  "good",
			},
			{
				Id:       3,
				Username: "Mr Wang",
				Content:  "not good",
			},
		},
	}, nil
```

(3) Open the `configs/comment.yml` configuration file, find grpc, and modify the port and httpPort values under it:

```yaml 
grpc:     
  port: 18203              # listen port   
  httpPort: 18213        # profile and metrics ports  
```   

(4) Compile and start the comment service

```bash
make run
```

<br>  
  
#### Start the inventory service

Switch to the inventory directory and perform the same steps as for the comment:  
(1) Generate api interface code (internal/service directory), grpc client test code (internal/service directory), api interface error code (internal/ecode directory), generate pb.go code (api/inventory/v1 directory)

```bash
make proto  
```

(2) Open `internal/service/inventory.go`, which is the generated template code. There is a line of panic code prompting you to fill in the business logic code. Fill in the business logic here, for example, fill in the return value:

```go
	return &inventoryV1.GetByIDReply{
		InventoryDetail: &inventoryV1.InventoryDetail{
			Id:      1,
			Num:     999,
			SoldNum: 111,
		},
	}, nil
```

(3) Open the `configs/inventory.yml` configuration file, find grpc, and modify the port and httpPort values under it:

```yaml  
grpc:      
  port: 28203              # listen port    
  httpPort: 28213        # profile and metrics ports   
```

(4) Compile and start the inventory service  
```bash
make run
```  

<br>
  
#### Start the product service

Switch to the product directory and perform the same steps as for the comment:

(1) Generate api interface code (internal/service directory), grpc client test code (internal/service directory), api interface error code (internal/ecode directory), generate pb.go code (api/product/v1 directory)

```bash
make proto
```

(2) Open `internal/service/product.go`, which is the generated template code. There is a line of panic code prompting you to fill in the business logic code. Fill in the business logic here, for example, fill in the return value: 

```go
	return &productV1.GetByIDReply{
		ProductDetail: &productV1.ProductDetail{
			Id:          1,
			Name:        "Data cable",
			Price:       10,
			Description: "Android type c data cable",
		},
		InventoryID: 1,
	}, nil
```

(3) Open the `configs/product.yml` configuration file, find grpc, and modify the port and httpPort values under it: 

```yaml
grpc:      
  port: 38203              # listen port    
  httpPort: 38213        # profile and metrics ports
```

(4) Compile and start the product service 

```bash
make run
```

After the comment, inventory, and product microservices have started successfully, you can now generate and start the gateway service.

<br>  

### Quickly generate and start the grpc gateway service

Enter the Sponge UI interface, click the left menu bar 【Protobuf】-->【create grpc gateway project】, fill in some parameters to generate the grpc gateway project code. 

The web framework uses [gin](https://github.com/gin-gonic/gin), which also contains swagger documentation, common service governance function codes, build deployment scripts, etc. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tqur12x3mwf1b6kspe2q.png)

In order to connect to the comment, inventory, and product grpc services, you need to generate additional connection grpc service codes. Click the left menu bar 【Public】--> 【generate grpc connection code】, fill in the parameters to generate the code, and then move the generated connection grpc service code to the grpc gateway project code directory. 

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ou0gukjavishtlqwk5v.png)

In order to call the methods of the grpc services in the grpc gateway service, you need to copy the proto files of the comment, inventory, and product grpc services to the api/shop_gw/v1 directory of the grpc gateway service.

Switch to the shop_gw directory and perform the following steps:

(1) Copy proto file from grpc services comment,inventory,product.

```bash
make copy-proto SERVER=../comment,../inventory,../product
```

(2) Open the `configs/shop_gw.yml` configuration file, find grpcClient, and add the addresses of the comment, inventory, and product grpc services:

```yaml
grpcClient:
  - name: "comment"
    host: "127.0.0.1"
    port: 18282
    registryDiscoveryType: ""
  - name: "inventory"
    host: "127.0.0.1"
    port: 28282
    registryDiscoveryType: ""
  - name: "product"
    host: "127.0.0.1"
    port: 38282
    registryDiscoveryType: ""
```

(3) Generate pb.go code, generate registered routing code, generate template code, and generate swagger documentation

```shell
make proto
```

(4) Open `internal/service/shop_gw.go`, which is the generated API interface code. Fill in the business logic code here, fill in the following simple business logic code:

```go
package service

import (
	"context"

	commentV1 "shop_gw/api/comment/v1"
	inventoryV1 "shop_gw/api/inventory/v1"
	productV1 "shop_gw/api/product/v1"
	shop_gwV1 "shop_gw/api/shop_gw/v1"
	"shop_gw/internal/ecode"
	"shop_gw/internal/rpcclient"

	"github.com/zhufuyi/sponge/pkg/grpc/interceptor"
	"github.com/zhufuyi/sponge/pkg/logger"
)

var _ shop_gwV1.ShopGwLogicer = (*shopGwClient)(nil)

type shopGwClient struct {
	commentCli   commentV1.CommentClient
	inventoryCli inventoryV1.InventoryClient
	productCli   productV1.ProductClient
}

// NewShopGwClient create a client
func NewShopGwClient() shop_gwV1.ShopGwLogicer {
	return &shopGwClient{
		commentCli:   commentV1.NewCommentClient(rpcclient.GetCommentRPCConn()),
		inventoryCli: inventoryV1.NewInventoryClient(rpcclient.GetInventoryRPCConn()),
		productCli:   productV1.NewProductClient(rpcclient.GetProductRPCConn()),
	}
}

// GetDetailsByProductID get page detail by product id
func (c *shopGwClient) GetDetailsByProductID(ctx context.Context, req *shop_gwV1.GetDetailsByProductIDRequest) (*shop_gwV1.GetDetailsByProductIDReply, error) {
	err := req.Validate()
	if err != nil {
		logger.Warn("req.Validate error", logger.Err(err), logger.Any("req", req), interceptor.CtxRequestIDField(ctx))
		return nil, ecode.StatusInvalidParams.Err()
	}

	// fill in the business logic code here

	productReply, err := c.productCli.GetByID(ctx, &productV1.GetByIDRequest{
		Id: req.ProductID,
	})
	if err != nil {
		return nil, err
	}
	logger.Info("get product info successfully", interceptor.CtxRequestIDField(ctx))

	inventoryReply, err := c.inventoryCli.GetByID(ctx, &inventoryV1.GetByIDRequest{
		Id: productReply.InventoryID,
	})
	if err != nil {
		return nil, err
	}
	logger.Info("get inventory info successfully", interceptor.CtxRequestIDField(ctx))

	commentReply, err := c.commentCli.ListByProductID(ctx, &commentV1.ListByProductIDRequest{
		ProductID: req.ProductID,
	})
	if err != nil {
		return nil, err
	}
	logger.Info("list comments info successfully", interceptor.CtxRequestIDField(ctx))

	return &shop_gwV1.GetDetailsByProductIDReply{
		ProductDetail:   productReply.ProductDetail,
		InventoryDetail: inventoryReply.InventoryDetail,
		CommentDetails:  commentReply.CommentDetails,
	}, nil
}
```

(5) Compile and start the shop_gw service

```bash
make run
```

You can test the API interface by opening [http://localhost:8080/apis/swagger/index.html](http://localhost:8080/apis/swagger/index.html) in your browser.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rvcqmudx7q1zgxi7ei8r.png)

<br>  

### Summary 

Using the Sponge tool, it is easy to build a microservice cluster. The common service governance functions of each microservice in the cluster are also available, such as service registration and discovery, throttling, circuit breaking, link tracking, monitoring, performance analysis, resource statistics, CICD, etc. These functions are enabled or disabled in the YML configuration file. As long as the grpc methods are defined well in the proto file, the subsequent development is basically filling in the business logic code in the generated template code and verifying the business logic in the generated test code, making development simplified, improving development efficiency and saving development time.