I am currently writing a web-server that is using several databases as sources of information. I know this type of question has been asked and answered before but the responses seem to gloss over how to share dependencies to the sub packages.
Thus far I have struggled to find a way to implement my handlers in separate packages that I can use in my main binary.
I am currently using an environment struct with handlers implemented on this struct to allow each handler access to the database connection pool. However this strategy as far as I can tell will result in circular dependencies between my main server and the handler packages if they both need to know about the environment structure. My suspicion is that I can get this to work by using closures instead of an environment struct but I am still a bit unclear how this might be laid out in conjunction with middleware and my serve mux. Or maybe I can get the environment struct to work and I just have to reference it correctly.
ex. This is what a standard route in my app currently looks like.
I was able to get this to work with closures which IMO is a bit ugly. Hopefully someone can figure out how to take same approach using an environment struct.
I dont like this approach for a couple reasons.
If I want to change environment, I have to potentially change signature of all my handlers.
Many of my functions inside my handlers are structured to take the environment variable and need to be rewritten to take individual connection pools for each database.
The solution using closures for those interested:
###Inside main package we pass in database connection pool and get back the desired http.Handler
For those curious, I was finally able to get this to work with an environment struct as well however I still am making use of closures because I can’t define functions on an external type. If anyone can do better than below, I would be very interested to see.
The key to my current approach is that the environment struct needs to be defined in a separate package than the main package and then imported into main and the sub handler packages to avoid circular dependencies.
I came up with another possible solution, one I think I like the most of all but also requires subverting the go build system a little bit. I would like opinions anyways.
Rather than break all the handlers into separate sub-packages, keep everything in the main package and break your handlers out into separate directories as your choosing. Then prior to compiling the main binary, move all source files into a single directory and compile as a single monolith package.
I use a special build script to accomplish this as follows:
echo "CLEANING app directory"
rm -rf /home/vm/server/app
mkdir /home/vm/server/app
echo "MOVING source files to app directory"
cp /home/vm/server/handlers/*.go /home/vm/server/app/
cp /home/vm/server/handlers/**/*.go /home/vm/server/app/
echo "BUILDING development binaries"
go build -ldflags="-X main.UseDatabase=testdb -X main.UsePort=:9000 -o test_server ./app
if [ $? -eq 0 ]; then
echo "Test server was successfully built"
fi
I really like this approach for several reasons even if it may not be best practice for go.
I can group files together into subdirectories without worrying about imports, type references, or dependancy injection
I can structure my app as one monolith allowing a shared environment struct I can define handlers on
I don’t have to break out shared resource into external packages to prevent circular dependancies
I don’t have to re-include helper functions into each sub package.
This essentially allows your development code organization to not affect the actual go build system’s requirement that all code live in the root directory.