Package core is a service container that elegantly bootstrap and coordinate twelve-factor apps in Go.

Overview

Package core is a service container that elegantly bootstrap and coordinate twelve-factor apps in Go.

Build Go Reference codecov Go Report Card Sourcegraph

Background

The twelve-factor methodology has proven its worth over the years. Since its invention many fields in technology have changed, many among them are shining and exciting. In the age of Kubernetes, service mesh and serverless architectures, the twelve-factor methodology has not faded away, but rather has happened to be a good fit for nearly all of those powerful platforms.

Scaffolding a twelve-factor go app may not be a difficult task for experienced engineers, but certainly presents some challenges to juniors. For those who are capable of setting things up, there are still many decisions left to make, and choices to be agreed upon within the team.

Package core was created to bootstrap and coordinate such services.

Overview

Whatever the app is, the bootstrapping phase is roughly composed by:

  • Read the configuration from out of the binary. Namely, flags, environment variables, and/or configuration files.

  • Initialize dependencies. Databases, message queues, service discoveries, etc.

  • Define how to run the app. HTTP, RPC, command-lines, cronjobs, or more often mixed.

Package core abstracts those repeated steps, keeping them concise, portable yet explicit. Let's see the following snippet:

package main

import (
  "context"
  "net/http"

  "github.com/DoNewsCode/core"
  "github.com/DoNewsCode/core/observability"
  "github.com/DoNewsCode/core/otgorm"
  "github.com/gorilla/mux"
)

func main() {
  // Phase One: create a core from a configuration file
  c := core.New(core.WithYamlFile("config.yaml"))

  // Phase two: bind dependencies
  c.Provide(otgorm.Providers())

  // Phase three: define service
  c.AddModule(core.HttpFunc(func(router *mux.Router) {
    router.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
      writer.Write([]byte("hello world"))
    })
  }))

  // Phase Four: run!
  c.Serve(context.Background())
}

In a few lines, an HTTP service is bootstrapped in the style outlined above. It is simple, explicit, and to some extent, declarative.

The service demonstrated above uses an inline handler function to highlight the point. Normally, for real projects, we will use modules instead. The "module" in package Core's glossary is not necessarily a go module (though it can be). It is simply a group of services.

You may note that the HTTP service doesn't really consume the dependency. That's true.

Let's rewrite the HTTP service to consume the above dependencies.

package main

import (
  "context"
  "net/http"

  "github.com/DoNewsCode/core"
  "github.com/DoNewsCode/core/otgorm"
  "github.com/DoNewsCode/core/srvhttp"
  "github.com/gorilla/mux"
  "gorm.io/gorm"
)

type User struct {
  Id   string
  Name string
}

type Repository struct {
  DB *gorm.DB
}

func (r Repository) Find(id string) (*User, error) {
  var user User
  if err := r.DB.First(&user, id).Error; err != nil {
    return nil, err
  }
  return &user, nil
}

type Handler struct {
  R Repository
}

func (h Handler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
  encoder := srvhttp.NewResponseEncoder(writer)
  encoder.Encode(h.R.Find(request.URL.Query().Get("id")))
}

type Module struct {
  H Handler
}

func New(db *gorm.DB) Module {
  return Module{Handler{Repository{db}}}
}

func (m Module) ProvideHTTP(router *mux.Router) {
  router.Handle("/", m.H)
}

func main() {
  // Phase One: create a core from a configuration file
  c := core.New(core.WithYamlFile("config.yaml"))

  // Phase two: bind dependencies
  c.Provide(otgorm.Providers())

  // Phase three: define service
  c.AddModuleFunc(New)

  // Phase four: run!
  c.Serve(context.Background())
}

Phase three has been replaced by the c.AddModuleFunc(New). AddModuleFunc populates the arguments to New from dependency containers and add the returned module instance to the internal module registry.

When c.Serve() is called, all registered modules will be scanned for implemented interfaces. The module in the example implements interface:

type HTTPProvider interface {
	ProvideHTTP(router *mux.Router)
}

Therefore, the core knows this module wants to expose HTTP service and subsequently invokes the ProvideHttp with a router. You can register multiple modules, and each module can implement one or more services.

Now we have a fully workable project, with layers of handler, repository, and entity. Had this been a DDD workshop, we would be expanding the example even further.

That being said, let's redirect our attention to other goodies package core has offered:

  • Package core natively supports multiplexing modules. You could start you project as a monolith with multiple modules, and gradually migrate them into microservices.

  • Package core doesn't lock in transport or framework. For instance, you can use go kit to construct your service, and leveraging gRPC, AMPQ, thrift, etc. Non-network services like CLI and Cron are also supported.

  • Sub packages provide support around service coordination, including but not limited to distributed tracing, metrics exporting, error handling, event-dispatching, and leader election.

Documentation

Design Principles

  • No package global state.
  • Promote dependency injection.
  • Testable code.
  • Minimalist interface design. Easy to decorate and replace.
  • Work with the Go ecosystem rather than reinventing the wheel.
  • End to end Context passing.

Non-Goals

  • Tries to be a Spring, Laravel, or Ruby on Rails.
  • Tries to care about service details.
  • Tries to reimplement the functionality provided by modern platforms.

Suggested service framework

  • Gin (if HTTP only)
  • Go Kit (if multiple transports)
  • Go Zero
Comments
  • feat: Serve run

    feat: Serve run

    feature

    • srvgrpc
      • Add healthcheck module
      • Add metrics module
    • serve
      • Split service code block
      • ~~If there is no service to apply,then service will not run. The disable option is still available.~~
      • Add more debug log information when serve run
    opened by GGXXLL 4
  • run migrations in all databases

    run migrations in all databases

    Currently, database migrate command will run the migration in default database. It is more friendly if database migrate runs ALL migrations in ALL databases.

    enhancement help wanted 
    opened by Reasno 3
  • Breaking change: config remote support more clients

    Breaking change: config remote support more clients

    opened by GGXXLL 2
  • 关于添加 RunnerProvider 的提议

    关于添加 RunnerProvider 的提议

    RunnerProvider 提供注册 Server 以及 Crontab 的能力, 所有的 Runner 收集在 run.Group 中, 统一进行启动

    • 优势
      • 避免了框架中固定的单 http server 以及单 grpc server
      • 统一了 run.Group, 由使用者控制具体启动的 runner
      • 提供了多变 Server 能力, 可由使用者提供多个 http server 或 grpc server
    enhancement 
    opened by nfangxu 2
  • config.Duration Unmarshal is error with koanf

    config.Duration Unmarshal is error with koanf

    Code:

    func TestDuration_Unmarshal(t *testing.T) {
      type d struct {
        D Duration `json:"d" yaml:"d"`
      }
    
      val := "d: 1m5s\n"
      expected := d{Duration{5*time.Second + time.Minute}}
    
      k := koanf.New(".")
      k.Load(rawbytes.Provider([]byte(val)), yaml.Parser())
    
      r := d{}
      err := k.Unmarshal("", &r)
      assert.NoError(t, err)
    }
    

    Received error:

    * 'D' expected a map, got 'string'
    
    opened by GGXXLL 2
  • Based the validators in ExportedConfig, implement a k8s admission server

    Based the validators in ExportedConfig, implement a k8s admission server

    • docs: https://kubernetes.io/zh/docs/reference/access-authn-authz/extensible-admission-controllers/

    • a good example: https://github.com/kubernetes/kubernetes/blob/v1.13.0/test/images/webhook/main.go

    help wanted 
    opened by Reasno 0
Releases(v0.13.0-beta2)
Owner
DoNews
DoNews
🦄 Easy, fast and open-source local package manager for Python!

Unikorn ?? Easy, fast and open-source local package manager for Python! Key Features Speed: You can add a package in one second.

Penguen 6 Dec 11, 2021
GoFish is a cross-platform systems package manager, bringing the ease of use of Homebrew to Linux and Windows.

GoFish is a cross-platform systems package manager, bringing the ease of use of Homebrew to Linux and Windows.

Fishworks 813 Nov 18, 2022
Package Management for Golang

Glide: Vendor Package Management for Golang Are you used to tools such as Cargo, npm, Composer, Nuget, Pip, Maven, Bundler, or other modern package ma

null 8.2k Nov 30, 2022
GoLobby DotEnv is a lightweight package for loading dot env (.env) files into structs for Go projects

DotEnv GoLobby DotEnv is a lightweight package for loading dot env (.env) files into structs for Go projects Documentation Supported Versions It requi

GoLobby 19 Nov 21, 2022
gPac - a linux package manager

gPac is a useless package manager. It is included in the gSuite, which is a suite of tools written in GO. gPac is a KISS - like package manager.

Luis 4 Mar 13, 2022
Package manager for future projects

PCKGER is a package manager for my next project but when it will be able to build binaries and move libs it will be used like a normal package manager

null 0 Dec 20, 2021
gobin is a package manager for /go/bin

gobin gobin is a package manager for /go/bin Features List installed packages. Check for updates. Install packages (like go install). Uninstall packag

Andrey Burov 18 Nov 12, 2022
📦 An independent package manager for compiled binaries.

stew An independent package manager for compiled binaries. Features Easily distribute binaries across teams and private repositories. Get the latest r

Marwan Hawari 113 Dec 4, 2022
Create and manage Isolated Virtual Environments for Go

VenGO Create and manage Isolated Virtual Environments for Golang. Motivation Why a tool to generate and manage virtual environments in Go?. Well, some

Oscar Campos 124 Sep 27, 2022
Fetch license information for all direct and indirect dependencies of your Golang project

gocomply beta Give open source Golang developers the credit they deserve, follow your legal obligations, and save time with gocomply. This tiny little

Tawesoft 16 Nov 1, 2022
Generates a simple skeleton directory structure for go/gin/bootstrap web apps

Gin Bootstrap Initializer Generates a simple skeleton directory structure for go/gin/bootstrap web apps Description This package handles building a co

Anthony hopkins 0 Jan 21, 2022
A pure Go package for coordinate transformations.

WGS84 A pure Go package for coordinate transformations. go get github.com/wroge/wgs84 Usage east, north, h := wgs84.LonLat().To(wgs84.ETRS89UTM(32)).R

Malte Wrogemann 93 Nov 25, 2022
A pure Go package for coordinate transformations.

WGS84 A pure Go package for coordinate transformations. go get github.com/wroge/wgs84 Usage east, north, h := wgs84.LonLat().To(wgs84.ETRS89UTM(32)).R

Malte Wrogemann 93 Nov 25, 2022
The Single Sign-On Multi-Factor portal for web apps

Authelia is an open-source authentication and authorization server providing two-factor authentication and single sign-on (SSO) for your applications

Authelia 14.8k Dec 8, 2022
Viper: a complete configuration solution for Go applications including 12-Factor apps

Viper v2 feedback Viper is heading towards v2 and we would love to hear what you would like to see in it. Share your thoughts here: https://forms.gle/

艹尼玛 不会用 0 Dec 6, 2021
Lightweight Go library to use in 12-factor apps.

environ Lightweight Go library to use in 12-factor apps. s, err := environ.E("FOO").AsString() s, err := environ.E("FOO").Default("foo_value").AsStrin

Petr K. 0 Jan 5, 2022
Golang k-d tree implementation with duplicate coordinate support

Golang k-d tree implementation with duplicate coordinate support

DownFlux 46 Nov 9, 2022
Remaphore - Admin tool employing NATS to coordinate processes on distributed infrastructure.

remaphore Admin tool employing NATS to coordinate processes on distributed infrastructure. Tasks on widely distributed machines often have to be coord

Aurora 3 Jan 24, 2022
Herbert Fischer 198 Oct 8, 2022
A maroto way to create PDFs. Maroto is inspired in Bootstrap and uses gofpdf. Fast and simple.

Maroto A Maroto way to create PDFs. Maroto is inspired in Bootstrap and uses Gofpdf. Fast and simple. Maroto definition: Brazilian expression, means a

Johnathan Fercher 854 Dec 2, 2022