diff --git a/cmd/helpers/projectHelper.go b/cmd/helpers/projectHelper.go index affa91b..bfc1e77 100644 --- a/cmd/helpers/projectHelper.go +++ b/cmd/helpers/projectHelper.go @@ -28,6 +28,7 @@ func (h *projectHelper) InitProject(project models.Project) error { fileConfigs := []models.FileConfig{ {TemplatePath: "templates/gitignore.tmpl", Destination: "/.gitignore"}, {TemplatePath: "templates/Makefile.tmpl", Destination: "/Makefile"}, + {TemplatePath: "templates/README.md", Destination: "/README.md"}, {TemplatePath: "templates/go.tmpl", Destination: "/go.mod"}, {TemplatePath: "templates/main.tmpl", Destination: "/main.go"}, {TemplatePath: "templates/editorconfig.tmpl", Destination: "/.editorconfig"}, diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..f62ed3c --- /dev/null +++ b/templates/README.md @@ -0,0 +1,126 @@ +# 🧭 Project Structure Documentation + +This Go project was scaffolded using [`gozen`](https://github.com/tech-thinker/gozen). It follows a clean and modular architecture suited for microservices and REST/gRPC APIs. + +## 🗂️ Root-Level Files and Directories + +| Path | Description | +|-----------------------------|-------------------------------------------------------------------------| +| `main.go` | Entry point that initializes and runs the application. | +| `go.mod`, `go.sum` | Go modules files for dependency management. | +| `Makefile` | Contains commands to build, run, and manage the project. | +| `gozen.json` | GoZen metadata and configuration file. | +| `docker-compose*.yml` | Docker Compose files for different environments (dev, debug, prod). | +| `Dockerfile*` | Docker configurations for building images in various contexts. | +| `modd-*.conf` | Modd configs for live reloading. | +| `.env`, `.env.example` | Environment variables configuration files. | +| `.gitignore` | Git ignore file for project-specific files and directories. | +| `.editorconfig` | Editor configuration file for consistent coding styles. | + +### Environment Variables +| Environment | Description | +|-----------------------------|-------------------------------------------------------------------------| +| VERSION | Application version. | +| APP_NAME | Application name. | +| APP_ENV | Environment name (development, staging, production). | +| API_PORT | Port number for the rest api to listen on. | +| GRPC_PORT | Port number for the gRPC api to listen on. | +| DB_DRIVER | Database driver (mysql, postgres, sqlite etc.). | +| DB_HOST | Database host address. | +| DB_PORT | Database port number. | +| DB_USER | Database username. | +| DB_PASS | Database password. | +| DB_NAME | Database name. | + + +## 📦 Application Structure + +### `app/` +Main application logic divided into REST and gRPC interfaces. + +#### `app/init.go` +- Main service registry for the application. + + +### `app/rest/` +Handles REST API endpoints. + +| Path | Description | +|-----------------------------|-------------------------------------------------| +| `controllers/health.go` | Health check handler implementation (REST). | +| `router/router.go` | REST route registration. | + + +### `app/grpc/` +Handles gRPC endpoints and server setup. + +| Path | Description | +|---------------------------------|--------------------------------------------------| +| `handlers/health.go` | Health check handler implementation (gRPC). | +| `proto/health.proto` | Protobuf definitions for health service. | +| `proto/*.pb.go` | Generated Go code from `.proto`. | +| `router/router.go` | gRPC server and service registration. | + + +## ⚙️ Configuration and Constants + +### `config/config.go` +- Centralized configuration handling environments. + +### `constants/app.go` +- Defines constants used throughout the application. + + +## 🧱 Core Domain Layers + +### `models/health.go` +- Domain model for the health resource. + +### `repository/health.go` +- Database or external service access logic for health module. + +### `service/health.go` +- Business logic layer for the health service. + + +## 🧰 Supporting Infrastructure + +### `instance/` + +| Path | Description | +|------------------------------|------------------------------------------------------------------| +| `instance.go` | Initializes shared instances (DB, cache, etc). | +| `registry/models.go` | Structures for model registry or instance metadata. | + + +### `logger/logger.go` +- Logger setup and utilities (e.g., wrapper for Zap or Logrus). + + +### `runner/` + +| Path | Description | +|-----------------|------------------------------------| +| `api.go` | Initializes and runs REST server. | +| `grpc.go` | Initializes and runs gRPC server. | + + +### `utils/utils.go` +- General-purpose utility functions. + + +## 🐳 Docker Support + +| Path | Description | +|------------------------|---------------------------------------------------| +| `Dockerfile*` | Various Dockerfiles for dev/debug/production. | +| `modd-*.conf` | Live-reload configs using `modd` utility. | + + +## ✅ Summary + +This project structure promotes: + +- **Separation of Concerns**: Clear division between transport, logic, and infrastructure. +- **Scalability**: Easy to add new modules or services. +- **Maintainability**: Decoupled layers and modular design. diff --git a/templates/config/config.tmpl b/templates/config/config.tmpl index 2a42e72..022d356 100644 --- a/templates/config/config.tmpl +++ b/templates/config/config.tmpl @@ -5,6 +5,7 @@ import "github.com/spf13/viper" type Configuration interface { Version() string AppName() string + AppEnv() string APIPort() string GrpcPort() string @@ -18,6 +19,7 @@ type Configuration interface { type configuration struct { version string appName string + appEnv string apiPort string grpcPort string @@ -39,6 +41,11 @@ func (cfg *configuration) AppName() string { return cfg.appName } +// AppEnv returns AppEnv +func (cfg *configuration) AppEnv() string { + return cfg.appEnv +} + // APIPort returns port func (cfg *configuration) APIPort() string { return cfg.apiPort @@ -87,10 +94,11 @@ func Init( env.AutomaticEnv() config.version = env.GetString("version") - config.apiPort = env.GetString("app_name") + config.appName = env.GetString("app_name") + config.appEnv = env.GetString("app_env") config.apiPort = env.GetString("api_port") config.grpcPort = env.GetString("grpc_port") - + config.db_driver = env.GetString("db_driver") config.db_host = env.GetString("db_host") config.db_port = env.GetString("db_port") diff --git a/templates/constants/app.tmpl b/templates/constants/app.tmpl index 3fe819e..e30a034 100644 --- a/templates/constants/app.tmpl +++ b/templates/constants/app.tmpl @@ -1,5 +1,11 @@ package constants +const ( + APP_ENV_DEVELOPMENT = "development" + APP_ENV_STAGING = "staging" + APP_ENV_PRODUCTION = "production" +) + const ( DbDriverSQLite = "sqlite" DbDriverMySQL = "mysql" diff --git a/templates/env.sample.tmpl b/templates/env.sample.tmpl index 9ee0249..7ad430b 100644 --- a/templates/env.sample.tmpl +++ b/templates/env.sample.tmpl @@ -1,5 +1,6 @@ VERSION=1.0.0 APP_NAME={{.AppName}} +APP_ENV=development API_PORT=3000 GRPC_PORT=3001 diff --git a/templates/instance/instance.tmpl b/templates/instance/instance.tmpl index 2309144..735d131 100644 --- a/templates/instance/instance.tmpl +++ b/templates/instance/instance.tmpl @@ -37,14 +37,16 @@ func Init(cfg config.Configuration) Instance { instance := &instance{} var conn gorm.Dialector - if cfg.DbDriver() == constants.DbDriverSQLite { - conn = sqlite.Open(cfg.DbName()) - } else if cfg.DbDriver() == constants.DbDriverMySQL { + if cfg.DbDriver() == constants.DbDriverMySQL { url := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&parseTime=True&loc=Local", cfg.DbUser(), cfg.DbPass(), cfg.DbHost(), cfg.DbPort(), cfg.DbName()) conn = mysql.Open(url) } else if cfg.DbDriver() == constants.DbDriverPostgres { url := fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Jakarta", cfg.DbHost(), cfg.DbUser(), cfg.DbPass(), cfg.DbName(), cfg.DbPort()) conn = postgres.Open(url) + } else if cfg.DbDriver() == constants.DbDriverSQLite { + conn = sqlite.Open(cfg.DbName()) + } else { + log.Fatal("unsupported database driver. use [mysql, postgres or sqlite] instead.") } gormDB, err := gorm.Open(conn, &gorm.Config{}) if err != nil { diff --git a/templates/runner/grpc.tmpl b/templates/runner/grpc.tmpl index 366255e..041769b 100644 --- a/templates/runner/grpc.tmpl +++ b/templates/runner/grpc.tmpl @@ -12,8 +12,10 @@ import ( "{{.PackageName}}/app" "{{.PackageName}}/app/grpc/router" "{{.PackageName}}/config" + "{{.PackageName}}/constants" "{{.PackageName}}/instance" "{{.PackageName}}/logger" + "google.golang.org/grpc/reflection" ) // GrpcRunner is the interface for rest grpcRunner runner @@ -39,6 +41,9 @@ func (runner *grpcRunner) Go(ctx context.Context, wg *sync.WaitGroup) { } routerV1 := router.Init(svc) + if runner.cfg.AppEnv() == constants.APP_ENV_DEVELOPMENT { + reflection.Register(routerV1) + } // Channel to listen for interrupt signals stopChan := make(chan os.Signal, 1)