uber-go/fx

Can `fx.Private` be scoped as a top-level `fx.Option` instead of a `var`

rossb83 opened this issue · 3 comments

Is your feature request related to a problem? Please describe.
I'd like to be able to continue the scoping story started with fx.Private but make it a top-level fx.Option that can be applied outside of fx.Provide.

The usual pattern is for fx.Provide to already be set in legacy codebases; I would prefer not to go back and change old code by adding an fx.private into it. Instead I feel it may be easier and safer to create a new fx.Module around this to "privatize" the legacy module.

Describe the solution you'd like
Suppose we have legacy code that looks like this that we wish not to touch

package etcdmanagerfx

// imports

var Module = fx.Provide(
    // ... other dependencies
    NewEtcdManager,
)

type EtcdManager struct {
    etcdAddr string
}

func NewEtcdManager(/* ... */) *EtcdManager {
    // ...
}

in my main method I'd like to be able to do something like this to have two etcd's (perhaps a global and a regional)

package main

// imports

func main() {
    fx.New(
        fx.Module("global etcd submodule",
            // ... other dependencies
            etcdmanagerfx.Module,
            fx.Private, // <-- make all dependencies private to other modules outside this scope
        ),
        fx.Module("regional etcd submodule",
            // ... other dependencies
            etcdmanagerfx.Module,
            fx.Private, // <-- make all dependencies private to other modules outside this scope
        ),
    )
}

Describe alternatives you've considered
None

Is this a breaking change?
No

Additional context
None

How about fx.Private is like fx.Options but, like all the content can only be used inside the nearest parent fx.Module?

package examplefx
var Module = fx.Module("examplefx",
    fx.Private( 
      fx.Provide(...),      // only usable inside examplefx.Module
      fx.Supply(...),       // only usable inside examplefx.Module
      etcdmanagerfx.Module, // only usable inside examplefx.Module
    ), // end Private
    fx.Provide(...), // exposed to consumers of examplefx.Module
    fx.Supply(...),  // exposed to consumers of examplefx.Module

    fx.Module("innermodule",
      fx.Provide(...), // exposed to consumers of innermodule and consumers of examplefx.Module
      fx.Private(...), // only usable inside innermodule. Other parts of examplefx.Module can't use it.
    ), // end inner module
)

Yes that would work.

One issue here is the naming collision since there is already

var Private = privateOption{}

Perhaps this can be named as

fx.PrivateScope(...)