go - Dependency Injection & Testing -


i'm working on small go application that's wrapper various password stores (ansible vault, hashicorp vault, chef vault, etc). idea is: in various provisioning scripts, can use go wrapper grab secrets , if decide switch password stores behind scenes, of interfaces don't need updated across projects.

i'm trying setup proper tests application, , in doing so, trying figure out best way to inject dependencies.

for example, lets project called secrets. , 1 of implementations ansible. , ansible implementation needs own parser , needs open own connection ansible vault, retrieve data.

so might have following:

package secrets  type passwordstore interface {     getkey(key string) (string, error) }  func new(backend string, config map[string]interface{}) (passwordstore, error) {     switch backend {     case "ansible":         return ansible.new(config)     default:         return nil, fmt.errorf("password store '%s' not supported.", backend)     } }   package ansible   type connection interface {     open() (string, error) }  type ansible struct {     connection connection     contents   map[string]string }  func new(c map[string]interface{}) (*ansible, error) {     conn, err := newconnection(c["ansible_path"].(string))     if err != nil {         return nil, err     }      // open connection, parse, etc...         := &ansible{         connection: conn,         contents:   parseddata,     }      return a, nil } 

so seems nice because secrets package doesn't need knowledge of ansible package dependencies (connection), , factory new's instance config data. however, if need mock connection ansible receives, there doesn't seem way (unless config map had connection option called mock)

the other option abandon factory, , assemble dependencies secrets package, like:

package secrets  type passwordstore interface {     getkey(key string) (string, error) }  func new(backend string, config map[string]interface{}) (passwordstore, error) {     switch backend {     case "ansible":         return ansible.new(ansibleconnection{}, config)     default:         return nil, fmt.errorf("password store '%s' not supported.", backend)     } }  package ansible   // same before in file, injected dependency ...  func new(connect connection, c map[string]interface{}) (*ansible, error) {     conn, err := connect.newconnection(c["ansible_path"].(string))     if err != nil {         return nil, err     }      // open connection, parse, etc...         := &ansible{         connection: conn,         contents:   parseddata,     }      return a, nil } 

now dependency injected, seems secrets needs have knowledge of every dependency every implementation.

is there more logical way structure secrets knows less? or typical top level package orchestrating everything?

what decides backend is? should guide you. i've done similar support multiple databases on project, , did basically:

  • config package reads in config file, determines backend being used
  • store package offers generic interface , has function takes config, , returns implementation
  • server package references interface
  • main package reads config, passes factory function in store, injects result server on creation

so when create server (which uses data store), pass config factory function in store, returns interface, , inject server. thing has know different concrete implementations same package exposes interface , factory; server, config, , main packages see black box.


Comments

Popular posts from this blog

What is happening when Matlab is starting a "parallel pool"? -

angular - DownloadURL return null in below code -

php - Cannot override Laravel Spark authentication with own implementation -