motif

module
v0.0.0-...-9a998da Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 10, 2023 License: BSD-3-Clause

README

motif

Motif was inspired to capitalize on the Go language for application development. Determining the patterns, or motifs, that need to be employed, is critical for writing clear idiomatic Go code. This YouTube video Edward Muller - Go Anti-Patterns, does an excellent job of framing idiomatic go. Robert Griesemer - The Evolution of Go also presents an important analogy between Go packages and LEGO® bricks. Reviewing the Go standard library packaging structure provides a blueprint for an application architecture, and underscores how essential package design is for idiomatic Go.

Package dependencies also need to be obsessively managed. Rob Pike lists an important deign goal relating copying to dependency in Go Proverbs, #8. Larger dependencies can be imported for test only to insure that the copied code is correct. Kent Beck's book on Test Driven Development, states, "Dependency is the key problem in software development of all scales." Lessening dependencies reduces complexity and increases reliability. Doug McIlroy describes the early approach taken at Bell Labs when developing and revising Research Unix:

        We used to sit around in the Unix Room saying, 'What can we throw out? Why is there this option?' It's often because there 
    is some deficiency in the basic design — you didn't really hit the right design point. Instead of adding an option, think 
    about what was forcing you to add that option.

With the release of Go generics, a new paradigm has emerged: templates. Templates are not new, having been available in C++ since 1991, and have become a standard through the work of teams like boost. The term templates is used over generics, as templates are a paradigm, and generics connotes a class of implementations. Templates in C++ also support value parameters, which if implemented in Go, would allow passing a function as a template parameter. This functionality would allow further customization of templated code.

What follows is a description of the packages in Motif, highlighting specific patterns and template implementations.

exchange

Exchange provides functionality for processing an Http request/response. Exchange functionality is provied via a templated function, utilizing template paramters for error processing, deserialization type, and the function for processing the http.Client.Do():

func DoT[E runtime.ErrorHandler, T any, H Exchange](req *http.Request) (resp *http.Response, t T, status *runtime.Status) {
    // implementation details
}

The deserialization function is also templated:

// Deserialize - templated function, providing deserialization of a request/response body
func Deserialize[E runtime.ErrorHandler, T any](body io.ReadCloser) (T, *runtime.Status) {
    // implementation details
}

Testing Http calls is implemented through a proxy design pattern: a context.Context interface that contains an http.Client.Do() call.

// Exchange - interface for Http request/response interaction
type Exchange interface {
	Do(req *http.Request) (*http.Response, error)
}

Exchange also includes a common http write response function:

// WriteResponse - write a http.Response, utilizing the data, status, and headers for controlling the content
func WriteResponse(w http.ResponseWriter, buf []byte, status *runtime.Status, headers ...string) {
    // implementation details
}

messaging

Messaging provides a way for a hosting process to communicate with packages. Packages that register themselves can then be started and pinged by the host via the templated functions:

// Ping - templated function to "ping" a registered resource
func Ping[E template.ErrorHandler](ctx context.Context, uri string) (status *runtime.Status) {
    // Implementation details
}

// Startup - templated function to startup all registered resources.
func Startup[E template.ErrorHandler, O template.OutputHandler](duration time.Duration, content ContentMap) (status *runtime.Status) {
    // Implementation details
}

runtime

Runtime implements environment, request context, status, error, and output types. The status type is used extensively as a function return value, and provides error, http, and gRPC status codes.

The error and output types are designed to be used as template parameters.

// ErrorHandler - template parameter error handler interface
type ErrorHandler interface {
	Handle(location string, errs ...error) *runtime.Status
	HandleWithContext(ctx context.Context, location string, errs ...error) *runtime.Status
	HandleStatus(s *runtime.Status) *runtime.Status
}

// OutputHandler - template parameter output handler interface
type OutputHandler interface {
	Write(s string)
}

Context functionality is provied for a request Id, and Http exchange testing:

// ContextWithRequestId - creates a new Context with a request id
func ContextWithRequestId(ctx context.Context, requestId string) context.Context {
    // implementation details
}

// ContextWithHttpExchange - create a new Context interface, containing a Http exchange function
func ContextWithHttpExchange(ctx context.Context, do func(*http.Request) (*http.Response, error)) context.Context {
    // implementation details
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL