<p>I'm trying to organize the code base. The structure I had in my mind is like:</p>
<pre><code>project/
...
database/
database.go
model/
models.go
...
...
</code></pre>
<p>database.go</p>
<pre><code>import (
"database/sql"
"github.com/lib/pq"
)
type Database struct {
database *sql.DB
}
var db *Database
func connectDB() {
...
dbObject, err := sql.Open("postgres", dbinfo)
...
db = &Database{database: dbObject}
}
func GetDatabase() *Database {
return db
}
</code></pre>
<p>models.go</p>
<pre><code>import "project/database"
func getUser(db database.Database, ...) (user User, err error) {
query := "SELECT * FROM ..."
err = db.QueryRow(query, ...).Scan(...)
if err != nil {
return user, err
}
return user, err
}
</code></pre>
<p>routes.go</p>
<pre><code>func getUser(c *gin.Context) {
db := c.MustGet(DB).(Database) // Gets from middleware calling GetDatabase()
...
user, err := getUser(db, id)
...
}
</code></pre>
<p>The problem here is in models.go it is not possible to <code>db.Query</code> since <code>Database</code> doesn't implement that function. Instead of <code>Database</code> I can create a <code>*sql.DB</code> object but then I can't abstract the <code>sql.DB</code> inside models.go. It will be like this <code>getUser(db *sql.DB, ...)</code>. I checked some opensource codes but couldn't find a good structure to abstract the database operations which uses SQL. Any advices? </p>
<p>You can also give advices/examples which have completely different structures than the structure I described which does the abstraction</p>
<p>PS: I don't use any ORM</p>
<hr/>**评论:**<br/><br/>danhardman: <pre><p>My project is fairly similarly structured to this. However, I have defined my models as just entities, so User, Order, Item etc. and I have a separate package for database interaction called datastores.</p>
<p>In the datastores package, each model has a datastore (userDatastore, orderDatastore) which has a defined struct that has an *sql.DB field and I add all database functions onto that struct.</p>
<p>Here's an example:</p>
<pre><code>//UserDatastore handles database functionality for the user model
type UserDatastore struct {
BaseDatastore
}
//NewUserDatastore creates a new user datastore with the provided database and
//collection name
func NewUserDatastore(db *sql.DB, c string) *UserDatastore {
return &UserDatastore{BaseDatastore{db, c}}
}
//Create adds a new user to the datastore
func (d *UserDatastore) Create(u models.User) error {
stmt, err := d.DB().Prepare("")
if err != nil {
return err
}
//rest of the code...
}
</code></pre></pre>QThellimist: <pre><p>looks prettier than the structure I described. Do you have another abstraction where a you can only access to datastore within models like </p>
<pre><code>func (u User) CreateUser() (User, error) {
return u.Datastore.Create()
}
</code></pre>
<p>or does the endpoints/routes have direct access to datastore?</p></pre>danhardman: <pre><p>Like the UserDatastore, all others take a *sql.DB as an argument. So as long as you can pass it that, you can use it from anywhere. I've taken my DB initialisation out to a separate package for that reason.</p></pre>crowl91: <pre><p>Take a look at <a href="http://www.alexedwards.net/blog/organising-database-access" rel="nofollow">this</a></p></pre>QThellimist: <pre><p>This is awesome thanks. I got a question though. In the last example the author stated passing variables over context in a request scope is not adviced. Why is that? It is a more functional approach also allows a better abstraction between requests as I can see. </p></pre>QThellimist: <pre><p>If I use the <code>Database struct</code> structure these interfaces solve the problem.</p>
<pre><code>type DatabaseHandler interface {
Prepare(query string) (*sql.Stmt, error)
QueryRow(query string, args ...interface{}) *sql.Row
Query(query string, args ...interface{}) (*sql.Rows, error)
Exec(query string, args ...interface{}) (sql.Result, error)
}
</code></pre></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传