How to dynamically inject dependencies into "Service" in Go?
This solution (approach) is based on:
-
variadic functions
(https://yourbasic.org/golang/variadic-function/) and -
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 main service. In our snippet it is accounts
:
accs := accounts.New()
Init the second service (it will be property for main service):
srvSubscriptions := subscriptions.New(111)
After that you should bind second service and set properties for 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 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)
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
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) }
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
All sources you can find in my repository:
https://github.com/romanitalian/go-service-options
PR's are welcome.
Top comments (0)