DEV Community

Roman Romadin
Roman Romadin

Posted on

Go (golang) service options (dependencies)

How to dynamically inject dependencies into "Service" in Go?

This solution (approach) is based on:

  1. variadic functions (https://yourbasic.org/golang/variadic-function/) and
  2. self-referential functions (https://www.geeksforgeeks.org/self-referential-structures/).

At first glance, this seems a bit confusing, but it's actually a simple solution.
This approach can be used to initialize the application as a whole and to load configuration and other services.

Using variadic function:

type Option func(*accs)

func (f *accs) AddOptions(opts ...Option) {
    for _, opt := range opts {
        opt(f)
    }
}
Enter fullscreen mode Exit fullscreen mode

Using self-referential functions:

func BindSubscriptions(v subscriptions.Client) Option {
    return func(f *accs) {
        f.srvSubscriptions = v
    }
}
Enter fullscreen mode Exit fullscreen mode

First of all you should create main service. In our snippet it is accounts:

accs := accounts.New()
Enter fullscreen mode Exit fullscreen mode

Init the second service (it will be property for main service):

srvSubscriptions := subscriptions.New(111)
Enter fullscreen mode Exit fullscreen mode

After that you should bind second service and set properties for first service:

sbs := accounts.BindSubscriptions(srvSubscriptions)
accs.AddOptions(sbs)
Enter fullscreen mode Exit fullscreen mode

And finally we process business logic (process):

accs.Process(222)
Enter fullscreen mode Exit fullscreen mode

As well we can change any properties in second service (accounts or billing in our snippet): set value = 222

And in addition by using variadic functions we can add one or more properties into first service:

accs.AddOptions(sbs, bl)
Enter fullscreen mode Exit fullscreen mode

or by this way:

accs.AddOptions(sbs)
accs.AddOptions(bl)
Enter fullscreen mode Exit fullscreen mode

Full code:

.
├── cmd
│   └── main.go
├── pkg
│   ├── accounts
│   │   └── service.go
│   ├── billing
│   │   └── service.go
│   └── subscriptions
│       └── service.go
└── README.md
Enter fullscreen mode Exit fullscreen mode
package main

import (
    "../pkg/billing"
    "../pkg/subscriptions"
    "../pkg/accounts"
    "fmt"
)

func main() {
    fmt.Println("Variant_1:")
    variant1()

    fmt.Println("Variant_2:")
    variant2()

    fmt.Println("Variant_3:")
    variant3()
}


func variant1() {
    accs := accounts.New()

    srvAccounts := subscriptions.New(111)

    sbs := accounts.BindSubscriptions(srvAccounts)

    accs.AddOptions(sbs)

    accs.Process(222)
}

func variant2() {
    accs := accounts.New()

    srvAccounts := subscriptions.New(333)
    srvBilling := billing.New()

    sbs := accounts.BindSubscriptions(srvAccounts)
    bl := accounts.BindBilling(srvBilling)

    accs.AddOptions(sbs, bl)

    accs.Process(444)
}

func variant3() {
    accs := accounts.New()

    srvAccounts := subscriptions.New(555)
    srvBilling := billing.New()

    sbs := accounts.BindSubscriptions(srvAccounts)
    bl := accounts.BindBilling(srvBilling)

    accs.AddOptions(sbs)
    accs.AddOptions(bl)

    accs.Process(777)
}


Enter fullscreen mode Exit fullscreen mode

Output result:

Variant_1:
        a.Prop (before): 111
        a.Prop (after): 222

Variant_2:
        a.Prop (before): 333
        a.Prop (after): 444

Variant_3:
        a.Prop (before): 555
        a.Prop (after): 777

Enter fullscreen mode Exit fullscreen mode
Thank you for reading.

All sources you can find in my repository:
https://github.com/romanitalian/go-service-options

PR's are welcome.

Top comments (0)