pilot tech talk #12 — dependency injection in go by justus perlwitz
TRANSCRIPT
Dependency Injection in Go
The Problem
Big applications have hidden dependencies
Hidden dependencies make code hard to test
High maintenance cost in strong coupling
Example
Mail Delivery Application
package main
type Mail struct { content string recipient string}
Mail Sending Function
// External Mailer APIimport "mailbib" // Treat this a black box
func (e *Mail) Send() error { err := mailbib.Mailer.Send(e.content, e.recipient) if err != nil { return fmt.Errorf("MailBib error: %+v", err) } return nil}
MailBib
func (m MailBib) Send(c string, r string) error { if r == "Santa" { return fmt.Errorf("Recipient too far away") }
m.mails = append(m.mails, c) fmt.Printf("Sent '%s' to %s\n", c, r) return nil}
Let's use it
mail := Mail{ content: "Hello, World!", recpient: "Bigfoot",}if err := mail.Send(); err != nil { fmt.Printf("Error: %+v\n", err)}
This just works fine
Sent 'Hello, World!' to Bigfoot
A Letter to Santa
// Let's try sending the same mail to Santamail.recipient = "Santa"
// What if Mailbib cannot deliver mail to the Arctic Circle?if err := mail.Send(); err != nil { fmt.Printf("Error: %+v\n", err)}
Santa is living too far away
Error: MailBib error: Recipient too far away
Dependency Injection
Let's inject our mailer dependency into the Mail struct:
type MailDelivery interface { Send(string, string) error}
The interface requires the mailer to have one function Send()
type Mail struct { content string recipient string delivery MailDelivery}
Trying Different Dependencies
Now, we have to initialize a Mail as follows
mail := Mail{ content: "Hello, World!", recipient: "Santa", delivery: mailbib.Mailer,}
And we adjust the mailing method
func (e *Mail) Send() error { err := e.delivery.Send(e.content, e.recipient) if err != nil { return fmt.Errorf("Error: %+v", err) } return nil}
Of course, MailBib still refuses to deliver our mail:
if err := mail.Send(); err != nil { fmt.Printf("Could not deliver mail ‐> %+v\n", err)}
outputs
Could not deliver mail ‐> Error: Recipient too far away
Injecting a different dependency
Let's pretend, there is a second delivery API, that delivers mail to thearctic circle
import "mailarctica"
Now we can inject a different service
mail.delivery = mailarctica.Mailer
Let's send the email again
mail.delivery = mailarctica.Mailer
// This time, MailArctica will deliver our letterif err := mail.Send(); err != nil { fmt.Printf("Error: %+v\n", err)}
will output
Sent 'Hello, World!' to Santa
Useful Packages
There are two packages with similar names that make dependencyinjection a lot easier:
inject by Facebook
Inject
Questions?