GO应用程序和mongodb的清洁和通用项目结构

时间:2015-12-28 19:00:38

标签: go architecture restful-architecture

我想使用GO和MongoDB构建基于API的应用程序。我来自Asp.net MVC背景。可能如果我使用MVC Web应用程序构建一个需要考虑的东西

  1. 关注点分离(SoC)

    • 的DataModel
    • BusinessEntities
    • BusinessServices
    • 控制器
  2. Dependeny注射和工作统一

  3. 单元测试
    • MoQ或nUnit
  4. 与UI框架集成
    • Angularjs或其他人
  5. 支持SEO的RESTful网址
  6. 以下架构可以解决我在基于MVC的应用程序中的需求

    enter image description here

    Web上有资源来构建基于Asp.Net或Java的应用程序,但我还没有找到Golang应用程序架构的解决方案。

    是GO与C#或Java不同,但仍有Structs,Interfaces用于创建可重用代码和通用应用程序架构。 考虑以上几点,我们如何在GO应用程序中创建一个干净且可重用的项目结构,以及如何在DB(Mongodb)事务中创建一个通用的存储库。任何网络资源也是一个很好的起点。

5 个答案:

答案 0 :(得分:11)

这取决于你自己的风格和规则,在我的公司,我们以这种方式开发项目:

  • 配置由环境变量决定,因此我们有一个company/envs/project.sh文件,必须在服务之前进行评估(在图片中的项目之外)。
  • 我们添加了一个zscripts文件夹,其中包含所有额外脚本,例如添加用户或发布帖子。仅用于调试建议。
  • 数据模型(实体)位于名为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")
}

<强>模块

模块是与其他模块隔离的视图和控制器的容器,使用我们的配置,我建议不要在模块之间创建依赖关系。模块也称为应用程序。

每个模块使用路由器,子路由器或框架提供的路由配置其路由,例如(文件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.
}

最后,您在main(或测试中)配置应用程序:

e := echo.New()
dogs.Configure(e)
// more apps

e.Run(":8080")

注意:对于视图,您可以将它们添加到project/apps/<name>/views文件夹,并使用相同的功能对它们进行配置。

其他

有时我们还会添加project/constantsproject/utils个包。

这是它的样子:

Example of project structure

请注意,在上面的示例中,templates与应用程序分开,因为它的占位符目录为空。

希望它有用。来自墨西哥的问候:D。

答案 1 :(得分:4)

我过去一直在努力构建如何构建我的Go Web API,并且不知道任何可以告诉您如何编写Go Web API的Web资源。

我所做的只是查看Github上的其他项目,并尝试他们如何构建代码,例如,Docker repo在其API上有非常独特的Go代码。

此外,Beego是一个RESTful框架,它以MVC方式为您生成项目结构,并且根据docs,它也可以用于API。

答案 2 :(得分:3)

我一直在golang中构建一个Web API。

你必须做一些研究,但我可以给你一些起点:

  1. Building Web Apps with Go - 电子书
  2. github.com/julienschmidt/httprouter - 用于路由地址
  3. github.com/unrolled/render/ - 用于呈现各种形式的回复(JSON,HTML等)。
  4. github.com/dgrijalva/jwt-go - JSON网络代币
  5. www.gorillatoolkit.org/pkg/sessions - 会话管理
  6. 关于最终如何协同工作的参考:

    Go Web API Repo - 个人项目

答案 3 :(得分:3)

在我看来生产服务器上的Go webapp项目文件夹看起来就像你的图片简单得多。资产结构没有什么特别之处 - 静态,模板,内容,样式,Img,JSlibs,DBscripts等常用文件夹。 WebAPI没有什么特别之处 - 像往常一样,您设计哪个URI将响应所需的功能并相应地将请求路由到处理程序。一些细节 - 许多地鼠不相信MVC架构,它肯定取决于你。并且您部署一个静态链接的可执行文件而没有依赖项。在您的开发环境中,您可以像在stdlib中那样在$ GOPATH中构建您的和导入/销售的sorce文件,但在生产环境中只部署一个可执行文件,当然需要静态资产。您可以在stdlib中看到如何对Go源包进行orginize。只有一个可执行文件,你会在生产中构建什么?

答案 4 :(得分:3)

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

备注:

  1. 在main之外的每个包中,我都有一个Setup()函数(带有相关参数),由主包调用。
  2. 对于apis文件夹下的软件包,它们通常只是初始化外部Go库。您还可以直接将现有库导入到处理程序/模型中,而无需apis包。
  3. 我将mux设置为handlers包中的导出全局,如下所示...

    Router := mux.NewRouter()
    

    ...然后为每个URL创建一个文件(具有相同URL的不同方法在同一个文件中)。在每个文件中,我使用Go的init()函数,该函数在全局变量初始化之后运行(因此使用路由器是安全的)但是在运行main()之前(因此对于main来说,假设所有内容都是安全的)已设置)。关于init()的好处是你可以在一个包中拥有任意数量的方法,因此在导入包时它们会自动运行。

    Main然后导入myprojectname/handlers,然后在main中提供handlers.Router

  4. 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。支持SEO的RESTful URL

    再说一次,我在这里帮不了你。这取决于您:发送正确的状态代码(不要发送带有404页面的200,因为您将停靠重复页面),不要复制页面(注意google.com/something vs {{1}希望你的框架不会弄乱这个),不要试图欺骗搜索引擎,等等。