Go Component Design

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

External

Internal

Overview

Explicit Component Dependencies

... and the avoidance of inversion of control (?):

Prefer this style:

func main() {
  cfg := GetConfig()
  db, err := ConnectDatabase(cfg.URN)
  if err != nil {
    panic(err)
  }
  repo := NewPersonRepository(db)
  service := NewPersonService(cfg.AccessToken, repo)
  server := NewServer(cfg.ListenAddr, service)
  server.Run()
}

We get explicitness: each component is constructed in dependency order, with errors handled in-line as they occur. Each constructor enumerates its dependencies as parameters, allowing new code readers to easily build the mental model of the relationship between the components, and nothing is obscured behind layers of indirection. If a refactoring requires a component to acquire a new dependency, it simply needs to be added to the relevant constructor. The next compile will trigger errors along the dependency lists, the and the diff result in the PR will clearly show the flow of dependencies throughout the program. This brings the failure detection in miswired components at compile-time, rather than deferring to runtime.

A Go program should have little to no package global state and instead it should enumerate dependencies through constructors.

More details here: Peter Bourgon - Best Practices for Industrial Programming

Organizatorium