Dependency Pattern
We are using this pattern when creating a new Uber-FX
dependency, it makes it easy adding new external dependencies later when your application evolves.
To explain this better, we will do this with a Notifier
example.
Preparation
-
Define our
Notifier
interface:type Notifier interface{ Alert(ctx context.Context, msg string) }
-
Create a dependency “container” for all future dependencies of the
Notifier
implementation and embedfx.In
into it. This will mark it and will tellUber-FX
to inject all the requested dependencies:// It's not public as you can see type notifierDeps struct { fx.In }
-
Create an implementation struct:
// Notice again that's not public type notifier struct{ deps notifierDeps }
-
Create a Constructor function that will return a
Notifier
implementation as a type:func CreateNotifier(deps notifierDeps) (Notifier,error) { return ¬ifier(deps:deps), nil }
-
Finally, implement it:
func (n *notifier) Alert(ctx context.Context, msg string) { // alert someone }
Usage
Now, suppose you want to log every time you alert someone. All you need to do is:
-
Add a
log.Logger
dependency to thenotifierDeps
struct:type notifierDeps struct { fx.In Logger log.Logger }
-
Use it:
func (n *notifier) Alert(ctx context.Context, msg string) { n.deps.Logger.WithField("msg", msg).Debug(ctx, "alerting") // alert someone }
Tests
You are happily using Notifier
in your application, but what about tests?
You know, to test logic that has Notifier
as a dependency. Notifier
will probably call an external service, which is not available during tests.
One way to do it, is to use gomock.
-
You can add a comment above the
Notifier
interface://go:generate mockgen -source=notifier.go -destination=mock/notifier_mock.go type Notifier interface{ Alert(ctx context.Context, msg string) }
-
Execute
go generate ./...
and it will generate all the mocks in your application. -
Use the generated mock in your tests.
Remember not to call a real Notifier Constructor
Mortar includes mocks of all of its interfaces in their respective directories, for example a Logger mock.
You can find several test examples here.