Thanks to visit codestin.com
Credit goes to github.com

Skip to content

z5labs/bedrock

Repository files navigation

bedrock

Mentioned in Awesome Go Go Reference Go Report Card Coverage build

bedrock provides a minimal, modular and composable foundation for quickly developing more use case specific frameworks in Go.

Building custom frameworks with bedrock

One of the guiding principals for bedrock is to be composable. This principal comes from the experience gained from working with custom, tailor made frameworks which over their lifetime within an organization are unable to adapt to changing development and deployment patterns. Eventually, these frameworks are abandoned for new ones or completely rewritten to reflect the current state of the organization.

bedrock defines a small set of types and carefully chooses its opinions to balance composability and functionality, as much as it can. The result is, in fact, a framework that isn't necessarily designed for building services directly, but instead meant for building more custom, use case specific frameworks.

For example, bedrock could be used by your organizations platform engineering or framework team(s) to quickly develop internal frameworks which abstract over all of your organizations requirements e.g. OpenTelemetry, Logging, Authenticated endpoints, etc. Then, due to the high composibility of bedrock, any changes within your organization would then be very easy to adapt to within your internal framework.

Core Concepts

Builder

type Builder[T any] interface {
	Build(context.Context) (T, error)
}

Builder is a generic interface for constructing application components with context support. Builders can be composed using functional combinators like Map and Bind.

Runtime

type Runtime interface {
	Run(context.Context) error
}

Runtime is a simple abstraction over the execution of your specific application type e.g. HTTP server, gRPC server, background worker, etc.

Runner

type Runner[T Runtime] interface {
	Run(context.Context, Builder[T]) error
}

Runner executes application components built from Builders. Runners can be wrapped to add cross-cutting concerns like signal handling with NotifyOnSignal and panic recovery with RecoverPanics.

Configuration

package config

type Reader[T any] interface {
	Read(context.Context) (Value[T], error)
}

The config.Reader is arguably the most powerful abstraction defined in any of the bedrock packages. It abstracts over reading configuration values that may or may not be present, distinguishing between "not set" and "set to zero value." Readers can be composed using functional combinators like Or, Map, Bind, and Default.

Putting them altogether

Below is a tiny and simplistic example of all the core concepts of bedrock.

main.go

package main

import (
	"context"
	"log/slog"
	"os"
	"syscall"

	"github.com/z5labs/bedrock"
	"github.com/z5labs/bedrock/config"
)

func main() {
	os.Exit(run())
}

func run() int {
	// Create a runner with signal handling and panic recovery
	runner := bedrock.NotifyOnSignal(
		bedrock.RecoverPanics(
			bedrock.DefaultRunner[bedrock.Runtime](),
		),
		os.Interrupt,
		os.Kill,
		syscall.SIGTERM,
	)

	// Build and run the application
	err := runner.Run(
		context.Background(),
		bedrock.BuilderFunc[bedrock.Runtime](buildApp),
	)
	if err == nil {
		return 0
	}
	return 1
}

type myApp struct {
	log *slog.Logger
}

// buildApp constructs the application using functional config composition
func buildApp(ctx context.Context) (bedrock.Runtime, error) {
	// Read log level from environment with a default
	logLevelReader := config.Default(
		"INFO",
		config.Env("MIN_LOG_LEVEL"),
	)

	logLevel, err := config.Read(ctx, logLevelReader)
	if err != nil {
		return nil, err
	}

	// Parse the log level string
	var level slog.Level
	err = level.UnmarshalText([]byte(logLevel))
	if err != nil {
		return nil, err
	}

	return &myApp{
		log: slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
			Level: level,
		})),
	}, nil
}

// Run implements the bedrock.Runtime interface.
func (a *myApp) Run(ctx context.Context) error {
	// Do something here like:
	// - run an HTTP server
	// - start the AWS lambda runtime,
	// - run goroutines to consume from Kafka
	//   etc.

	a.log.InfoContext(ctx, "running my app")
	return nil
}

Built with bedrock

About

A framework for building other frameworks

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 5

Languages