包内组织的Golang数据库层

I would like to hear some advice from Golang gurus. Imagine I have a package for database layer with many table which I should define structs and functions for them. As an example, I have a table called "MyObject" and define a struct for it.

type MyObject struct {
    RecID       int
    Name        string
}

Is it convenient for functions related to this table/struct I define receiver like this:

func (o MyObject) AllMyObjects() ([]MyObject, error) {
    // retreive all MyObjects by query
}


func (o *MyObject) OneMyObjects() (MyObject, error) {
    // retreive one MyObject by query
}

In this way as my db package get bigger by structs and functions, it will look cleaner and easier to call methods like this (although I should define 1 extra line):

var myobject MyObject
var myobjects,err = myobject.AllMyObjects()

rather than calling functions directly on package level (db is name of my package)

var myobjects,err = db.AllMyObjects()

Is this Go way of organizing code or do you have other advice for this?

I like Ben Johnson's method explained here https://medium.com/@benbjohnson/standard-package-layout-7cdbc8391fc1

The idea is to have your domain objects in your base package, then wrap the database/nosql/redis/whatever persistence layer as services in subpackages. When I do this, I end up with this sort of thing:

  • myobject.go (defines MyObbject struct, and MyObjectService interface, has things like maybe 'Get, Update')
  • mysql/
    • myobject_service.go (implements MyObjectService for mysql persistence)
  • redis/
    • myobject_service.go (implementation for redis)

this allows you to pass around MyObjectService in your business logic and you don't even care how the persistence actually happens. A mysql.MyObjectService concrete struct tends to hold things like a pointer to the database connection. A cache package may store things in memory. Etc.

So in other words, don't base your struct slavishly to the database, locking yourself into a particular implementation. Separate the business code logic from the act of persisting, and use interfaces to allow a consistent pattern to accessing persisted data.

If you think about how this is used, this is an odd way to organize your code. Methods are behaviors of their receiver type. Is MyObject's job to retrieve other MyObjects from the database? That doesn't sound right. That should be the job of something that connects to the database. That could be package functions, or it could be an object that wraps the database connection or database session; each could be a valid approach depending on your application, database, and testing needs (package functions are much harder to test with because they can't be mocked).

For example, if your model types are Foo and Bar, your database package might have types FooStore and BarStore, with each having methods to get one by ID, get all, insert one, update one, and so on (whatever the needs are for each type).

It's often important to keep the database code separated from the data model, so that packages that work with the model don't have to also import the DBAL, which helps with separation of concerns.

Great question. Usually, regardless of which language used in the Project, I try to apply the MVC pattern, in which this database's method should be separated from the Models.

enter image description here

I would create another file called your_model_controller.go and give all the persistence responsibilities to this file

Introduction about MVC