uber-go / fx

A dependency injection based application framework for Go.
https://uber-go.github.io/fx/
MIT License
5.48k stars 283 forks source link

Example of using fx for a short-lived CLI process? #755

Open cweagans opened 3 years ago

cweagans commented 3 years ago

All of the information/examples that I can find about how to use Fx in the "correct" way are centered around long-lived processes (HTTP servers and the like). I really like how my application is structured with Fx, but I'm not quite sure how to use it for e.g. a CLI application that should be running for < 1 second.

For instance, is there any way to silence the log output entirely? Is there a way to default to not a long-running process?

sywhang commented 3 years ago

As of now, there isn't really a great way. Fx is designed to be more suitable for long-lived processes because it was mainly designed as a DI framework for microservices at Uber, which are, long-lived processes :).

Enabling fx to be more friendly for short-lived processes has been an ongoing discussion but there is no planned timeline on that happening.

oakad commented 2 years ago

Fx works quite well with short lived processes thanks to very handy Shutdowner interface. Yes, the boilerplate could be leaner, but the approach definitely woks.

rhzs commented 2 years ago

@oakad can you give us concrete working example?

oakad commented 2 years ago
package main

import (
    "fmt"
    "time"
    "context"

    "go.uber.org/fx"
)

type Action struct {
    sh fx.Shutdowner
}

func newAction(
    lc fx.Lifecycle,
    sh fx.Shutdowner,
) *Action {
    act := &Action{
        sh: sh,
    }

    lc.Append(fx.Hook{
        OnStart: act.start,
        OnStop:  act.stop,
    })

    return act
}

func (act *Action) start(ctx context.Context) error {
    go act.run()
    return nil
}

func (act *Action) stop(ctx context.Context) error {
    fmt.Println("Stopped")
    return nil
}

func (act *Action) run() {
    time.Sleep(time.Second)
    fmt.Println("Will stop now")
    act.sh.Shutdown()
}

func main() {
    fx.New(
        fx.Provide(
            newAction,
        ),
        fx.Invoke(func(*Action) {}),
    ).Run()
}
anjolaoluwaakindipe commented 1 year ago
package main

import (
  "fmt"
  "time"
  "context"

  "go.uber.org/fx"
)

type Action struct {
  sh fx.Shutdowner
}

func newAction(
  lc fx.Lifecycle,
  sh fx.Shutdowner,
) *Action {
  act := &Action{
      sh: sh,
  }

  lc.Append(fx.Hook{
      OnStart: act.start,
      OnStop:  act.stop,
  })

  return act
}

func (act *Action) start(ctx context.Context) error {
  go act.run()
  return nil
}

func (act *Action) stop(ctx context.Context) error {
  fmt.Println("Stopped")
  return nil
}

func (act *Action) run() {
  time.Sleep(time.Second)
  fmt.Println("Will stop now")
  act.sh.Shutdown()
}

func main() {
  fx.New(
      fx.Provide(
          newAction,
      ),
      fx.Invoke(func(*Action) {}),
  ).Run()
}

could this be added to the documentation for short-lived applications

pespantelis commented 10 months ago

Here's another workaround for short-lived processes:

package main

import (
    "context"
    "fmt"

    "go.uber.org/fx"
)

func Run(opts ...fx.Option) {
    app := fx.New(opts...)

    if err := app.Start(context.Background()); err != nil {
        fmt.Println("[Fx] START FAILED\t" + err.Error())
        return
    }
    if err := app.Stop(context.Background()); err != nil {
        fmt.Println("[Fx] STOP FAILED\t" + err.Error())
        return
    }

    fmt.Println("[Fx] DONE")
}

func main() {
    Run(
        fx.Invoke(func() {
            fmt.Println("--> Hello world!")
        }),
    )
}