GO 应用程序和 mongodb 的简洁通用项目结构
Clean and generic project structure for GO applications and mongodb
我想使用 GO 和 MongoDB 构建一个基于 API 的应用程序。我来自 Asp.net MVC 背景。可能如果我用 MVC web 应用程序构建架构,需要考虑的事情是
关注点分离(SoC)
- 数据模型
- 商业实体
- 商业服务
- 控制器
依赖注入和工作统一
- 单元测试
- MoQ 或 nUnit
- 与 UI 框架集成
- Angularjs 或其他
- RESTful 启用 SEO 的网址
以下架构可能是满足我在基于 MVC 的应用程序中的需求的解决方案
网络上有构建基于 Asp.Net 或 Java 的应用程序的资源,但我还没有找到 Golang 应用程序架构的解决方案。
是的,GO 与 C# 或 Java 不同,但仍然有用于创建可重用代码的结构、接口和通用应用程序架构。
考虑以上几点,我们如何在 GO 应用程序和 DB(Mongodb) 事务的通用存储库中创建一个干净且可重用的项目结构。任何网络资源也是一个很好的起点。
我过去也曾为如何构建我的 Go web API 而苦苦挣扎,并且不知道任何可以准确告诉您如何编写 Go web API 的网络资源。
我所做的只是查看 Github 上的其他项目并尝试他们如何构建代码,例如,Docker 存储库中有非常惯用的 Go 代码 API.
另外,Beego is a RESTful framework that generates the project structure for you in a MVC way and according to their docs,也可以用于APIs。
我在 golang 中构建 Web API 已经有一段时间了。
你需要做一些研究,但我可以给你一些起点:
- Building Web Apps with Go -- 电子书
- github.com/julienschmidt/httprouter -- 用于路由地址
- github.com/unrolled/render/ -- 用于呈现各种形式的响应(JSON、HTML 等..)
- github.com/dgrijalva/jwt-go -- JSON 网络令牌
- www.gorillatoolkit.org/pkg/sessions -- 会话管理
以及一些东西最终如何协同工作的参考:
Go Web API Repo -- 个人项目
在我看来,生产服务器上的 Go webapp 项目文件夹在您的图片上看起来更简单。资产结构没有什么特别之处——静态、模板、内容、样式、Img、JSlibs、DBscripts 等常用文件夹。 WebAPI 中没有什么特别之处——像往常一样,您设计哪个 URI 将响应所需的功能并将请求相应地路由到处理程序。一些细节——许多 gophers 不相信 MVC 架构,这当然取决于你。并且您部署了一个没有依赖关系的静态链接可执行文件。在您的开发环境中,您可以像在 stdlib 中一样在 $GOPATH 中构建您的文件和 imported/vendored 源文件,但在生产环境中只部署一个可执行文件,确保需要静态资产。你可以看到如何在 stdlib 中组织 Go 源包。只有一个可执行文件,您会在生产环境中构造什么?
看你自己的风格和规则,在我公司,我们是这样开发项目的:
- 配置由环境变量决定,所以我们有一个
company/envs/project.sh
文件,必须在 服务(在图像中的项目之外)之前对其进行评估。
- 我们添加了一个
zscripts
文件夹,其中包含所有额外的脚本,例如添加用户或发布 post。仅用于调试建议。
- 数据模型(实体)位于名为
project/models
的包中。
- 所有控制器和视图(HTML 模板)被分类为 "apps" 或 "modules"。我们使用 REST 路径作为主组分隔符,因此路径
/dogs
转到包 project/apps/dogs
和 /cats
到 project/apps/cats
.
- 管理器位于项目根目录下的单独包中
project/manager
。
- 静态文件(.css、.png、.js 等)位于
project/static/[app/]
。有时需要有可选的 [app/]
文件夹,但只有当两个应用程序具有仪表板或文件名冲突时才会发生。大多数情况下,您不需要对静态资源使用 [app/]
。
经理
我们调用一个管理器,一个包含帮助应用程序执行其任务的纯函数的包,例如数据库、缓存、S3 存储等。我们在开始之前调用 package.Startup()
初始化每个管理器监听,并在程序中断时完成调用package.Finalize()
。
经理的例子可以是 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)
}
在main.go(或your_thing_test.go)中:
var spec cache.Config
envconfig.Process("project", &spec)
cache.Startup(spec)
并且在应用程序(或模块)中:
func SetCacheHandler(_ http.ResponseWriter, _ *http.Request){
cache.Set("this", "rocks")
}
模块
模块是与其他模块隔离的视图和控制器的容器,使用我们的配置我建议不要在模块之间创建依赖关系。模块也称为应用程序。
每个模块使用路由器配置其路由,sub-router 或您的框架提供的内容,例如(文件 project/apps/dogs/configure.go
):
func Configure(e *echo.Echo) {
e.Get("/dogs", List)
}
然后,所有处理程序都位于 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.
}
最后在主程序(或测试程序)中配置应用程序:
e := echo.New()
dogs.Configure(e)
// more apps
e.Run(":8080")
注意:对于视图,您可以将它们添加到 project/apps/<name>/views
文件夹并使用相同的功能配置它们。
其他
有时我们还会添加一个 project/constants
和一个 project/utils
包。
这是它的样子:
请注意,在上面的示例中,templates
与应用程序分开,那是因为它是一个占位符,目录是空的。
希望有用。来自墨西哥的问候 :D.
1。关注点分离 (SoC)
我没有直接接触过SoC,但我有自己的模式。您可以适应任何模式(MVC、您自己的等)。
在我的代码中,我将代码分成不同的包:
myprojectname (package main) — Holds the very basic setup and configuration/project consts
* handlers (package handlers) — Holds the code that does the raw HTTP work
* models (package models) — Holds the models
* apis (NOT a package)
- redis (package redis) — Holds the code that wraps a `sync.Pool`
- twilio (package twilio) — Example of layer to deal with external API
备注:
- 在除 main 之外的每个包中,我都有一个由 main 包调用的
Setup()
函数(带有相关参数)。
- 对于
apis
文件夹下的包,往往只是初始化外部Go库。您也可以直接将现有库导入 handlers/models 而无需 apis
包。
我像这样在 handlers
包中将我的多路复用器设置为导出的全局...
Router := mux.NewRouter()
...然后为每个URL创建一个文件(具有相同URL的不同方法在同一个文件中)。在每个文件中,我使用 Go 的 init()
函数,它是 运行 在全局变量初始化之后(所以使用路由器是安全的)但在 main()
之前是 运行 (所以main 假设一切都已设置是安全的)。 init()
的伟大之处在于,您可以在一个包中拥有任意数量的这些方法,因此在导入包时它们会自动获得 运行。
Main 然后导入 myprojectname/handlers
,然后在 main.
中提供 handlers.Router
2。依赖注入和工作统一
我没有使用过 Unity of Work,所以我不知道可能的 Go 实现。
对于 DI,我构建了一个真实对象和模拟对象都将实现的接口。
在包中,我将其添加到根目录中:
var DatabaseController DatabaseControllerInterface = DefaultController
然后,我可以将每个测试的第一行 DatabaseController
更改为该测试需要的任何内容。不测试时,单元测试不应该是运行,默认是DefaultController
.
3。单元测试
Go 使用 go test package
命令提供内置测试。您还可以使用 go test --cover
来发出覆盖百分比。你甚至可以 have coverage displayed in your browser, highlighting the parts that are/aren't covered.
我使用 testify/assert 包来帮助我测试标准库的不足之处:
// something_test.go
//
// The _test signifies it should only be compiled into a test
// Name the file whatever you want, but if it's testing code
// in a single file, I like to do filename_test.go.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMath(t *testing.T) {
assert.Equal(t, 3+1, 4)
}
4。与 UI 框架集成
我还没有看到 Angular。虽然没用过,但是Go好用template engine built into the standard lib.
5。 RESTful URL 启用 SEO
同样,我在这里帮不了你。这取决于您:发送正确的状态代码(不要发送带有 404 页面的 200,因为您会因重复页面而停靠),不要重复页面(注意 google.com/something
与 google.com/something/
; 希望你的框架不会搞砸),不要试图欺骗搜索引擎,等等。
我想使用 GO 和 MongoDB 构建一个基于 API 的应用程序。我来自 Asp.net MVC 背景。可能如果我用 MVC web 应用程序构建架构,需要考虑的事情是
关注点分离(SoC)
- 数据模型
- 商业实体
- 商业服务
- 控制器
依赖注入和工作统一
- 单元测试
- MoQ 或 nUnit
- 与 UI 框架集成
- Angularjs 或其他
- RESTful 启用 SEO 的网址
以下架构可能是满足我在基于 MVC 的应用程序中的需求的解决方案
网络上有构建基于 Asp.Net 或 Java 的应用程序的资源,但我还没有找到 Golang 应用程序架构的解决方案。
是的,GO 与 C# 或 Java 不同,但仍然有用于创建可重用代码的结构、接口和通用应用程序架构。 考虑以上几点,我们如何在 GO 应用程序和 DB(Mongodb) 事务的通用存储库中创建一个干净且可重用的项目结构。任何网络资源也是一个很好的起点。
我过去也曾为如何构建我的 Go web API 而苦苦挣扎,并且不知道任何可以准确告诉您如何编写 Go web API 的网络资源。
我所做的只是查看 Github 上的其他项目并尝试他们如何构建代码,例如,Docker 存储库中有非常惯用的 Go 代码 API.
另外,Beego is a RESTful framework that generates the project structure for you in a MVC way and according to their docs,也可以用于APIs。
我在 golang 中构建 Web API 已经有一段时间了。
你需要做一些研究,但我可以给你一些起点:
- Building Web Apps with Go -- 电子书
- github.com/julienschmidt/httprouter -- 用于路由地址
- github.com/unrolled/render/ -- 用于呈现各种形式的响应(JSON、HTML 等..)
- github.com/dgrijalva/jwt-go -- JSON 网络令牌
- www.gorillatoolkit.org/pkg/sessions -- 会话管理
以及一些东西最终如何协同工作的参考:
Go Web API Repo -- 个人项目
在我看来,生产服务器上的 Go webapp 项目文件夹在您的图片上看起来更简单。资产结构没有什么特别之处——静态、模板、内容、样式、Img、JSlibs、DBscripts 等常用文件夹。 WebAPI 中没有什么特别之处——像往常一样,您设计哪个 URI 将响应所需的功能并将请求相应地路由到处理程序。一些细节——许多 gophers 不相信 MVC 架构,这当然取决于你。并且您部署了一个没有依赖关系的静态链接可执行文件。在您的开发环境中,您可以像在 stdlib 中一样在 $GOPATH 中构建您的文件和 imported/vendored 源文件,但在生产环境中只部署一个可执行文件,确保需要静态资产。你可以看到如何在 stdlib 中组织 Go 源包。只有一个可执行文件,您会在生产环境中构造什么?
看你自己的风格和规则,在我公司,我们是这样开发项目的:
- 配置由环境变量决定,所以我们有一个
company/envs/project.sh
文件,必须在 服务(在图像中的项目之外)之前对其进行评估。 - 我们添加了一个
zscripts
文件夹,其中包含所有额外的脚本,例如添加用户或发布 post。仅用于调试建议。 - 数据模型(实体)位于名为
project/models
的包中。 - 所有控制器和视图(HTML 模板)被分类为 "apps" 或 "modules"。我们使用 REST 路径作为主组分隔符,因此路径
/dogs
转到包project/apps/dogs
和/cats
到project/apps/cats
. - 管理器位于项目根目录下的单独包中
project/manager
。 - 静态文件(.css、.png、.js 等)位于
project/static/[app/]
。有时需要有可选的[app/]
文件夹,但只有当两个应用程序具有仪表板或文件名冲突时才会发生。大多数情况下,您不需要对静态资源使用[app/]
。
经理
我们调用一个管理器,一个包含帮助应用程序执行其任务的纯函数的包,例如数据库、缓存、S3 存储等。我们在开始之前调用 package.Startup()
初始化每个管理器监听,并在程序中断时完成调用package.Finalize()
。
经理的例子可以是 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)
}
在main.go(或your_thing_test.go)中:
var spec cache.Config
envconfig.Process("project", &spec)
cache.Startup(spec)
并且在应用程序(或模块)中:
func SetCacheHandler(_ http.ResponseWriter, _ *http.Request){
cache.Set("this", "rocks")
}
模块
模块是与其他模块隔离的视图和控制器的容器,使用我们的配置我建议不要在模块之间创建依赖关系。模块也称为应用程序。
每个模块使用路由器配置其路由,sub-router 或您的框架提供的内容,例如(文件 project/apps/dogs/configure.go
):
func Configure(e *echo.Echo) {
e.Get("/dogs", List)
}
然后,所有处理程序都位于 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.
}
最后在主程序(或测试程序)中配置应用程序:
e := echo.New()
dogs.Configure(e)
// more apps
e.Run(":8080")
注意:对于视图,您可以将它们添加到 project/apps/<name>/views
文件夹并使用相同的功能配置它们。
其他
有时我们还会添加一个 project/constants
和一个 project/utils
包。
这是它的样子:
请注意,在上面的示例中,templates
与应用程序分开,那是因为它是一个占位符,目录是空的。
希望有用。来自墨西哥的问候 :D.
1。关注点分离 (SoC)
我没有直接接触过SoC,但我有自己的模式。您可以适应任何模式(MVC、您自己的等)。
在我的代码中,我将代码分成不同的包:
myprojectname (package main) — Holds the very basic setup and configuration/project consts
* handlers (package handlers) — Holds the code that does the raw HTTP work
* models (package models) — Holds the models
* apis (NOT a package)
- redis (package redis) — Holds the code that wraps a `sync.Pool`
- twilio (package twilio) — Example of layer to deal with external API
备注:
- 在除 main 之外的每个包中,我都有一个由 main 包调用的
Setup()
函数(带有相关参数)。 - 对于
apis
文件夹下的包,往往只是初始化外部Go库。您也可以直接将现有库导入 handlers/models 而无需apis
包。 我像这样在
handlers
包中将我的多路复用器设置为导出的全局...Router := mux.NewRouter()
...然后为每个URL创建一个文件(具有相同URL的不同方法在同一个文件中)。在每个文件中,我使用 Go 的
init()
函数,它是 运行 在全局变量初始化之后(所以使用路由器是安全的)但在main()
之前是 运行 (所以main 假设一切都已设置是安全的)。init()
的伟大之处在于,您可以在一个包中拥有任意数量的这些方法,因此在导入包时它们会自动获得 运行。Main 然后导入
myprojectname/handlers
,然后在 main. 中提供
handlers.Router
2。依赖注入和工作统一
我没有使用过 Unity of Work,所以我不知道可能的 Go 实现。
对于 DI,我构建了一个真实对象和模拟对象都将实现的接口。
在包中,我将其添加到根目录中:
var DatabaseController DatabaseControllerInterface = DefaultController
然后,我可以将每个测试的第一行 DatabaseController
更改为该测试需要的任何内容。不测试时,单元测试不应该是运行,默认是DefaultController
.
3。单元测试
Go 使用 go test package
命令提供内置测试。您还可以使用 go test --cover
来发出覆盖百分比。你甚至可以 have coverage displayed in your browser, highlighting the parts that are/aren't covered.
我使用 testify/assert 包来帮助我测试标准库的不足之处:
// something_test.go
//
// The _test signifies it should only be compiled into a test
// Name the file whatever you want, but if it's testing code
// in a single file, I like to do filename_test.go.
package main
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMath(t *testing.T) {
assert.Equal(t, 3+1, 4)
}
4。与 UI 框架集成
我还没有看到 Angular。虽然没用过,但是Go好用template engine built into the standard lib.
5。 RESTful URL 启用 SEO
同样,我在这里帮不了你。这取决于您:发送正确的状态代码(不要发送带有 404 页面的 200,因为您会因重复页面而停靠),不要重复页面(注意 google.com/something
与 google.com/something/
; 希望你的框架不会搞砸),不要试图欺骗搜索引擎,等等。