-
Notifications
You must be signed in to change notification settings - Fork 315
Description
Hello fx-ers, I'm curious if an idea like this has been discussed yet:
Common DI Patterns
Today, I imagine the most common way to construct a concrete type and then depend on the interface is as follows (https://play.golang.org/p/riYmEo2wtsL):
- Interface
Quackerwith methodQuack() - Concrete type
Duck(with private fields) with constructorNewDuckthat returns aQuacker - An
fx.ProvideprovidesNewDuck - A function depends on the
Quackerand invokesQuack()
Problem: this ends up with some boilerplate still. If you want to add a new field to your Duck, you need to copy it in three places: (1) the struct definition, (2) the constructor function signature, and (3) when constructing the concrete type in the return statement.
Alternative Available Today
As an alternative, you could make the fields of your concrete type public, and let DI do some of the heavy lifting for you (https://play.golang.org/p/wAewK_sbXK-):
- Interface
Quackerwith methodQuack() - Concrete type
Duckthat embedsfx.Inand has all public fields. - An
fx.Providebinds the concrete type to the interfacefunc(d Duck) Quacker { return d } - A function depends on
Quackerand invokesQuack()
This works, but only because it abuses the fx.In statement, by turning the concrete type into a parameter struct. It forces the implementation to be aware of fx, which is not ideal. For example, your concrete type can't have a pointer receiver, making many usages impossible.
Alternatives of the Future?
What if we could Populate() the concrete Duck and then Provide() it as a dependency that implements the Quacker interface? That would be pretty magical:
- Interface
Quackerwith methodQuack() - Concrete type
Duckwith all public fields. - We
fx.PopulateAndProvide(&Duck{}, func(d *Duck) Quacker { return d })(...or something, maybe Supply is better) - A function depends on the
Quackerand invokesQuack()
This would effectively populate the Duck with all the properties it needs, then provide it as a Quacker to the fx graph.
Is this even idiomatic Go?
Maybe, maybe not. On the one hand, the Duck now has these public fields like Name, which shouldn't be available to the rest of the application. On the other hand, since it's bound to the Quacker interface in the fx graph, those public fields are not directly accessible without type asserting to the concrete type, at which point you're breaking encapsulation anyway so everything's out the window.
But why really?
Recently I migrated a codebase to fx, and idioms can vary based on experience/maturity of the application. If a codebase makes significant use of public structs/fields that implement interfaces, then more code must be written to take advantage of fx. And at the end of the day, fx's goal should be to reduce boilerplate code, not require more of it.
π¦ π¦ π¦
tyler