Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: Define Container, fx.Provide, fx.Supply, fx.Invoke #1156

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ module.exports = {
{
title: 'Concepts',
children: [
'container.md',
'lifecycle.md',
'modules.md',
],
Expand Down
156 changes: 156 additions & 0 deletions docs/container.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Container

Container is the abstraction responsible for holding all constructors and values.
It’s the primary means by which an application interacts with Fx.
You teach the container about the needs of your application,
how to perform certain operations,
and then you let it handle actually running your application.

Fx does not provide direct access to the container.
Instead, you specify operations to perform on the container
by providing `fx.Option`s to the `fx.New` constructor.

```go
package fx

type App
func New(opts ...Option) *App
func (app *App) Run()

type Option
func Provide(constructors ...interface{}) Option
func Invoke(funcs ...interface{}) Option
```
Comment on lines +13 to +23
Copy link
Contributor

@JacobOaks JacobOaks Feb 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your intention is to show some relevant functions for this section and their relevent types, but this throws me off a little because its formatted as a code block and yet isn't really valid Go syntax. I think it's fine, just something to be aware of. Is this a common format for trying to portray this kind of information that I'm not aware of?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This formatting was borrowed from how godoc shows things:

image

I'm not tied to it, though. We can change it if you'd like.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay! Makes sense. I think this is good then.


Check the [API Reference](https://pkg.go.dev/go.uber.org/fx#Option)
for a complete list of options and their behaviors.

## Providing values

You must provide values to the container before you can use them.
Fx provides two ways to provide values to the container:

- `fx.Provide` for values that have a constructor.

```go
fx.Provide(
func(cfg *Config) *Logger { /* ... */ },
)
```

This says that Fx should use this function to construct a `*Logger`,
and that a `*Config` is required to build one.

- `fx.Supply` for pre-built non-interface values.

```go
fx.Provide(
fx.Supply(&Config{
Name: "my-app",
}),
)
```

This says that Fx should use the provided `*Config` as-is.

**Important**: `fx.Supply` is only for non-interface values.
See *When to use fx.Supply* for more details.

Values provided to the container are available to all other constructors.
In the example above, the `*Config` would become available to the `*Logger` constructor,
and the `*Logger` to any other constructors that need it.

### When to use fx.Supply

Usually, `fx.Provide` is the right choice because more often than not,
constructing an object requires its dependencies.
`fx.Supply` is a convenience function for the rare cases where that isn't true:
standalone values that don't depend on anything else.

```go
fx.Provide(func() *Config { return &Config{Name: "my-app"} })
// is the same as
fx.Supply(&Config{Name: "my-app"})
```

However, even then, `fx.Supply` comes with a caveat:
it can only be used for non-interface values.

<details>
<summary>Why can&#39;t I use fx.Supply for interface values?</summary>

This is a technical limitation imposed by the fact that `fx.Supply` has to rely
on runtime reflection to determine the type of the value.

Passing an interface value to `fx.Supply` is a lossy operation:
it loses the original interface type, only giving us `interface{}`,
at which point reflection will only reveal the concrete type of the value.

For example, consider:

```go
var svc RepositoryService = &repoService{ ... }
```

If you were to pass `svc` to `fx.Supply`,
the container would only know that it's a `*repoService`,
and it will not know that you intend to use it as a `RepositoryService`.

</details>

## Using values

Providing values to the container only makes them available to the application.
It doesn't do anything with them yet.
Constructors passed to `fx.Provide` are not called until they are needed.

For example, the following won't do anything:

```go
fx.New(
fx.Provide(newHTTPServer), // provides an *http.Server
).Run()
```

You next have to tell the container what is needed, and what to do with it.
Fx provides [`fx.Invoke`](https://pkg.go.dev/go.uber.org/fx#Invoke) for this purpose.

In the example above, we'll want an invocation that starts the server:

```go
fx.New(
fx.Provide(newHTTPServer),
fx.Invoke(startHTTPServer),
).Run()
```

### When to use fx.Invoke

`fx.Invoke` is typically used for root-level invocations,
like starting a server or running a main loop.
It's also useful for invoking functions that have side effects.

Examples of cases where you might use `fx.Invoke`:

- Starting a background worker
- Configuring a global logger

As an example, consider an application organized into many distinct abstractions.

```mermaid
flowchart LR
CacheWarmer --> Redis
Server[http.Server] --> UserHandler & PostHandler
UserHandler --> Redis[redis.Client] & Client[http.Client]
PostHandler --> sqlDB[sql.DB]

subgraph Roots
CacheWarmer
Server
end
```

`CacheWarmer` and `http.Server` are the roots of the application.
We'll need `fx.Invoke` for the side effects of starting the server
and the cache warmer loop.
Everything else will be handled by the container automatically.
Loading