It depends on your own style and rules, in my company, we develop our projects this way:
- Configuration is determined by environment variables, so we have a
company/envs/project.sh
file which has to be evaluated before service (outside the project in the image).
- We add a
zscripts
folder that contains all extra scripts, like adding users or publishing a post. Intended to be used only for debug proposes.
- Data models (entities) are in a package called
project/models
.
- All controllers and views (HTML templates) are categorized into "apps" or "modules". We use the REST path as main group delimiter, so path
/dogs
goes to package project/apps/dogs
and /cats
to project/apps/cats
.
- Managers are in their separated package at project's root
project/manager
.
- Static files (.css, .png, .js, etc.) are located at
project/static/[app/]
. Sometimes is required to have the optional [app/]
folder, but it only happens when two apps have dashboards or conflicting file names. Most of cases you won't need to use [app/]
for static resources.
Managers
We call a manager, a package that contains pure functions which helps apps to perform its task, for example, databases, cache, S3 storage, etc. We initialize each manager calling package.Startup()
before we start to listen, and finalize calling package.Finalize()
when program is interrupted.
An example of a manager could be project/cache/cache.go
:
type Config struct {
RedisURL string `envconfig:"redis_url"`
}
var config Config
var client *redis.Client
func Startup(c Config) error {
config = c
client, err := redis.Dial(c.RedisURL)
return err
}
func Set(k,v string) error {
return client.Set(k, v)
}
in main.go (or your_thing_test.go):
var spec cache.Config
envconfig.Process("project", &spec)
cache.Startup(spec)
And in a app (or module):
func SetCacheHandler(_ http.ResponseWriter, _ *http.Request){
cache.Set("this", "rocks")
}
Modules
A module is a container of views and controllers that are isolated from other modules, using our configuration I would recommend to not create dependencies between modules. Modules are also called apps.
Each module configures its routes using a router, sub-router or what your framework provides, for example (file project/apps/dogs/configure.go
):
func Configure(e *echo.Echo) {
e.Get("/dogs", List)
}
Then, all handlers live in project/apps/dogs/handlers.go
:
// List outputs a dog list of all stored specimen.
func List(c *echo.Context) error {
// Note the use of models.Xyz
var res := make([]models.Dog, 0) // A little trick to not return nil.
err := store.FindAll("dogs", nil, &res) // Call manager to find all dogs.
// handle error ...
return c.JSON(200, res) // Output the dogs.
}
Finally you configure the app in main (or in a test):
e := echo.New()
dogs.Configure(e)
// more apps
e.Run(":8080")
Note: for views, you can add them to project/apps/<name>/views
folder and configure them the using the same function.
Other
Sometimes we also add a project/constants
and a project/utils
package.
Here is what it looks like:
Note that in above sample, templates
are separated from apps, thats because its a placeholder, directory is empty.
Hope it was useful. Greetings from México :D.