Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The journey to automatic dependency injection in go - James Mallison - Sixt

GoDays
January 23, 2020

The journey to automatic dependency injection in go - James Mallison - Sixt

Dependency Injection is a language-agnostic approach to building well designed and testable code. In many dynamically typed languages, dependency injection can easily be automated to provide a platform for rapid application development, but runtime automatic dependency injection has it’s drawbacks. Go makes automatic DI more of a challenge; powerful languages require powerful tooling and Go is no exception.

In this talk we will explore some novel (read: extremely hacky) approaches to automatic dependency injection, from traversing the AST and the challenges and magic of runtime DI with a custom solution through to generating factories for compile-time DI with google’s wire library and beyond.

GoDays

January 23, 2020
Tweet

More Decks by GoDays

Other Decks in Technology

Transcript

  1. @J7mbo • How do I communicate over TCP? • Http?

    - Learned how to use Mux • Rpc? - Learned how to use Grpc / Protobuf • How do I fully automate dependency injection for rapid application development?
  2. @J7mbo • My journey in attempting to achieve this in

    a new language • Implementing runtime dependency injection atrociously • A live demo of the above • Moving on to compile-time dependency injection • Discussion of the pros and cons • Problems I encountered and solved • What I learned, and what I’d love to see next Automatic Dependency Injection
  3. @J7mbo Automatic Runtime Dependency Injection Ideally with ZERO configuration •

    Tell some library object to build our Repository • It uses reflection to see dependencies • It finds DB dependency • It uses reflection on DB dependency • Initialise DB dependency • Initialise Repository with DB dependency injected
  4. @J7mbo Using Reflection Function takes interface{} and returns interface{}, so

    it can work with any object reflect package allows us to look at object internals at runtime Loop around the fields in Repository Find DB as a dependency Dynamically initialise a new
 instance of this dependency Set the DB field to the newly initialised dependency Must type assert
  5. @J7mbo Generating a type registry At the end: Write the

    map[string]interface{} to a new file (registry.go) Found an exported struct, Store it in an array of structs
  6. @J7mbo Generating a type registry src/ ├───dir1 │ repository.go ├───dir2

    │ repository.go ├───dir3 │ ├───sub1 │ │ db.go │ └───sub2 go.mod contains “module app” app/src/dir1/repository.go app/src/dir2/repository.go app/src/dir3/sub1/db.go But how can I get this “fully qualified” module path for each file in each directory?
  7. @J7mbo Make() • Look for name in registry, retrieve struct

    • Reflect on struct, look at dependencies… • For each dependency, lookup in registry, recurse • Finally, inject each one, one-by-one, before returning Repository
  8. @J7mbo Delegate() Delegate() just saves the choice in the injector

    This overrides type-registry-found factories But what about if we have two factories? Lambdas too
  9. @J7mbo Pros and Cons 1. Very nice, simple to grok

    API 2. Fast, rapid app development 3. Mostly automatic registration 4. New properties auto-injected 1. I wrote it 2. Have to type assert interface{} for each call 3. Still need to generate a registry each time 4. Doesn’t work properly in edge cases 5. Doesn’t work properly in normal cases 6. Fields are struct copies - expensive 7. Requires registry of all types in memory 8. Debugging is an absolute nightmare
  10. @J7mbo Compile-Time Dependency Injection Tell wire which factory methods it

    has to call It’ll look at the return type It’ll look at the factory methods
  11. @J7mbo Pros and Cons 1. I didn’t write it, the

    Google guys did 2. Compile-time guarantees 3. Much more of a standard solution 4. No coupling to library after generation 5. No ABSOLUTELY AWFUL DEBUGGING 1. Not as user friendly an API 2. Devs still have to write extra code 3. Not fully automatic 4. Far fewer features than typical DI libs
  12. @J7mbo Conclusion: Thoughts on runtime vs compile-time Runtime will never

    provide the type guarantees A huge suite of unit tests can cover this base (but my lib tests suck) Until a runtime tool works 100% and is community backed.. teams should use compile-time if they want a DI tool Use runtime in your side-projects instead if you like
  13. @J7mbo Conclusion - What next? Considering building a tool: •

    Looks at the code you want to run (AST) • Writes wire.Build code for you • Runs wire to generate it • (One step closer to fully automatic with compile-time guarantees) Turns out that what I created basically already exists (Facebook Inject - now archived?) - without autogeneration of registry. I’m still going to work on my runtime project for fun. https://github.com/j7mbo/goij