fxbarrier

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 20, 2025 License: MIT Imports: 4 Imported by: 1

README

fxbarrier

fxbarrier is a small utility library for the go.uber.org/fx dependency injection framework. It provides a mechanism to enforce execution order, ensuring that some components are initialized only after a central processing step is complete.

The Core Idea

In fx, constructors are executed lazily as needed, which makes it hard to enforce a strict startup order. For example, how do you ensure that an action like parsing command-line flags happens before any component that depends on those flag values is created?

fxbarrier solves this by introducing a named synchronization point, or "barrier".

  1. fxbarrier.Barrier("my-phase", action): This creates a barrier. The action (e.g., parsing flags) is executed at a specific point in the startup process.

  2. fxbarrier.Provide("my-phase", constructor): This tells fx that the constructor depends on the barrier. It will only be executed after the barrier's action has successfully completed.

Usage Example

The canonical problem is the "define-parse-use" sequence for command-line flags. fxbarrier can coordinate these steps perfectly.

Let's look at a complete example that implements a --version flag.

package main

import (
	"flag"
	"fmt"
	"os"

	"go.uber.org/fx"

	"github.com/lftk/fxbarrier"
)

// A struct to hold our parsed flag values.
type flags struct {
	Version bool
}

func main() {
	app := fx.New(
		// 1. Define: This constructor *defines* the flags.
		// fxbarrier ensures it runs before the barrier's action,
		// and makes its result (*flags) available only after the barrier is lifted.
		fxbarrier.Provide("flags",
			func(fs *flag.FlagSet) *flags {
				var f flags
				fs.BoolVar(&f.Version, "version", false, "show version")
				return &f
			},
		),

		// 2. Parse: This defines the "flags" barrier and its action.
		// The action (flag.Parse) will run after all flag definitions
		// from fxbarrier.Provide are complete.
		fxbarrier.Barrier("flags",
			func(fs *flag.FlagSet) error {
				return fs.Parse(os.Args[1:])
			},
		),

		// Provide the standard flag.CommandLine to the container.
		fx.Supply(flag.CommandLine),

		// 3. Use: This fx.Invoke block uses the *flags struct.
		// It is guaranteed to run only after the flags have been defined and parsed.
		fx.Invoke(func(f *flags) {
			if f.Version {
				fmt.Println("Version: v0.1.0")
				os.Exit(0)
			}
		}),

		// Use fx.NopLogger to keep the output clean.
		fx.NopLogger,
	)
	app.Run()
}
How It Works
  1. fxbarrier.Provide("flags", ...): We wrap the constructor that defines our flags (fs.BoolVar(...)). fxbarrier intercepts this. It ensures this constructor runs first, but it holds back its result (*flags) from the rest of the application.

  2. fxbarrier.Barrier("flags", ...): This defines the barrier's action, which is to parse the flags (fs.Parse(...)). fxbarrier guarantees that this action runs after all constructors associated with the "flags" barrier have completed.

  3. Barrier Lifted: Once the Parse action is successful, the barrier is "lifted". fxbarrier now releases the *flags struct into the fx container.

  4. fx.Invoke(...): The Invoke function, which depends on *flags, can now finally run. It can safely use the f.Version field, knowing it has been properly parsed.

This creates a clear and safe execution flow: Define -> Parse -> Use.

Real-World Implementation

For a more complete, real-world implementation of flag parsing built on top of fxbarrier, see the flagfx library.

Documentation

Overview

Package fxbarrier provides a mechanism to defer the execution of fx.Provide constructors until a certain phase in the application lifecycle. This is useful for scenarios where some components must be initialized only after a central processing step has completed.

The core idea is to split a constructor `func(deps...) (results...)` into two parts:

  1. A "phase 1" constructor that captures the dependencies (`deps...`) and registers an entry.
  2. A "phase 2" constructor that depends on a signal (the "barrier") and, once the barrier is lifted, executes the original constructor logic to produce the results (`results...`).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Barrier

func Barrier(name string, actions ...any) fx.Option

Barrier sets up the processing phase for a given named barrier. It provides the core `process` engine and registers any user-defined actions that should be executed when the barrier is triggered. The name should be unique to avoid conflicts with other barriers.

func Provide

func Provide(barrier string, constructors ...any) fx.Option

Provide returns an fx.Option that wraps constructors, deferring their execution until the named barrier is lifted. The name should match the one used in `Barrier`.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL