<p>I'm writing a web application. I've got my database layer (CouchDB but it can be anything) and I've got a separate caching layer (Redis but again can be anything). How do you structure your code?</p>
<p>Here is a gist which shows how I do it. <a href="https://gist.github.com/zemirco/49e6bebd8f281bce349136931d939cad">https://gist.github.com/zemirco/49e6bebd8f281bce349136931d939cad</a>. As you can see I'm using a wrapper around my DatabaseInterface and my CacheInterface. My handlers have access to this wrapper.</p>
<p>Is that too complicated? Especially the part where I need the pure database layer inside my handlers.</p>
<p>I have to use interfaces to be able to test my code without real database and caching infrastructure.</p>
<hr/>**评论:**<br/><br/>nuunien: <pre><p>I'm using something like this</p>
<pre><code>type Store interface {
GetFollowers(userID string) ([]models.User, error)
}
type DatabaseStore struct {
// wrap sql connection
// or any other driver that your database needs
}
func (s *DatabaseStore) GetFollowers(userID string) ([]models.User, error) {
// get users from db
// can be any db, e.g. PostgreSQL, CouchDB, MongoDB
return users, nil
}
type CacheStore struct {
// embed any other store
// this also makes unimplemented store methods just pass through
Store
// cache internals
}
func (s *CacheStore) GetFollowers(userID string) ([]models.User, error) {
// return user from cache if it's there
// if not, get from underlying store
user, err = s.Store.GetFollowers(userID)
// error check
// add user to cache
return users, nil
}
// easily instantiate any configuration you need
store := CacheStore{DatabaseStore{DB: db}}
</code></pre>
<p>This configuration also makes testing and mocking a lot easier</p></pre>madman2233: <pre><p>I've done it two ways before. </p>
<p> </p>
<p>First is one struct for data access that has both caching and database interfaces in it. I used boltdb with an auto expiring cache system. Simple API and allows for auto handling of cache misses, distributing load, and quick integration. I used "parallel access" so cache misses didn't add to the db access time. This one was low turnover and small amounts of data.</p>
<p> </p>
<p>Second one was a cache layer that also handles the database. This was larger amounts of data, with very high turnover. I used batched writing on the database and a cache only read system for performance.</p>
<p> </p>
<p>Both were a single struct and used an api like data, err := db.Get(key) and err := db.Store(key, data), and everything else was internal to the package. I eventually added Write and Read functions so I could use it for simple io.</p></pre>adelowo: <pre><p>Interfaces are the way to go.. I don't see anything really complicated there..</p>
<p>You could have an <code>App</code> struct that takes in the <code>Wrapper</code>, then inject it into your handlers like [1]
func createPost(a *app) http.HandlerFunc {</p>
<pre><code>return func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
}
//app could look like this
app struct {
Cache CacheInterface
DB WhateverInterface
}
</code></pre>
<p>//Then on initialization of this struct, you do <code>wrapper.GetXX</code> for the corresponding properties.</p>
<p>[1] <a href="https://github.com/adelowo/mockdemo/blob/master/main.go#L92-L96" rel="nofollow">https://github.com/adelowo/mockdemo/blob/master/main.go#L92-L96</a></p>
<p>Shameless plug <a href="https://lanreadelowo.com/blog/2017/07/06/mocking-go/" rel="nofollow">https://lanreadelowo.com/blog/2017/07/06/mocking-go/</a> (talks about isolation of the database)</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传