- 1. Gong
- 2. Gong is a Go sub language
- 3. Using gong
- 4. Gong's Features
- 4.1. Gong is a go sub langage for generating a full stack
- 4.2. Back end SQL and go code from gong code
- 4.3. Controler go code from gong code
- 4.4. Front end angular code from gong code
- 4.5. Angular material code from gong code
- 4.6. Front stage, back stage and repository programming model
- 4.7. UML Code documentation as go code
- 4.8. Integrated Stack configuration management
- 4.9. Persistance as go code for enabling fast refactoring
- 4.10. Further documentation
- 5. A "hello world" stack in 5 minutes
With gong, a web application is a set of stacks. Each stack is based on go and angular. Each stack has its own data model.
Gong is a work in progress. API of the framework is not yet stabilized/baselined
Gong (go + ng) is a framework for rapid web application development (a.k.a. full stack development) based on go and angular. The go back-end uses gin, gorm and sqlite (a pure go sqlite, no cgo needed). The angular front-end uses angular material.
The unit of development in gong is the gong stack (a "stack" in the rest of this document). A stack can import other stacks (both the front end and the back end of a stack are integrated as a whole). The granularity of a stack is similar to an angular components.
Gong includes gongc, a go data model compiler to generate front-end and back-end code. Gongc compiles gong code, a go sub-langage, to go and angular.
Gong fullstack approach was inspired by the idea that complexity facing the programmer should be carefuly managed (for the idea, see conceptual compression concept or Rob Pike's design of Go regarding complexity.
go version equal or above 1.21. See https://golang.org for installation.
Gong uses sqlite3 in a cgo free configuration by default.
go-swagger is a go program that is used (as an option) after each gongc
compilation to generate the project API in a yml
file. gongc is robust to the absence of go-swagger but it is recommanded to use it if you need to document the back-end API with yaml.
Gong uses npm version >= 8 (see https://nodejs.org)
Gong uses angular version 16 (see https://angular.io for installation)
Vscode is usefull & handy because the tasks definitions and debug configuration related to gong are provided in the repository.
Gong is a langage with a compiler and a library.
Gong is also a go sub language because it is not autonomous. It must be developped within a go envionment and it needs the tools of the go toolchains. A gong program is a go program.
Gong specification is the go specification with additional constraints. If a go construct does not meet those contraints, it is transparant to the gong compiler.
There are three objects in gong.
A gong program is a go program (go > 1.20) developped with a go module. The gong code is developed in only one package "go/models"
package models
No particular inmport is necessary.
// Hello is a gongstruct
type Hello struct { // it is exported
Name string // it has an exported "Name" field
}
// foo is not a gongstruct
type foo struct { // it is not exported
Name string
}
// Bar is not a gongstruct
type Bar struct { // it is exported
name string // it has no exported "Name" field
}
// Zong is not a gongstruct
//
// gong:ignore it has a "ignore" magic code
type Zong struct { // it is exported
Name string // it has an exported "Name" field
}
// Gongenums can be of type tring or int.
type AEnumType string // It is exported
const (
ENUM_VAL1 AEnumType = "ENUM_VAL1_NOT_THE_SAME"
ENUM_VAL2 AEnumType = "ENUM_VAL2"
)
type CEnumTypeInt int
// values for EnumType
const (
CENUM_VAL1 CEnumTypeInt = iota
CENUM_VAL2
)
Gongfields are fields within a gongstruct.
// Hello is a gongstruct
type Hello struct { // it is exported
Name string // it has an exported "Name" field
// Those are gongfields. They are exported fields of type int, int64, float, float64 bool, string
Floatfield float64
Intfield int
Booleanfield bool
// Those are gongfields. They are exported fields of type time.Duration or time.Time
Duration1 time.Duration
Date time.Time
// HelloP is a gongfield. It is an exported pointer to a gongstruct within the same package
HelloP *Hello
// HellosP is a gongfield. It is an exported slice of pointers to a gongstruct within the same package
HellosP []*Hello
// AEnum is a gongfield. It is an exported field of a gongenum type within the same pakage
AEnum AEnumType
// CEnum is gongfield
CEnum CEnumTypeInt
}
// A gongnote is a string const with a comment
// that starts with the GONGDOC keyword
// It follows the "Note" defined in the [go doc](https://pkg.go.dev/go/doc) and
// [go doc comment]([]https://pkg.go.dev/go/doc/comment) defined in the
// standard go library
//
// > A Note represents a marked comment starting with "MARKER(uid): note body".
// GONGDOC(NoteExample): Note example
// It can reference Gongstructs
// [models.Bstruct],
// [models.Astruct],
// Gongenums
// [models.AEnumType]
// Gongfields
// [models.Astruct.Associationtob],
// having the following const exported identifier allows for
// referencing the note from the UML note and allows for
// renaming
//
// # This is heading 1
//
// ## This is heading 1.1
//
// -
const NoteExample = ""
The gongc compilers generates code within the "go/models" package and within other packages.
the test
directory contains a stack wit the generated code.
cd test/ng
npm i; ng build
cd ..
go run main.go
Then, browse to localhost:8080
Example of a generated application with gong
Installing The gong compiler.
From the root directory.
cd go/gongc; go install; cd ../..
Generating the code
cd test; gongc go/models
A gong application is a stack that can integrate other stacks. Below is a list of stacks that can be reused.
https://github.com/fullstack-lang/gongdoc, a UML editor (based on jointjs) for documenting a gong model. gongdoc uses the gong stack.
https://github.com/fullstack-lang/gongsim, a stack for developping simulations
https://github.com/fullstack-lang/gongleaflet, a stack for developping application with leaflet carto components
https://github.com/fullstack-lang/gongsvg, a stack for developping application with svg graphical components
https://github.com/fullstack-lang/gongjointjs, a stack for developping application with jointjs interactive graphical component
https://github.com/fullstack-lang/helloworld is a recommanded starting point for understanding gong.
https://github.com/fullstack-lang/bookstore is a little more sophisticated example than helloworld.
https://github.com/fullstack-lang/laundromat, is a more sophisticated example. It is a simulation stack that reuses 3 other stacks (gong, gongsim, gongdoc)
https://github.com/fullstack-lang/gongfly, an airplane simulation that reuses 4 stacks (gong, gongsim, gongdoc, gongleaflet)
https://github.com/fullstack-lang/gongproject, a project management application that reuses 3 stacks (gong, gongjointjs, gongdoc)
Gong is a sublangage of go (stereotyped go). It is comprised of one or many gongstruct and gongenum. The gongc compiler is based on the go compiler.
A gong project is a go module with a unique name (for instance github.com/fullstack-lang/gongdoc
). gongstruct and gongenum have to be present in the go/models
sub package.
A gongstruct is a go struct with a Name
field.
package models
type Hello struct {
Name string
}
A gongenum is a go string with some const values. The string type must be commented with // swagger:enum
package models
// swagger:enum AEnumType
type AEnumType string
// values for EnumType
const (
ENUM_VAL1 AEnumType = "ENUM_VAL1"
ENUM_VAL2 AEnumType = "ENUM_VAL2"
)
A gongstruct can have gongfield. A gongfield is an exported fields (starts with an Upperscase) with
type as int
, float64
, bool
, time.Time
, time.Duration
, pointer
to another gongstruct
in
the same package and slice
of pointers
to another gongstruct
in the same package (for ONE-MANY associations).
MANY-MANY associations are also implemented (if the target of the ONE-MANY association ends with the Use
).
// Astruct demonstrate basic gong features
type Astruct struct {
// a "Name" field is necessary to generate a GongStruct
Name string
// string a is a supported type of gong
Description string
// bool is a supported type of gong
Booleanfield bool
// enums is a supported type of gong (if they are string)
Aenum AEnumType
// an embedded struct is supported if it is not a gongstruct (i.e. it is without a field `Name`)
Cstruct
// float64 is a supported type of gong
Floatfield float64
// int is a supported type of gong
Intfield int
// time.Time is a supported type of gong
Date time.Time
// time.Duration is a supported type of gong
Duration time.Duration
// ONE-ZERO/ONE association is a supported type of gong
Associationtob *Bstruct
// ONE-MANY association is a supported type of gong
Sliceofb []*Bstruct
// MANY-MANY association,
// because AclassBclassUse ends with "Use", it will implement a ONE/ONE assocation to the Bstruct)
AnarrayofbUse []*AstructBstructUse
}
The gorm framework is a go API for ORM (Object Realtionship Management). This means you do not need to code SQL to configure/migrate the database. gorm acts as a conceptual compression since you do not need to know SQL code to program the database, you only need to master the go API.
However, gorm is still a concept you need to know to program the database in go. gong allows to ignore this conceptual layer
since the gorm code is generated. Indeed, the gongc compiler compiles the models (the go/models
package)
to generate the appropriate gorm code (in the go/orm
package). Therefore, gong is another
conceptual compression
to avoid programming gorm code.
The gin framework is an API for programming the controllers that implement the REST web service of the application. gin acts as a conceptual compression since it provides a high level concept to program the REST api.
However, you need to know to program gin for implementing the REST api. gong allows to ignore this conceptual layer since the gin code is generated by gong. The gongc compiles compiles the models (the go/models
package)
to generate the appropriate gin code (in the go/controllers
package)
Angular framework is an API for programming front-end of the application.
gongc compiles the models (the go/models
package) to generate the appropriate angular code (in the ng/projects/<name of the package>
angular workspace). This code provides an API to the front end code that
follows the data models of the the go/models
package.
Each gongstruct/gongenum is compiled into an appropriate typescript code.
Angular Material is a set of front-end Angular components.
gongc compiles the models (the go/models
package) to generate the appropriate angular material code, in the ng/projects/<name of the package>
angular workspace :
- navigation tree between all gongstruct of the
go/models
package - table for each gongstruct of the
go/models
package - form for each gongstruct of the
go/models
package
Putting data to the database and retrieving data from the database (CRUD operations) is performed via an API that is generated for each gongstruct.
The API follows loosely the repository pattern.
The generated API comprises Stage()
, Unstage()
, Commit()
, Delete()
for each gongstruct.
see gongcode.
The configuration of both back-end and front-end code of a stack is a single configuration item.
This is done thank the go module
. For the go code, it is the standard way of managing dependencies. For the angular/typescript/js code, it is done in four steps.
First, gong uses the go embed
fearure that allows, by using four lines of go in a file stored in the angular workspace to directory,
package ng
import "embed"
//go:embed projects
var Projects embed.FS
that the code in the angular workspace projects
directory is stored into the go module
.
The second step is to import the created ng package
in the project that will use the stack. For instance, the following line makes avaible the Project
directory to the project.
_ "github.com/fullstack-lang/gongjointjs/ng"
The third step is another go feature, the go mod vendor
command, that makes available the source code of all dependencies in a vendor
directory simply by issuing the command. Then, the angular code is now in the directory vendor/github.com/fullstack-lang/gongjointjs/ng
.
The four step is to define your front-end dependency by using the tsconfig.json
file and point it the to import path into the vendor
directory (instead of using the installation by npm install
of the imported front code module). you are therefore assured that your back-end code and front-end code belong to the same configuration. (see the https://github.com/fullstack-lang/gongproject/blob/master/ng/tsconfig.json for an example of tsconfig.json configuration).
Gong's goal is to speed up development of full stack applications. Gong's goal is therefore to allow fast iterations of the database model and content/database.
An iteration of the data model can include an addition or removal of a concept (a go´struct') or the addition or removal of a field of a concept. In this case, the 'gorm' tool takes care of the content/database migration.
An iteration can also include a renaming of a field or the renaming of a struct. In this case, the code can be automatically changed by the use of the refactoring function of the "go please" langage server for the backend and the typescript language server for the front end.
Wihout gong, if one needs to refactor the name of a gongstruct or the name of a field of a gongstruct, the content/database of the application must be refactored by hand.
For instance :
- via a json file
- via the sqlite table/column renaming
- via gorm
With gong, data refactoring is automatic. Gong API provides a Marshall()
function of the staged objects that generates an Unmarshall()
function in go code (a persistance of the repository data as go code)
when refactoring the code, the generated go code is refactored. Therefore, no need to manualy refactor the data.
See gong back-end API for API details.
See gong back-end implementation for implementation details.
If prerequisite and gongc are installed, it is possible to generate a functionning stack in 5 minutes.
In a terminal, below commands :
mkdir
creates ahelloworld
directorymkdir
generates a sub directorygo/models
echo
commands generates 2 go structs in this subdirectoryHello
which stores a way to say helloCountry
which stores a country and an association to the way to say hello in this country
gongc go/models
compiles the models./helloworld
run the server
mkdir helloworld
cd helloworld
mkdir go
mkdir go/models
echo "package models
type Hello struct {
Name string
}" > go/models/hello.go
echo "package models
type Country struct {
Name string
Hello *Hello
AlternateHellos []*Hello
}" > go/models/country.go
gongc go/models
cd go/cmd/helloworld
./helloworld -unmarshallFromCode=stage.go -marshallOnCommit=stage
Then, browse to localhost:8080 and add data manualy.
With the option -marshallOnCommit=stage
, a stage.go
file is generated as each save operation (along the default sqlite database test.db
). When the application is restarted with the --unmarshall=stage
, the data is injected from the stage.go
file, not from the database.
gongc go/models
takes a few minutes the first time it is executed. gongc
can be long the first time it is executed for a stack because it perfoms npm i
which can be long if it is the first time (3'37'' on a macbook pro with a 2,6 GHz 6-Core Intel Core i7).
If gongc
is performed again, it will take a few tens seconds (32'' on a macbook pro with a 2,6 GHz 6-Core Intel Core i7, 1'16'' on a Core I7 windows PC).
The backend of a gong application is a REST server (thanks to gin). You can interact with the server via REST calls.
For instance, if you start from an empty database, the following commands will inject proprer data.
curl --request POST \
--url http://localhost:8080/api/helloworld/go/v1/hellos \
--header 'content-type: application/json' \
--data '{"Name": "Bonjour"}'
curl --request POST \
--url http://localhost:8080/api/helloworld/go/v1/hellos \
--header 'content-type: application/json' \
--data '{"Name": "Bonjorno"}'
curl --request POST \
--url http://localhost:8080/api/helloworld/go/v1/countrys \
--header 'content-type: application/json' \
--data '{"Name": "France","HelloID":{"Int64":1,"Valid":true}}'
curl --request POST \
--url http://localhost:8080/api/helloworld/go/v1/countrys \
--header 'content-type: application/json' \
--data '{"Name": "Italy","HelloID":{"Int64":2,"Valid":true}}'