Skip to content

Conversation

@RobyFerro
Copy link

This implementation allows developers to invoke specific function between multiple containers.
This may be useful in case of you have a container instantiated at application startup (singleton container) and a second container instantiated just before handle every HTTP request (factory container).

With GroupInvoke you can resolve dependencies across singleton and factory container.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


Roberto Ferro seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@RobyFerro RobyFerro closed this Aug 19, 2021
@abhinav
Copy link
Collaborator

abhinav commented Nov 4, 2021

@RobyFerro, I know that you've closed this PR,
but I want to take a second to call out that
Dig and Fx rely heavily on reflection to implement their functionality.
For that reason, we advise that you use them only at startup,
and do not use them on the request path.

Creating per-request containers to (presumably) build a request-scoped handler
is not something we suggest doing with Dig. We instead recommend that you work
around this limitation by manually implementing request-scoped handlers.

For example, suppose your service interface is the following.

package keyvalue

type Interface interface {
  Set(context.Context, *SetRequest) error
  Get(context.Context, *GetRequest) (*GetResponse, error)
}

You can build a Handler that pretends it's request scoped:

type Handler struct {
  // ...
  cache map[something]somethingElse
}

func (h *Handler) Set(context.Context, *keyvalue.SetRequest) error {
  h.cache[...]
}

But instead of building a constructor for it, use of a factory type, which is constructed with dependency injection.

type HandlerFactory struct {
  log *zap.Logger
  // ..
}

func NewHandlerFactory(log *zap.Logger) *HandlerFactory { ... }

// In main, fx.Provide(NewHandlerFactory)

func (f *HandlerFactory) NewHandler(...) *Handler {
  return &Handler{
    log: log,
    cache: make(...),
  }
}

And have HandlerFactory implement keyvalue.Interface, but delegate to a new Handler each request.

func (f *HandlerFactory) Set(ctx context.Context, req *keyvalue.SetRequest) error {
  return f.NewHandler(...).Set(ctx, req)
}

func (f *HandlerFactory) Get(ctx context.Context, req *keyvalue.GetRequest) (*keyvalue.GetResponse, error) {
  return f.NewHandler(...).Get(ctx, req)
}

Then in your main, instead of passing Handler as the implementation of Interface, pass in HandlerFactory.

fx.Provide(func(f *HandlerFactory) keyvalue.Interface { return f })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants