網頁

2023/10/30

Golang uber Fx 依賴注入套件 簡單範例

Go語言的三方依賴注入(Dependency Injection)套件uber-go/fx簡單範例。


範例環境

  • Go 1.19
  • uber-go/fx v1.20.1


安裝

在專案根目錄執行go get go.uber.org/fx@v1安裝Fx。


範例一

Fx是透過在本身的運行環境中實現依賴注入,(這個「環境」又稱為IoC容器),一開始用fx.New建立fx.App物件,然後呼叫fx.App.Run來運行Fx。

fx.New的參數中設定各種物件的建構式和調用函式。

fx.Provider用來註冊物件的建構式,告訴Fx如何建立依賴類型的實例,這些建構式的參數可能依賴於其他建構式的結果,Fx會根據建構式的注入參數類型自動透過所提供的其他建構式返回結果將實例注入為參數。

fx.Invoke在Fx執行時會立刻被調用。在fx.Invoke中去執行一個參數為*A的函式func(a *A){},該函式需要注入*A的實例,因此Fx會去調用在fx.Provider註冊的NewA建構式。

main.go

package main

import (
    "fmt"

    "go.uber.org/fx"
)

func main() {
    fx.New(
        fx.Provide(NewA),
        fx.Provide(NewB),
        fx.Invoke(func(a *A) {
            fmt.Println("invoke")
        }),
    ).Run()
}

type A struct {
    *B
}

func NewA(b *B) *A {
    fmt.Println("create A")
    return &A{
        B: b,
    }
}

type B struct {
}

func NewB() *B {
    fmt.Println("create B")
    return &B{}
}

github



測試

執行程式後輸出以下:

[Fx] PROVIDE    *main.A <= main.NewA()
[Fx] PROVIDE    *main.B <= main.NewB()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.main.func1()
create B
[Fx] RUN        provide: main.NewB()
create A
[Fx] RUN        provide: main.NewA()
invoke
[Fx] RUNNING


範例二

下圖為本範例個物件的依賴關係:



fx.Annotate用來標注建構式參數,第一個參數為要被標注的建構式;第二個參數以後為要標注的選項。

  • fx.As 用來標注建構式返回的實例為介面,用來告訴Fx建構函式要注入的介面參數的實例為何。

fx.Invoke中去執行一個參數為CartService的函式func(cartService CartService),該函式需要注入CartService的實例,因此Fx會去調用在fx.Provider註冊的NewCartServieImpl建構式。

main.go

package main

import (
    "fmt"
    "net/http"

    "go.uber.org/fx"
)

func main() {
    fx.New(
        fx.Provide(
            fx.Annotate(
                NewCartServiceImpl,
                fx.As(new(CartService)),
            ),
            fx.Annotate(
                NewPaymentServiceImpl,
                fx.As(new(PaymentService)),
            ),
            fx.Annotate(
                NewShippingServiceImpl,
                fx.As(new(ShippingService)),
            ),
            fx.Annotate(
                NewOrderRepoImpl,
                fx.As(new(OrderRepo)),
            ),
            fx.Annotate(
                NewProductRepoImpl,
                fx.As(new(ProductRepo)),
            ),
            fx.Annotate(
                NewMemberRepoImpl,
                fx.As(new(MemberRepo)),
            ),
            NewHttpClient,
        ),
        fx.Invoke(func(cartService CartService) {
            cartService.Checkout()
        }),
    ).Run()
}

// shopping cart
type CartService interface {
    Checkout()
}

type CartServiceImpl struct {
    orderRepo       OrderRepo
    paymentService  PaymentService
    shippingService ShippingService
}

func NewCartServiceImpl(
    orderRepo OrderRepo,
    paymentService PaymentService,
    shippingService ShippingService,
) *CartServiceImpl {
    return &CartServiceImpl{
        orderRepo:       orderRepo,
        paymentService:  paymentService,
        shippingService: shippingService,
    }
}

func (svc CartServiceImpl) Checkout() {
    fmt.Println("checkout - start")
    svc.orderRepo.GetOrder()
    svc.paymentService.Pay()
    svc.shippingService.Ship()
    fmt.Println("checkout - end")
}

// payment
type PaymentService interface {
    Pay()
}

type PaymentServiceImpl struct {
    httpClient *http.Client
}

func NewPaymentServiceImpl(httpClient *http.Client) *PaymentServiceImpl {
    return &PaymentServiceImpl{
        httpClient: httpClient,
    }
}

func (svc PaymentServiceImpl) Pay() {
    fmt.Println("make payment")
}

// shipping
type ShippingService interface {
    Ship()
}

type ShippingServiceImpl struct {
    productRepo ProductRepo
    memberRepo  MemberRepo
}

func NewShippingServiceImpl(
    productService ProductRepo,
    memberService MemberRepo,
) *ShippingServiceImpl {
    return &ShippingServiceImpl{
        productRepo: productService,
        memberRepo:  memberService,
    }
}

func (svc ShippingServiceImpl) Ship() {
    fmt.Println("ship - start")
    svc.productRepo.GetProduct()
    svc.memberRepo.GetMember()
    fmt.Println("ship - end")
}

// product
type ProductRepo interface {
    GetProduct()
}

type ProductRepoImpl struct {
}

func NewProductRepoImpl() *ProductRepoImpl {
    return &ProductRepoImpl{}
}

func (svc ProductRepoImpl) GetProduct() {
    fmt.Println("get product")
}

// order
type OrderRepo interface {
    GetOrder()
}

type OrderRepoImpl struct {
}

func NewOrderRepoImpl() *OrderRepoImpl {
    return &OrderRepoImpl{}
}

func (svc OrderRepoImpl) GetOrder() {
    fmt.Println("get order")
}

// member
type MemberRepo interface {
    GetMember()
}

type MemberRepoImpl struct {
}

func NewMemberRepoImpl() *MemberRepoImpl {
    return &MemberRepoImpl{}
}

func (svc MemberRepoImpl) GetMember() {
    fmt.Println("get member")
}

func NewHttpClient() *http.Client {
    return http.DefaultClient
}

github



測試

執行程式輸出以下結果:

[Fx] PROVIDE    main.CartService <= fx.Annotate(main.NewCartServiceImpl(), fx.As([[main.CartService]])
[Fx] PROVIDE    main.PaymentService <= fx.Annotate(main.NewPaymentServiceImpl(), fx.As([[main.PaymentService]])
[Fx] PROVIDE    main.ShippingService <= fx.Annotate(main.NewShippingServiceImpl(), fx.As([[main.ShippingService]])
[Fx] PROVIDE    main.OrderRepo <= fx.Annotate(main.NewOrderRepoImpl(), fx.As([[main.OrderRepo]])
[Fx] PROVIDE    main.ProductRepo <= fx.Annotate(main.NewProductRepoImpl(), fx.As([[main.ProductRepo]])
[Fx] PROVIDE    main.MemberRepo <= fx.Annotate(main.NewMemberRepoImpl(), fx.As([[main.MemberRepo]])
[Fx] PROVIDE    *http.Client <= main.NewHttpClient()
[Fx] PROVIDE    fx.Lifecycle <= go.uber.org/fx.New.func1()
[Fx] PROVIDE    fx.Shutdowner <= go.uber.org/fx.(*App).shutdowner-fm()
[Fx] PROVIDE    fx.DotGraph <= go.uber.org/fx.(*App).dotGraph-fm()
[Fx] INVOKE             main.main.func1()
[Fx] RUN        provide: fx.Annotate(main.NewOrderRepoImpl(), fx.As([[main.OrderRepo]])
[Fx] RUN        provide: main.NewHttpClient()
[Fx] RUN        provide: fx.Annotate(main.NewPaymentServiceImpl(), fx.As([[main.PaymentService]])
[Fx] RUN        provide: fx.Annotate(main.NewProductRepoImpl(), fx.As([[main.ProductRepo]])
[Fx] RUN        provide: fx.Annotate(main.NewMemberRepoImpl(), fx.As([[main.MemberRepo]])
[Fx] RUN        provide: fx.Annotate(main.NewShippingServiceImpl(), fx.As([[main.ShippingService]])
[Fx] RUN        provide: fx.Annotate(main.NewCartServiceImpl(), fx.As([[main.CartService]])
checkout - start
get order
make payment
ship - start
get product
get member
ship - end
checkout - end
[Fx] RUNNING


沒有留言:

張貼留言