A good route towards a maintainable Go API?

polaris · · 524 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Hi, I&#39;ve slowly been attempting to teach myself Golang over the past few months and I&#39;m really unsure on whether i&#39;m writing in a maintainable way. The repo I have is: <a href="https://github.com/VJftw/orchestrate/tree/develop/commander" rel="nofollow">https://github.com/VJftw/orchestrate/tree/develop/commander</a></p> <p>Right now it&#39;s just an API that allows <em>Users</em> to register and obtain a (currently non-expiring) JWT token that they can then use to create <em>Projects</em>. </p> <p>Feel free to run the tests using <code>invoke test</code>, or see this build: <a href="http://i.imgur.com/9jOveFI.jpg" rel="nofollow">http://i.imgur.com/9jOveFI.jpg</a></p> <p>It uses:</p> <ul> <li><a href="https://github.com/jinzhu/gorm" rel="nofollow">GORM</a> as an ORM for persistence.</li> <li><a href="https://github.com/Masterminds/glide" rel="nofollow">Glide</a> for dependency management.</li> <li><a href="http://goconvey.co/" rel="nofollow">GoConvey</a> for tests (admittedly i&#39;m not doing real BDD where the tests are written first, as i&#39;m not entirely sure what i&#39;ll be mocking and i&#39;m still learning).</li> <li><a href="https://github.com/facebookgo/inject" rel="nofollow">facebookgo/inject</a> for dependency injection.</li> <li><a href="https://github.com/urfave/negroni" rel="nofollow">Negroni</a> as a middleware handler (I&#39;ve read that frameworks aren&#39;t particularly idiomatic and I quite liked the idea of smaller binaries!).</li> <li><a href="http://www.gorillatoolkit.org/pkg/mux" rel="nofollow">gorilla/mux</a> for routing.</li> </ul> <p>(the plan is to obtain the <code>hmacSecret</code> and other configuration from environment variables thanks to Docker containers!)</p> <p>As an overview:</p> <ul> <li>I&#39;ve mainly used dependency injection so that I can mock different parts of the application in my tests.</li> <li>I&#39;ve also tried to eliminate my stuttering as well, but i&#39;m sure you&#39;ll see that i&#39;ve ended up with <em>providers/user.go</em>, <em>resolvers/user.go</em> so in my editor i&#39;ll have lots of files open with the same name, but in different locations, which can get confusing sometimes.</li> <li>In order to mock parts, i&#39;ve created further interfaces so that I can set mocked services, but this has ended up with me creating arbitrary interfaces (<em>providers.IUser</em>) just for this to work (<a href="https://github.com/VJftw/orchestrate/blob/develop/commander/providers/user.go" rel="nofollow">https://github.com/VJftw/orchestrate/blob/develop/commander/providers/user.go</a>) I also read that interfaces should just be <em>providers.User</em>, but obviously this clashes with my implementation of that interface :(. I could have a <em>providers.Provider</em> interface, but then my implementations (<em>providers.User</em>, <em>providers.Project</em>) would only return a <em>models.Model</em> and thus I wouldn&#39;t be able to use <em>models.User</em> specific functions.</li> </ul> <p>I think I could carry on going down this route and not have any major problems but there&#39;s something in the back of my head telling me that this isn&#39;t idiomatic at all. <strong>How could something like this be written in idiomatic go?</strong> whilst being easy to test and having interchangeable parts; as I plan on adding another persistence layer, such as <a href="http://redis.io/" rel="nofollow">Redis</a>, to use as a cache. In theory I could just implement the <em>persisters.Persister</em> interface with another type that makes calls to Redis. </p> <p>Any comments or suggestions are greatly appreciated!</p> <hr/>**评论:**<br/><br/>Snavelzalf: <pre><p>Looks really nice! Just take a look at ur naming Convention interfaces prepended with I are not idiomatic</p></pre>VJftw: <pre><p>Thanks! If I had a <code>providers.User</code> interface, do you think that <code>providers.SimpleUser</code>would be a good name for a struct that implements it?, so this: <a href="https://github.com/VJftw/orchestrate/blob/develop/commander/providers/user.go" rel="nofollow">https://github.com/VJftw/orchestrate/blob/develop/commander/providers/user.go</a> turns into this:</p> <pre><code>package providers import &#34;github.com/vjftw/orchestrate/commander/models&#34; type User interface { New() *models.User } type SimpleUser struct { } func NewSimpleUser() *SimpleUser { return &amp;SimpleUser{} } func (uP SimpleUser) New() *models.User { return &amp;models.User{} } </code></pre></pre>ChristophBerger: <pre><ul> <li><p>Dependency Injection: I never got comfortable with reflection-based DI. It can be done without. I did a bit research on this not so long ago and wrote an <a href="https://appliedgo.net/di" rel="nofollow">article</a> about it.</p></li> <li><p>GoConvey: I used it for my first projects, and I loved it. Especially the intuitive syntax and the browser interface. But test packages add another dependency, and now I am back to almost plain Go tests. &#34;Almost plain&#34; because I generate my test files using <a href="https://github.com/cweill/gotests" rel="nofollow">gotests</a>.</p></li> </ul></pre>VJftw: <pre><p>That&#39;s understandable! I just felt that the facebookgo/inject way was a quick and easy way for me to get started. Thanks for the article! To handle singletons, do you think this would be appropriate? (Imagining that the notebook is a database/cache connection, i&#39;d like to avoid having multiple connections where the service is reused).</p> <pre><code>type Notebook struct { poems map[string][]byte } var notebook Notebook func NewNotebook() *Notebook { if notebook == nil { notebook = &amp;Notebook{ poems: map[string][]byte{}, } } return notebook } </code></pre> <p>What&#39;s wrong with test packages adding another dependency? Do they add to the final build size when running <code>go build</code>? though I think the go compiler ignores *_test.go files :)</p></pre>knotdjb: <pre><blockquote> <p>What&#39;s wrong with test packages adding another dependency? Do they add to the final build size when running go build? though I think the go compiler ignores *_test.go files :)</p> </blockquote> <p>No, but I think people starting with Go go a little overboard with 3rd party dependencies because the stdlib doesn&#39;t provide bells and whistles. However proper use of the standard library (which the community is still trying to contemplate) show that most 3rd party dependencies have little to no value. See: <a href="https://golang.org/doc/faq#testing_framework" rel="nofollow">https://golang.org/doc/faq#testing_framework</a></p> <p>Specifically with goconvey, it makes a great test runner, but the language for writing tests brings little to no value in my opinion.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

524 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传