modules

package module
v1.16.0 Latest Latest
Warning

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

Go to latest
Published: Jan 9, 2024 License: MIT Imports: 8 Imported by: 0

README

MitzIT - Go Modules

MitzIT microservices are built on top of one or multiple modules. Each module is an independent piece and has its own domain. This package allows registering modules to a single entry point and having it all started with one call.

Installation

go get -u github.com/mitz-it/golang-modules

Usage

package main

import (
  modules "github.com/mitz-it/golang-modules"
)

func main() {
  builder := modules.NewHostBuilder()
  host := builder.Build()

  host.Run()
}

Configure the API

The HostBuilder can be used to enable Swagger and set the API base path e.g: /api

package main

import (
  modules "github.com/mitz-it/golang-modules"
  ginzap "github.com/gin-contrib/zap"
  "github.com/gin-gonic/gin"
  "github.com/mitz-it/my-microservice/docs" // import the docs folder content generated by swag
  "github.com/mitz-it/my-microservice/logger"
)

func main() {
  builder := modules.NewHostBuilder()
  builder.ConfigureAPI(func(api *modules.API) {
    api.UseSwagger(docs.SwaggerInfo)
    api.WithBasePath("/api") // /api is the default base path
    api.ConfigureRouter(func(router *gin.Engine) { // override router configurations
      router.Use(ginzap.Ginzap(logger.Log.Zap(), time.RFC3339, true))
      router.Use(ginzap.RecoveryWithZap(logger.Log.Zap(), true))
    })
  })
  host := builder.Build()

  host.Run()
}

The API can also be disabled by invoking the builder.UseAPI(false) like so:

package main

import (
  modules "github.com/mitz-it/golang-modules"
)

func main() {
  builder := modules.NewHostBuilder()
  builder.UseAPI(false) // any controller for any module will be ignored if this method is called passing false
  host := builder.Build()

  host.Run()
}

Dependency Injection

The golang-modules package provides an interface to create standardized dependency injection containers. To implement the IDependencyInjectionContainer interface the struct must have a Setup() method. As a best practice, we create a constructor function that returns both the concrete implementation of IDependencyInjectionContainer and the pointer to the module's *dig.Container.

package privategroupsmodule

import (
  modules "github.com/mitz-it/golang-modules"
  "go.uber.org/dig"
)

type DependencyInjectionContainer struct {
  container *dig.Container
}

func (di *DependencyInjectionContainer) Setup() {
  modules.SetupConfig(di.container, "../config/dev.env", "private_groups")
  modules.SetupLogging(di.container)
  di.setupCQRS()
  di.setupMessaging()
  // add configuration to the DI container for this module
}

func NewDependencyInjectionContainer() (modules.IDependencyInjectionContainer, *dig.Container) {
  container := dig.New()
  di := &DependencyInjectionContainer{
    container: container,
  }
  return di, container
}

Controllers

Controllers are a group of endpoints for the same resource or collection. To define a controller you need to implement the IController interface by adding the Register(*gin.RouterGroup) method to the controller struct and, and create a constructor function that receives a parameter of type *dig.Container.

package privategroupsmodule

import (
  "github.com/gin-gonic/gin"
  modules "github.com/mitz-it/golang-modules"
  "go.uber.org/dig"
)

type PrivateGroupsController struct {
  container *dig.Container
}

func (controller *PrivateGroupsController) Register(group *gin.RouterGroup) {
  // /api/private-groups/:id/invite
  group.POST("/:id/invite", func(ctx *gin.Context) {
    // invite the member
  })
  // /api/private-groups/:id/members
  group.GET("/:id/members", func(ctx *gin.Context) {
    // get the members
  })
}

func NewPrivateGroupsController(container *dig.Container) modules.IController {
  return &PrivateGroupsController{
    container: container,
  }
}

Workers

Workers can be used to do background processing e.g: listen to a broker for messages. To define a worker you need to implement the IWorker interface by adding the Run() method to the worker struct and, create a constructor function that receives a parameter of type *dig.Container.

package privategroupsmodule

import (
  "context"

  config "github.com/mitz-it/golang-config"
  logging "github.com/mitz-it/golang-logging"
  messaging "github.com/mitz-it/golang-messaging"
  modules "github.com/mitz-it/golang-modules"
  "go.uber.org/dig"
)

type PrivateGroupRequestsWorker struct {
  consumer messaging.IConsumer
}

func (worker *PrivateGroupRequestsWorker) Run() {
  go worker.consumer.Consume(worker.configureConsumer, worker.onMessageReceived)
}

func (worker *PrivateGroupRequestsWorker) configureConsumer(config *messaging.ConsumerConfiguration) {
  // configure the consumer ...
}

func (worker *PrivateGroupRequestsWorker) onMessageReceived(ctx context.Context, message []byte) {
  // process received messages ...
}

func NewPrivateGroupRequestsWorker(container *dig.Container) modules.IWorker {
  worker := &PrivateGroupRequestsWorker{}
  err := container.Invoke(func(config config.Config, logger *logging.Logger) {
    connectionString := config.Standard.GetString("amqp_connection_string")
    worker.consumer = messaging.NewConsumer(logger, connectionString)
  })
  if err != nil {
    panic(err)
  }
  return worker
}

Modules

Modules can be composed by zero or more controllers, and zero or more workers which will be sharing the same instance of *dig.Container.

To create a module, first create the module configuration funcion:

package privategroupsmodule

import modules "github.com/mitz-it/golang-modules"

func PrivateGroupsModule(config *modules.ModuleConfiguration) {
  // add the resource/collection path for the module
  config.WithName("/private-groups")
  // create the DI and *dig.Container instances
  di, container := NewDependencyInjectionContainer()
  // configure the *dig.Container
  di.Setup()
  config.WithDIContainer(container)
  // register the controller constructor function
  config.AddController(NewPrivateGroupsController)
  // register the worker constructor function
  config.AddWorker(NewPrivateGroupRequestsWorker)
}

FOR VERSIONS >= 1.13.0, MODULE NAMES ARE OPTIONAL

When a module is nameless, all endpoints are added to the root *gin.RouterGroup, which default path is /api.

Register the PrivateGroupsModule function using the HostBuilder instance:

package main

import (
  modules "github.com/mitz-it/golang-modules"
  "github.com/mitz-it/groups-microservice/docs" // import the docs folder content generated by swag
)

func main() {
  builder := modules.NewHostBuilder()
  builder.ConfigureAPI(func(api *modules.API) {
    api.UseSwagger(docs.SwaggerInfo)
    api.WithBasePath("/api") // /api is the default base path
  })
  // register the module
  builder.AddModule(privategroupsmodule.PrivateGroupsModule)
  // you can register as many modules as you want, e.g:
  builder.AddModule(publicgroupsmodule.PublicGroupsModule)
  host := builder.Build()

  host.Run()
}

Init Call

You can invoke a function using the *dig.Container instance from a module before the application starts.

package foomodule

import (
  modules "github.com/mitz-it/golang-modules"
  os
)

func FooModule(config *modules.ModuleConfiguration) {
  config.WithName("/foos")
  di, container := NewDependencyInjectionContainer()
  di.Setup()
  config.WithDIContainer(container)
  config.AddController(NewFoosController)
  config.AddWorker(NewFooWorker)
  config.SetupInitCall(migrateDatabase)
}

func migrateDatabase(container *dig.Container) {
    env := os.GetEnv("APPLICATION_ENVIRONMENT")

    if env != "dev" || env != "local" {
      return
    }

    err := container.Invoke(func(db DatabaseProvider) {
      if connection, err := db.Connect(); err == nil {
          connection.Migrate()
      } else {
        panic(err)
      }
    })

    if err != nil {
      panic(err)
    }
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SetupConfig

func SetupConfig(container *dig.Container, path, prefix string)

func SetupLogging

func SetupLogging(container *dig.Container)

Types

type API

type API struct {
	// contains filtered or unexported fields
}

func NewAPI

func NewAPI() *API

func (*API) ConfigureRouter

func (api *API) ConfigureRouter(configure RouterConfigFunc)

func (*API) UseSwagger

func (api *API) UseSwagger(spec *swag.Spec)

func (*API) WithBasePath

func (api *API) WithBasePath(basePath string)

type ConfigureAPIFunc

type ConfigureAPIFunc func(api *API)

type ConfigureModule

type ConfigureModule func(config *ModuleConfiguration)

type ControllerConstructorFunc

type ControllerConstructorFunc func(container *dig.Container) IController

type Host

type Host struct {
	// contains filtered or unexported fields
}

func NewHost

func NewHost(api *API, modules []*Module) *Host

func (*Host) Run

func (host *Host) Run()

type HostBuilder

type HostBuilder struct {
	// contains filtered or unexported fields
}

func NewHostBuilder

func NewHostBuilder() *HostBuilder

func (*HostBuilder) AddModule

func (builder *HostBuilder) AddModule(configure ConfigureModule)

func (*HostBuilder) Build

func (builder *HostBuilder) Build() *Host

func (*HostBuilder) ConfigureAPI

func (builder *HostBuilder) ConfigureAPI(configure ConfigureAPIFunc)

func (*HostBuilder) UseAPI

func (builder *HostBuilder) UseAPI(useAPI bool)

type IController

type IController interface {
	Register(group *gin.RouterGroup)
}

type IDependencyInjectionContainer

type IDependencyInjectionContainer interface {
	Setup()
}

type IWorker

type IWorker interface {
	Run()
}

type InitCall

type InitCall func(container *dig.Container)

type Module

type Module struct {
	// contains filtered or unexported fields
}

type ModuleConfiguration

type ModuleConfiguration struct {
	// contains filtered or unexported fields
}

func (*ModuleConfiguration) AddController

func (config *ModuleConfiguration) AddController(controllerFunc ControllerConstructorFunc)

func (*ModuleConfiguration) AddWorker

func (config *ModuleConfiguration) AddWorker(workerFunc WorkerConstructorFunc)

func (*ModuleConfiguration) SetupInitCall

func (config *ModuleConfiguration) SetupInitCall(initCall InitCall)

func (*ModuleConfiguration) WithDIContainer

func (config *ModuleConfiguration) WithDIContainer(container *dig.Container)

func (*ModuleConfiguration) WithName

func (config *ModuleConfiguration) WithName(name string)

type RouterConfigFunc

type RouterConfigFunc func(router *gin.Engine)

type WorkerConstructorFunc

type WorkerConstructorFunc func(container *dig.Container) IWorker

Jump to

Keyboard shortcuts

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