go-doudou v2.0.5 New Features Guide

During the 2023 Spring Festival, the open-source Go language microservice framework go-doudou released its first official version of 2023, v2.0.5. This version mainly includes 9 new features. This article will introduce the usage and provide code examples in two parts: the CLI command-line code generator part and the microservice framework part. The code used in this article is available at https://github.com/unionj-cloud/go-doudou-tutorials/tree/master/go-statsOpen in new window and https://github.com/unionj-cloud/go-doudou-tutorials/tree/master/gin2doudouOpen in new window. Please clone the code repository and refer to the example code for understanding.

go-doudou CLI Code Generator

Installation

Since the go-doudou CLI code generator iterates quite quickly, and if there are bugs they will be discovered during command execution and code compilation, without introducing online bugs, it is recommended to always install the main branch version.

go install -v github.com/unionj-cloud/go-doudou/v2@main
1

1. New Feature: Generate DTO Structs from Database Table Structures

DTO stands for data transfer object, and structs of data structures that need to be transmitted over the network are generally called DTOs. The most common architectural solution for software development now is the layered architecture MVC. V refers to the view layer, and the current and future mainstream implementation solution is still front-end and back-end separation, with the view layer generally developed using front-end SPA frameworks such as Vuejs/Reactjs/Angularjs, etc. M refers to the model layer, including entity classes that map database table structures. C refers to the controller layer, and DTO belongs to this layer. It is not recommended to skip DTO and directly use entity classes in the model layer as data structures for network transmission. Best practice still requires DTO as a medium, with conversion between DTO and entity classes in the control layer code logic to achieve decoupling. This feature can directly map database table structures and generate DTO struct code with a single command, eliminating the manual definition work for developers.

Suppose there are several tables in the tutorial database as shown in the figure below. Figure 1 First, we need to configure the database connection in the .env file.

DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWD=1234
DB_SCHEMA=tutorial
DB_CHARSET=utf8mb4
1
2
3
4
5
6

Then we execute the following command to generate the DTO.

go-doudou ddl --pre=cad_ -r
1
  • --pre: Specify the table name prefix. In this example, it only generates DTOs for tables with the prefix cad_, optional
  • -r: Reverse generation, indicating generating Go code from database table structures, required

After executing the command, we can see that the dto package and entity package are generated, and the table structure mapping entity classes of the model layer will be generated simultaneously. Figure 2 The dto package has a dto_gen.go file, with the _gen suffix to distinguish it from manually created files by developers. Figure 3 The "You can edit it as your need" prompt on line 3 indicates that developers can customize and modify this file. Repeated execution of the above command will not overwrite manual modifications by developers, as the code will only be incrementally generated, appended to the end of the file. The go:generate directive on line 11 is followed by the built-in name tool of go-doudou, used for batch modification of struct json tags, defaulting to camelCase with the first letter lowercase. The CommonComment struct in the screenshot is the DTO struct corresponding to the cad_common_comment table.

One thing to note is that repeated execution of the above command will decide whether to generate the DTO struct based on whether the entity class mapped to the database table structure in the entity package exists. That is, if a developer only deletes the dto_gen.go file or the struct code inside it, without deleting the corresponding entity class in the entity package, repeated execution of the above command will not regenerate the DTO corresponding to already existing entity classes in the entity package. If the database table structure is modified and needs to regenerate the DTO, both the corresponding entity class in the entity package and the corresponding DTO in the dto package must be deleted as a pair, and then execute the above command.

2. New Feature: Generate Initial go-doudou Project from Swagger2/Swagger3

This feature refers to initializing a go-doudou microservice project through a json file compatible with Swagger 2 or Swagger 3 (OpenAPI 3.0) specifications, mainly including interface definition files, which can facilitate migration from other frameworks to go-doudou, such as from Java ecosystem's Spring Cloud/Spring boot or Go ecosystem's beego framework/gin framework, etc.

Just execute the following command in the same path as the Swagger file.

go-doudou svc init gin2doudou -f gin-admin-swagger.json
1
  • gin2doudou: Specify the project root directory folder name, which is also the default Go module name
  • -f: Specify the Swagger file path, which can be a relative path, an absolute path, or a download link. This example uses the Swagger file from the well-known open source project https://github.com/LyricTian/gin-adminOpen in new window.

Below is a code screenshot of the generated svc.go file Figure 4 Click the green triangle button on line 13 to continue generating Protobuf files and gRPC stub code. Figure 5 Click the green run button on line 12 to continue generating RESTful service code. Execute go run cmd/main.go to start the RESTful service.

➜  gin2doudou git:(master) ✗ go run cmd/main.go                                                                                          
2023/01/29 11:03:04 maxprocs: Leaving GOMAXPROCS=16: CPU quota undefined
                           _                    _
                          | |                  | |
  __ _   ___   ______   __| |  ___   _   _   __| |  ___   _   _
 / _` | / _ \ |______| / _` | / _ \ | | | | / _` | / _ \ | | | |
| (_| || (_) |        | (_| || (_) || |_| || (_| || (_) || |_| |
 \__, | \___/          \__,_| \___/  \__,_| \__,_| \___/  \__,_|
  __/ |
 |___/
2023-01-29 11:03:04 INF ================ Registered Routes ================
2023-01-29 11:03:04 INF +----------------------------+--------+------------------------------+
2023-01-29 11:03:04 INF |            NAME            | METHOD |           PATTERN            |
2023-01-29 11:03:04 INF +----------------------------+--------+------------------------------+
2023-01-29 11:03:04 INF | ApiV1Menus                 | POST   | /api/v1/menus                |
2023-01-29 11:03:04 INF | ApiV1PubLogin              | POST   | /api/v1/pub/login            |
2023-01-29 11:03:04 INF | ApiV1PubLoginExit          | POST   | /api/v1/pub/login/exit       |
2023-01-29 11:03:04 INF | ApiV1PubRefreshtoken       | POST   | /api/v1/pub/refreshtoken     |
2023-01-29 11:03:04 INF | ApiV1Roles                 | POST   | /api/v1/roles                |
2023-01-29 11:03:04 INF | ApiV1Users                 | POST   | /api/v1/users                |
2023-01-29 11:03:04 INF | DeleteApiV1Menus_Id        | DELETE | /api/v1/menus/:id            |
2023-01-29 11:03:04 INF | DeleteApiV1Roles_Id        | DELETE | /api/v1/roles/:id            |
2023-01-29 11:03:04 INF | DeleteApiV1Users_Id        | DELETE | /api/v1/users/:id            |
2023-01-29 11:03:04 INF | GetApiV1Menus              | GET    | /api/v1/menus                |
2023-01-29 11:03:04 INF | GetApiV1Menus_Id           | GET    | /api/v1/menus/:id            |
2023-01-29 11:03:04 INF | GetApiV1Menustree          | GET    | /api/v1/menustree            |
2023-01-29 11:03:04 INF | GetApiV1PubCurrentMenutree | GET    | /api/v1/pub/current/menutree |
2023-01-29 11:03:04 INF | GetApiV1PubCurrentUser     | GET    | /api/v1/pub/current/user     |
2023-01-29 11:03:04 INF | GetApiV1PubLoginCaptcha    | GET    | /api/v1/pub/login/captcha    |
2023-01-29 11:03:04 INF | GetApiV1PubLoginCaptchaid  | GET    | /api/v1/pub/login/captchaid  |
2023-01-29 11:03:04 INF | GetApiV1Roles              | GET    | /api/v1/roles                |
2023-01-29 11:03:04 INF | GetApiV1Roles_Id           | GET    | /api/v1/roles/:id            |
2023-01-29 11:03:04 INF | GetApiV1Rolesselect        | GET    | /api/v1/rolesselect          |
2023-01-29 11:03:04 INF | GetApiV1Users              | GET    | /api/v1/users                |
2023-01-29 11:03:04 INF | GetApiV1Users_Id           | GET    | /api/v1/users/:id            |
2023-01-29 11:03:04 INF | PutApiV1Menus_Id           | PUT    | /api/v1/menus/:id            |
2023-01-29 11:03:04 INF | PutApiV1PubCurrentPassword | PUT    | /api/v1/pub/current/password |
2023-01-29 11:03:04 INF | PutApiV1Roles_Id           | PUT    | /api/v1/roles/:id            |
2023-01-29 11:03:04 INF | PutApiV1Users_Id           | PUT    | /api/v1/users/:id            |
2023-01-29 11:03:04 INF | GetDoc                     | GET    | /go-doudou/doc               |
2023-01-29 11:03:04 INF | GetOpenAPI                 | GET    | /go-doudou/openapi.json      |
2023-01-29 11:03:04 INF | Prometheus                 | GET    | /go-doudou/prometheus        |
2023-01-29 11:03:04 INF | GetConfig                  | GET    | /go-doudou/config            |
2023-01-29 11:03:04 INF | GetStatsvizWs              | GET    | /go-doudou/statsviz/ws       |
2023-01-29 11:03:04 INF | GetStatsviz                | GET    | /go-doudou/statsviz/*        |
2023-01-29 11:03:04 INF | GetDebugPprofCmdline       | GET    | /debug/pprof/cmdline         |
2023-01-29 11:03:04 INF | GetDebugPprofProfile       | GET    | /debug/pprof/profile         |
2023-01-29 11:03:04 INF | GetDebugPprofSymbol        | GET    | /debug/pprof/symbol          |
2023-01-29 11:03:04 INF | GetDebugPprofTrace         | GET    | /debug/pprof/trace           |
2023-01-29 11:03:04 INF | GetDebugPprofIndex         | GET    | /debug/pprof/*               |
2023-01-29 11:03:04 INF +----------------------------+--------+------------------------------+
2023-01-29 11:03:04 INF ===================================================
2023-01-29 11:03:04 INF Http server is listening at :6060
2023-01-29 11:03:04 INF Http server started in 3.347258ms
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

Note that if you execute line 12 first, a main function for the RESTful service will be generated. If you execute line 13 first, a main function for the gRPC service will be generated. If a main function already exists, it will be skipped, and go-doudou will not make any modifications. If you want to start both RESTful and gRPC services simultaneously, you need to manually modify the main function. For specific code, please refer to the quick start tutorial on the go-doudou official website.

3. New Feature: Go Language HTTP Client Automatic Gzip Compression for Request Body

The purpose of adding this feature is to compress large-sized request bodies, speed up network transmission, and improve performance. In the go-stats project, execute go-doudou svc http -c to generate the client package and HTTP client code encapsulating go-resty (unit test files and code need to be implemented by the developer). Figure 6 Gzip-related code is in lines 50-71 of the client.go file. Figure 7 Compared to previous versions, there is an additional input parameter of type Options, which acts as a switch. The caller can set whether to compress the request body through this switch when calling client methods.

type Options struct {
    GzipReqBody bool
}
1
2
3

4. New Feature: Automatically Generate Enum Type Implementation Methods

go-doudou provides support for enum types, requiring enum types to implement the IEnum interface:

type IEnum interface {
	StringSetter(value string)
	StringGetter() string
	UnmarshalJSON(bytes []byte) error
	MarshalJSON() ([]byte, error)
}
1
2
3
4
5
6

In previous versions, developers needed to implement this interface manually. With this new feature, code can be generated directly with a single command. Figure 8 As shown in the figure above, you can directly write the //go:generate directive shown on line 34 anywhere in the file where the enum type is defined, and then click the green triangle button to execute the command. go-doudou will generate an enums_gen.go file in the same directory as the original file. In this example, the following code will be generated:

/**
* Generated by go-doudou v2.0.5.
* Don't edit!
 */
package vo

import "encoding/json"

func (k *KeyboardLayout) StringSetter(value string) {
	switch value {
	case "UNKNOWN":
		*k = UNKNOWN
	case "QWERTZ":
		*k = QWERTZ
	case "AZERTY":
		*k = AZERTY
	case "QWERTY":
		*k = QWERTY
	default:
		*k = UNKNOWN
	}
}

func (k *KeyboardLayout) StringGetter() string {
	switch *k {
	case UNKNOWN:
		return "UNKNOWN"
	case QWERTZ:
		return "QWERTZ"
	case AZERTY:
		return "AZERTY"
	case QWERTY:
		return "QWERTY"
	default:
		return "UNKNOWN"
	}
}

func (k *KeyboardLayout) UnmarshalJSON(bytes []byte) error {
	var _k string
	err := json.Unmarshal(bytes, &_k)
	if err != nil {
		return err
	}
	k.StringSetter(_k)
	return nil
}

func (k *KeyboardLayout) MarshalJSON() ([]byte, error) {
	return json.Marshal(k.StringGetter())
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

5. New Feature: PathVariable Dynamic Route Support

Previous versions did not support PathVariable dynamic routes because query strings could completely replace them in actual development. However, to implement feature 2, this feature was added.

This feature is mainly used when defining service interfaces. You can tell go-doudou which part of the interface method signature needs to be used as a PathVariable to generate routes through an underscore _ followed by uppercase English letters. Let's understand with an example. Figure 9 As shown in line 21 of the code, the method name GetShelves_ShelfBooks_Book will generate the interface route /shelves/:shelf/books/:book, containing two parameters: shelf and book. Note: These two parameters must be passed in the method signature, the order doesn't matter, and they can only be all lowercase English letters. In the generated transport/httpsrv/handler.go file, you can see the following code. Figure 10

6. New Feature: Generate Go Language HTTP Request Client Code from Swagger2

go-doudou internally calls the https://github.com/getkin/kin-openapiOpen in new window library to handle Swagger2 to Swagger3 conversion.

7. New Feature: Service Definition Support for decimal.Decimal Type

This feature is particularly useful for developing transaction or e-commerce type projects. Figure 11 As shown in the figure above, you can define decimal.Decimal type parameters in the service interface definition. The parsing of this type of parameter will be automatically handled in the generated transport/httpsrv/handlerimpl.go file. Figure 12 The core is calling the ToDecimalE method of the cast package.

In the generated Swagger file, decimal.Decimal parameters or properties are treated as string types, meaning that for front-end colleagues, they are just strings. Figure 13 Example representation of the DecimalWrapper struct in Swagger:

type DecimalWrapper struct {
	Data decimal.Decimal `json:"data"`
}
1
2
3

Figure 14

go-doudou Microservice Framework

8. New Feature: Server Automatic Gzip Decompression for HTTP Request Body

A gzipBody middleware has been added to the framework/rest/middleware.go file in the go-doudou code repository and has been added to the middleware call chain by default, transparent to developers, and ready to use out of the box. This middleware is only effective for requests with Content-Encoding equal to gzip in the HTTP request header. Figure 15

9. New Feature: Custom Global PanicHandler

The go-doudou framework has added a recovery middleware by default, used to recover the program from panic, transparent to developers, and ready to use out of the box. However, to facilitate custom panic handling, this feature was added. The usage method is shown in the figure below: Figure 16 Just replace the previous version's rest.NewRestServer() with rest.NewRestServerWithOptions and pass in the Functional Options functionOpen in new window rest.WithPanicHandler.