|
I also did it for a Go app recently exactly like that. It was mostly about putting the "imperative" part closer to main.go and putting business rules and things like that in the other files: Routing, serialization, validation, business rules, data transformation, command line argument parsing, configuration parsing, they all go in the "functional" part. Instantiating the HTTP server, reading argv, files and reading/writing to the database goes in the "imperative" part. The major hurdle for me is frameworks and libraries that often want to be used in an imperative way. Some routers, for, instance could be purely functional, but they often want to instantiate the HTTP server themselves. Not a big issue in practice in Golang, though. Also: the database. To keep purity in a simple way, you gotta wrap the imperative parts and use callback-ish structures. This part is often where I cheat. But with a proper abstraction it's great. I hate to say the forbidden word here, but a "monadic" abstraction you can have your cake and eat it. I never did it in Golang and I have no idea whether it would work, but, I did it in C# and it was a breeze. I agree with your assessment about testing. In the end it was incredibly easy for me to get 100% coverage on the functional part. |