使用跨多个项目的供应商依赖性构建Go项目

OK, so I have a few projects in Go that all share the same set of structs to represent my database schema. So to avoid code going out of sync, I moved all the structs into their own "models" project. My folder structure looks like this

GOPATH
  - src
    - project1
    - project2
    - models
  - pkg
  - bin

Models have some methods defined to fetch records from the database, but since each project needs to control its own database connection pool, I am passing the db connection to the model methods as an argument, so in models I have something like

import (
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"
)

type User struct {
    ...
}

type UserList []User

func (u *UserList) FetchAll(db *sqlx.DB) {
    ...
}

and in the project code, I have something like

import (
    "github.com/jmoiron/sqlx"
    _ "github.com/lib/pq"

    . "models"
)

func UserListAPI(c *gin.Context) {
    var users UserList
    db := GetDBConnection()
    users.FetchAll(db)
    c.JSON(200, users)
}

But now, when I try to build, I get an error

cannot use db (type *"project1/vendor/github.com/jmoiron/sqlx".DB) as type *"models/vendor/github.com/jmoiron/sqlx".DB in argument to users.FetchAll

Is there any way to resolve this without drastically changing my project structure?

I am using dep to manage my dependencies.

Here is one solution using a src directory:

  1. Set your GOPATH to your project root.
  2. Create a src directory in your project root. All your Go code should live in here.
  3. Within the src directory, create a vendor directory. All your third party packages should live here. Dependency Management tools like GVT will install third party packages directly to this folder for you.

So your project should look like this:

- bin
- pkg
- src
    - project1
    - project2
    - models
    - vendor
        - github.com
            ...

To import third party packages within your code (for example from github.com), you would just use the github path directly, like import "github.com/foo/bar". The Go compiler will use your GOPATH to determine that github.com/foo/bar actually lives at $GOPATH/src/vendor/github.com/foo/bar.

To import local packages, just use your local path directly, like import "models". The Go compiler will use your GOPATH to determine that models actually lives at $GOPATH/src/models.


Update

Based on our conversation below, you have two options:

  1. Consolidate the dependencies of all your projects into one vendor folder located at $GOPATH/src/vendor.
  2. Use isolated projects and toggle your GOPATH to a new project root based on the project you're currently working on.

I would go with approach #2 to keep things clean and independent.

To achieve #2, you can create a structure like this:

- project1
    - bin
    - pkg
    - src
        - vendor
- project2
    - bin
    - pkg
    - src
        - vendor

To work on project1, set GOPATH to the root of project1. Same goes for project2 and any other projects you have.

Everything else I said above should still apply, because these projects still follow the same structure individually, but now we've also fulfilled the requirement of having a unique vendor directory for each project.

I know it's not "seamless" because you'll have to toggle your GOPATH, but I hardly think that is a big deal because you're only ever going to do that once in a blue moon, and it takes half a second. Plus, Golang was not made specifically to cater to such project structure requirements, though it is flexible enough to get it working without much hassle (all you have to do is toggle GOPATH).

You can even automate this further by writing aliases that change your GOPATH for you for certain projects. For instance, to switch to project1 really fast you can create an alias project1 that runs the command export GOPATH=/path/to/project1.


Update 2

Here is how I structure a personal project of mine, which achieves everything you're trying to achieve:

- bin
- pkg
- src
    - vendor
    - core
    - apps
        - foo.com
        - api.foo.com
        - auth.foo.com

As you can see, in the apps folder I have multiple "projects" (though in reality these are just different binaries/HTTP servers).

You'll also see that I have a core directory. This is where all my shared logic lives. Each project in the apps folder can import from core with no problems since they're all within the same GOPATH namespace.