10 ms·
The things you describe are pretty strongly understood as antipatterns in Go.
by sagichmal 6y ago
The things you describe are pretty strongly understood as antipatterns in Go.
- virmundi 6y agoI hope to spur conversation on this. I've seen many in the Go community argue against abstractions. Many say, "Pass the database connection in the params." Or "make the DB pool a global variable". How can you write tests for logic without having to instantiate a DB? Many gophers appear to say I should have essentially a giant transaction script for each handler (https://martinfowler.com/eaaCatalog/transactionScript.html https://martinfowler.com/eaaCatalog/transactionScript.html). The handler opens the DB, makes it at least function scoped, and all my business logic goes in the handler, or some similar method where the DB connection is passed. Now my tests are functionally integration testings. This makes them both slow, and hard to test for proper error handling. When I write code in most OOP like languages, I follow the Clean Architecture model. The use case is an actual struct with the interfaces defining the repositories/services as members. I am now free to test the use case in isolation. I can test failure cases to since I return an error, which can be easily mocked. I write a factory to create my use cases. The handlers get the factory passed in as an argument to the constructor for server. I can now test handlers in isolation by passing a factor with mocks.
- sagichmal 6y ago> Many say, "Pass the database connection in the params." ... How can you write tests for logic without having to instantiate a DB? You define and use an interface to model the DB, and use that as the mock point. Basic stuff.
- throwaway894345 6y agoTo add to your answer for those who aren't familiar with the basics of mocking, here's a StackOverflow answer that I've shared with others--maybe it will help elucidate things: https://stackoverflow.com/questions/19167970/mock-functions-in-go https://stackoverflow.com/questions/19167970/mock-functions-...
- virmundi 6y agoI aware of making an interface for that. I advocate that. What I see people on r/golang saying, and even in HN, is to just pass the DB connection either explicitly or in the context. I hate this. It makes things bound to DBs.
- sagichmal 6y agoSome people say this, but when they do, other more senior people quickly interject and say it's a bad idea.
- throwaway894345 6y agoI agree. I’ve seen more prominent figures argue against global state often. Using global state is certainly not idiomatic Go.
- morelisp 6y ago> Using global state is certainly not idiomatic Go. Unfortunately it's not this simple (and probably multi-idiomatic). Go definitely adopts more global state than other languages; I don't know any other language that offers a default-global HTTP client and server. Now, part of that is because Go's stdlib goes out of its way to make these appear stateless even though they are not - and this is good, even if you (often rightly) don't use the default ones. But I think a lot of people saw those carefully engineered APIs and instead ran with "globals are OK in Go!" Lots of packages have global-level configuration properties - some of this is a hacky replacement for DI e.g. most logger injection. Well, OK, I can support/tolerate some of that because DI in these cases is usually a hacky replacement for real AOP language support. But some of it just shouldn't be global. e.g. Gin debug vs. release vs. test mode should be a setting on the Engine. And then you get into really bad stuff - I don't know why but it is common to to have a global sql.DB, or sarama.AsyncProducer, or whatnot. A lot of novice Go developers - anecodotally predominately skewed towards previous PHP users, I think because they are not used to have really global variables - use a global for anything concurrency-safe. And this has ended up in a lot of low-quality tutorials/examples/SO questions so I don't see it going away any time soon.