Welcome!

Blog Feed Post

Three Productive Go Patterns to Put on Your Radar

By most definitions, with just 26 keywords, a terse spec, and a commitment to orthogonal features, Go is a simple language. Go programmers are expected to use the basic building blocks offered by the language to compose more complex abstractions. Over time, best-in-class solutions to frequently encountered problems tend to be discovered, shared, and replicated. These design patterns draw heritage from other languages, but often look and feel distinct in Go. I’d like to cast a spotlight on three patterns I use over and over again.

The Dependency Injection Pattern

Dependency injection (DI) is actually an umbrella term that can mean vastly different things depending on context. The core idea is this: Give or inject dependencies to a component, rather than have the component take dependencies from the environment. Beyond that, things can get a little complicated. Some people use DI to refer to dependency injection frameworks, typically a package or object into which you register dependencies and later inject them into components that use them, usually by some key schema. But this style of DI isn’t a good match for Go, primarily because Go lacks the dynamic typing required to serve a literate API. Most DI frameworks in Go resort to stringly typed keys (meaning variables are often typed as strings), and rely on reflection to reify the dependencies to concrete types or interfaces, which is always a red flag.

Instead, a more basic version of DI is particularly well-suited to Go programs. The inspiration comes from functional programming, specifically, the idea of closure scoping. And it’s nothing special, really: Just provide all the dependencies to a component as line items in the component’s constructor.

// NewHandler constructs and returns a useable request handler.

func NewHandler(

db *sql.DB,

requestDuration *metrics.Histogram,

logger *log.Logger,

) *Handler {

return &Handler{

db:     db,

dur:    requestDuration,

logger: logger,

}

}

The clear consequence of this pattern is that constructors begin to get very long, especially as business capability grows. That’s a cost. But there’s also a notable benefit. Namely, there’s great virtue in making dependencies explicit at the callsite, especially to future readers and maintainers of your code. It’s immediately obvious that the Handler takes and uses a database, a histogram, and a logger. There’s no need to hunt down dependency relationships far from the site of construction.

The writer pays a cost of keystrokes, but the reader receives the benefit of comprehension. Outside of hobby projects, we know that code is read far more often than it is written. It’s reasonable, then, to optimize for the benefit of reader—even if it comes at some expense to the writer. But we have some tricks up our sleeve to make long constructors for large components more tractable.

One approach is to use a tightly scoped config struct, containing only those dependencies used by the specific component. It’s typical to omit individual fields when building a struct, so constructors should detect nils, when appropriate, and provide sane default alternatives.

// NewHandler constructs and returns a useable request handler.

func NewHandler(c HandlerConfig) *Handler {

if c.RequestDuration == nil {

c.RequestDuration = metrics.NewNopHistogram()

}

if c.Logger == nil {

c.Logger = log.NewNopLogger()

}

return &Handler{

db:     c.DB,

dur:    c.RequestDuration,

logger: c.Logger,

}

}

// HandlerConfig captures the dependencies used by the Handler.

type HandlerConfig struct {

// DB is the backing SQL data store. Required.

DB *sql.DB

 

// RequestDuration will receive observations in seconds.

// Optional; if nil, a no-op histogram will be used.

RequestDuration *metrics.Histogram

 

// Logger is used to log warnings unsuitable for clients.

// Optional; if nil, a no-op logger will be used.

Logger *log.Logger

}

If a component has a few required dependencies and many optional dependencies, the functional options idiom may be a good fit.

// NewHandler constructs and returns a useable request handler.

func NewHandler(db *sql.DB, options …HandlerOption) *Handler {

h := &Handler{

db:     c.DB,

dur:    metrics.NewNopHistogram(),

logger: log.NewNopLogger(),

}

for _, option := range options {

option(h)

}

return h

}

// HandlerOption sets an option on the Handler.

type HandlerOption func(*Handler)

// WithRequestDuration injects a histogram to receive observations in seconds.

// By default, a no-op histogram will be used.

func WithRequestDuration(dur *metrics.Histogram) HandlerOption {

return func(h *Handler) { h.dur = dur }

}

// WithLogger injects a logger to log warnings unsuitable for clients.

// By default, a no-op logger will be used.

func WithLogger(logger *log.Logger) HandlerOption {

return func(h *Handler) { h.logger = logger }

}

By using this simplified DI pattern, we’ve made the dependency graph explicit and avoided hiding dependencies in global state. It’s also worth using a simplified definition of dependency—that is, nothing more than something that a component uses to do its work. By this definition, loggers and metrics are clearly dependencies. So by extension, they should be treated identically to other dependencies. This can seem a bit awkward at first, especially when we’re used to thinking of e.g. loggers as incidental or ubiquitous. But by lifting them up to the regular DI mechanism, we do more than establish a consistent language for expressing needs-a relationships. We’ve made our components more testable by isolating them from the shared global environment. Good design patterns tend to have this effect—not only improving the thing they’re designed to improve, but also having positive knock-on effects throughout the program.

The Client-Side Interface Pattern

Concretely, interfaces are nothing more than a collection of methods that types can choose to implement. But semantically, interfaces are much more. They define behavioral contracts between components in a system. Understanding interfaces as contracts helps us decide where and how to define them. And just as contract testing in microservice architectures teaches us that the right place to write a contract is often with the consumer.

Consider a package with a type. Go programmers frequently model that type and its constructor like the following.

package foo

// widget is an unexported concrete type.

type widget struct{ /* … */ }

 

func (w *widget) Bop(int) int                     { /* … */ }

func (w *widget) Twist(string) ([]float64, error) { /* … */ }

func (w *widget) Pull() (string, error)           { /* … */ }

 

// Widget is an exported interface.

type Widget interface {

Bop(int) int

Twist(string) ([]float64, error)

Pull() (string, error)

}

 

// NewWidget constructor returns the interface.

func NewWidget() Widget { /* … */ }

In our contract model of interfaces, this establishes the Widget contract alongside the type that implements it. But how can we predict which methods consumers actually want to use? Especially as the type grows functionality and our interface grows methods, we lose utility. The bigger the interface, the weaker the abstraction.

Instead, consider having your constructors return concrete types and letting package consumers define their own interfaces as required. For example, consider a client that only needs to Bop a Widget.

func main() {

w := foo.NewWidget() // returns a concrete *foo.Widget

process(w)           // takes a bopper, which *foo.Widget satisfies

}

 

// bopper models part of foo.Widget.

type bopper interface {

Bop(int) int

}

 

func process(b bopper) {

println(b.Bop(123))

}

 

The returned type is concrete, so all of its methods are available to the caller. The caller is free to narrow its scope of interest by capturing the interesting methods of Widget in an interface and using that interface locally. In so doing the caller defines a contract between itself and package foo: NewWidget should always produce something that I can Bop. And even better, that contract is enforced by the compiler. If NewWidget ever stops being Boppable, I’ll see errors at build time.

More icing on the cake: Testing the process function is now a lot easier, as we don’t need to construct a real Widget. We just need to pass it something that can be Bopped—probably a fake or mock struct, with predictable behavior. And this all aligns with the Code Review Comments guidelines around interfaces, which state that:

Go interfaces generally belong in the package that uses values of the interface type, not the package that implements those values. The implementing package should return concrete (usually pointer or struct) types: that way, new methods can be added to implementations without requiring extensive refactoring.

Sometimes there is a case for defining interfaces near to concrete types. For example, in the standard library, package hash defines a Hash interface, which types in subsidiary packages implement. Although the interface is defined in the producer, the semantics are the same: a tightly scoped contract that the package commits to supporting. If your contract is similarly tightly scoped and satisfied by different types with different implementations, then it may make sense to include it alongside those implementations as a signal of intent to your consumers.

The Actor Pattern

Like dependency injection, the idea of the actor pattern can mean wildly different things to different people. But at the core, it’s not much more than an autonomous component that receives input and probably produces output. In Go, we learned pretty early on that a great way to model an actor is as an infinitely looping function selecting on a block of channels. The goroutine acts as a synchronization point for state mutations in the actor, in effect making the loop body single threaded—a huge win for clarity and comprehensibility. We typically name this function `run` or `loop` and define it as a method on a struct type that holds the channels.

type Actor struct {

eventc   chan Event

requestc chan reqRes

quitc    chan struct{}

}

func (a *Actor) loop() {

for {

select {

case e := <-eventc:

a.consumeEvent(e)

case r := <-requestc:

res, err := a.handleRequest(r.req)

r.resc <- resErr{res, err}

case <-quitc:

return

}

}

}

Finally, we push onto those channels in our exported methods, forming our public API, which is naturally goroutine-safe.

func (a *Actor) SendEvent(e Event) {

a.eventc <- e

}

func (a *Actor) MakeRequest(r *Request) (*Response, error) {

resc := make(chan resErr)

a.requestc <- reqRes{req: r, resc: resc}

res := <-resc

return res.res, res.err

}

 

func (a *Actor) Stop() {

close(a.quitc)

}

 

type reqRes struct {

req  *Request

resc chan resErr

}

 

type resErr struct {

res *Response

err error

}

This works great in a lot of circumstances. But it does require us to define a unique channel per distinct public API method. It also makes things a little tricky when we need to return information to the caller. In this example, we use an intermediating `reqRes` type, with a response channel, but there are other possibilities.

There is an interesting alternative. Rather than having one channel per method, we use a single channel of unadorned functions. In the loop method, we simply execute every function that arrives; the exported methods define their functionality inline.

type Actor struct {

actionc chan func()

quitc   chan struct{}

}

 

func (a *Actor) loop() {

for {

select {

case f := <-actionc:

f()

case <-quitc:

return

}

}

}

 

func (a *Actor) SendEvent(e Event) {

a.actionc <- func() {

a.consumeEvent(e)

}

}

func (a *Actor) HandleRequest(r *Request) (res *Response, err error) {

done := make(chan struct{})

a.actionc <- func() {

defer close(done) // outer func shouldn’t return before values are set

res, err = a.handleRequest(r)

}

<-done

}

This style carries several advantages:

  1. There are fewer mechanical bits in the actor.
  2. We have much more freedom in the public API methods to return values to callers.
  3. Business logic is defined in the corresponding public API method, rather than hidden in an unexported loop method.

Your Patterns

The dependency injection, client-side interface, and actor patterns can improve your productivity, offer you more freedom, and optimize your programming. Rather than default to basic solutions, get creative and try out one or all of these three distinct Go patterns.

The post Three Productive Go Patterns to Put on Your Radar appeared first on Application Performance Monitoring Blog | AppDynamics.

Read the original blog entry...

More Stories By Jyoti Bansal

In high-production environments where release cycles are measured in hours or minutes — not days or weeks — there's little room for mistakes and no room for confusion. Everyone has to understand what's happening, in real time, and have the means to do whatever is necessary to keep applications up and running optimally.

DevOps is a high-stakes world, but done well, it delivers the agility and performance to significantly impact business competitiveness.

Latest Stories
21st International Cloud Expo, taking place October 31 - November 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA, will feature technical sessions from a rock star conference faculty and the leading industry players in the world. Cloud computing is now being embraced by a majority of enterprises of all sizes. Yesterday's debate about public vs. private has transformed into the reality of hybrid cloud: a recent survey shows that 74% of enterprises have a hybrid cloud strategy. Me...
With the rise of DevOps, containers are at the brink of becoming a pervasive technology in Enterprise IT to accelerate application delivery for the business. When it comes to adopting containers in the enterprise, security is the highest adoption barrier. Is your organization ready to address the security risks with containers for your DevOps environment? In his session at @DevOpsSummit at 21st Cloud Expo, Chris Van Tuin, Chief Technologist, NA West at Red Hat, will discuss: The top security r...
SYS-CON Events announced today that Enroute Lab will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Enroute Lab is an industrial design, research and development company of unmanned robotic vehicle system. For more information, please visit http://elab.co.jp/.
SYS-CON Events announced today that MIRAI Inc. will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. MIRAI Inc. are IT consultants from the public sector whose mission is to solve social issues by technology and innovation and to create a meaningful future for people.
IBM helps FinTechs and financial services companies build and monetize cognitive-enabled financial services apps quickly and at scale. Hosted on IBM Bluemix, IBM’s platform builds in customer insights, regulatory compliance analytics and security to help reduce development time and testing. In his session at 21st Cloud Expo, Lennart Frantzell, a Developer Advocate with IBM, will discuss how these tools simplify the time-consuming tasks of selection, mapping and data integration, allowing devel...
SYS-CON Events announced today that Mobile Create USA will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Mobile Create USA Inc. is an MVNO-based business model that uses portable communication devices and cellular-based infrastructure in the development, sales, operation and mobile communications systems incorporating GPS capabi...
There is huge complexity in implementing a successful digital business that requires efficient on-premise and cloud back-end infrastructure, IT and Internet of Things (IoT) data, analytics, Machine Learning, Artificial Intelligence (AI) and Digital Applications. In the data center alone, there are physical and virtual infrastructures, multiple operating systems, multiple applications and new and emerging business and technological paradigms such as cloud computing and XaaS. And then there are pe...
Today traditional IT approaches leverage well-architected compute/networking domains to control what applications can access what data, and how. DevOps includes rapid application development/deployment leveraging concepts like containerization, third-party sourced applications and databases. Such applications need access to production data for its test and iteration cycles. Data Security? That sounds like a roadblock to DevOps vs. protecting the crown jewels to those in IT.
SYS-CON Events announced today that Interface Corporation will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Interface Corporation is a company developing, manufacturing and marketing high quality and wide variety of industrial computers and interface modules such as PCIs and PCI express. For more information, visit http://www.i...
SYS-CON Events announced today that Keisoku Research Consultant Co. will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. Keisoku Research Consultant, Co. offers research and consulting in a wide range of civil engineering-related fields from information construction to preservation of cultural properties. For more information, vi...
SYS-CON Events announced today that SIGMA Corporation will exhibit at the Japan External Trade Organization (JETRO) Pavilion at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. uLaser flow inspection device from the Japanese top share to Global Standard! Then, make the best use of data to flip to next page. For more information, visit http://www.sigma-k.co.jp/en/.
SYS-CON Events announced today that B2Cloud will exhibit at SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. B2Cloud specializes in IoT devices for preventive and predictive maintenance in any kind of equipment retrieving data like Energy consumption, working time, temperature, humidity, pressure, etc.
Agile has finally jumped the technology shark, expanding outside the software world. Enterprises are now increasingly adopting Agile practices across their organizations in order to successfully navigate the disruptive waters that threaten to drown them. In our quest for establishing change as a core competency in our organizations, this business-centric notion of Agile is an essential component of Agile Digital Transformation. In the years since the publication of the Agile Manifesto, the conn...
While some developers care passionately about how data centers and clouds are architected, for most, it is only the end result that matters. To the majority of companies, technology exists to solve a business problem, and only delivers value when it is solving that problem. 2017 brings the mainstream adoption of containers for production workloads. In his session at 21st Cloud Expo, Ben McCormack, VP of Operations at Evernote, will discuss how data centers of the future will be managed, how th...
SYS-CON Events announced today that NetApp has been named “Bronze Sponsor” of SYS-CON's 21st International Cloud Expo®, which will take place on Oct 31 – Nov 2, 2017, at the Santa Clara Convention Center in Santa Clara, CA. NetApp is the data authority for hybrid cloud. NetApp provides a full range of hybrid cloud data services that simplify management of applications and data across cloud and on-premises environments to accelerate digital transformation. Together with their partners, NetApp em...