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)