Golang — how to dynamically inject dependencies into the structure

Roman Romadin
ITNEXT
Published in
2 min readFeb 6, 2021

--

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)
}
}

Using self-referential functions:

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

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

accs := accounts.New()

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

srvSubscriptions := subscriptions.New(111)

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

sbs := accounts.BindSubscriptions(srvSubscriptions)
accs.AddOptions(sbs)

And finally, we process business logic (process):

accs.Process(222)

As well we can change any properties in the 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 the first service:

accs.AddOptions(sbs, bl)

or by this way:

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

Full code:

.
├── cmd
│ └── main.go
├── pkg
│ ├── accounts
│ │ └── service.go
│ ├── billing
│ │ └── service.go
│ └── subscriptions
│ └── service.go
└── README.md

All code:

package mainimport (
"github.com/yourUserName/projectName/pkg/billing"
"github.com/yourUserName/projectName/pkg/subscriptions"
"github.com/yourUserName/projectName/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)
}

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

--

--