Slide 1

Slide 1 text

@J7mbo The Journey To Automatic Dependency Injection* James Mallison *In Go

Slide 2

Slide 2 text

@J7mbo

Slide 3

Slide 3 text

@J7mbo

Slide 4

Slide 4 text

@J7mbo Not crashed yet

Slide 5

Slide 5 text

@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?

Slide 6

Slide 6 text

@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

Slide 7

Slide 7 text

@J7mbo Standard Dependency Injection Initialise DB dependency Inject into Repository initialiser

Slide 8

Slide 8 text

@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

Slide 9

Slide 9 text

@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

Slide 10

Slide 10 text

@J7mbo Runtime Dependency Injection How can I create a struct, from a string?

Slide 11

Slide 11 text

@J7mbo Runtime Dependency Injection https://stackoverflow.com/questions/32132064/how-to-discover-all-package-types-at-runtime/32135975#comment95225870_32135975 (narrator: it did)

Slide 12

Slide 12 text

@J7mbo Runtime Dependency Injection A central registry of types Retrieve by name

Slide 13

Slide 13 text

@J7mbo Generating a type registry First, loop through all files in the directory

Slide 14

Slide 14 text

@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

Slide 15

Slide 15 text

@J7mbo Generating a type registry

Slide 16

Slide 16 text

@J7mbo Generating a type registry What about when two dependencies are called “Dependency”?

Slide 17

Slide 17 text

@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?

Slide 18

Slide 18 text

@J7mbo Generating a type registry Execute ‘go list’ to get the full path

Slide 19

Slide 19 text

@J7mbo Generating a type registry

Slide 20

Slide 20 text

@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

Slide 21

Slide 21 text

@J7mbo New() factory methods

Slide 22

Slide 22 text

@J7mbo Back to the type registry…

Slide 23

Slide 23 text

@J7mbo Back to the type registry…

Slide 24

Slide 24 text

@J7mbo Make() Make() now automatically invokes NewRepository and NewDB

Slide 25

Slide 25 text

@J7mbo Share() Tell the injector to always inject *this* db

Slide 26

Slide 26 text

@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

Slide 27

Slide 27 text

@J7mbo Interfaces Encounter this interface, inject this concrete implementation

Slide 28

Slide 28 text

@J7mbo Interface binding? Back in the AST parser… Map of interfaces -> concretes

Slide 29

Slide 29 text

@J7mbo Go Guru

Slide 30

Slide 30 text

@J7mbo My interface so far…

Slide 31

Slide 31 text

@J7mbo Demo of where I got to..

Slide 32

Slide 32 text

@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

Slide 33

Slide 33 text

@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

Slide 34

Slide 34 text

@J7mbo Compile-Time Dependency Injection It’ll generate this when you run wire on the command line

Slide 35

Slide 35 text

@J7mbo Compile-Time Dependency Injection Bind Interface -> Concrete Don’t use connectionString anywhere (IDE warning)

Slide 36

Slide 36 text

@J7mbo Compile-Time Dependency Injection

Slide 37

Slide 37 text

@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

Slide 38

Slide 38 text

@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

Slide 39

Slide 39 text

@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

Slide 40

Slide 40 text

@J7mbo Questions?