eliseomartelli
HomeBlogPhotosAbout

Modular applications in go

Dec 2, 2022 - ⏱️ 2 minutes to read

I'm working on an unnamed application (I can't tell much about it now, but I will share more when I can) where I needed a way to load plugins dynamically based on the app's configuration.

I concluded that I needed a way to "store" the plugins and load them based on the application configuration. It seems that the premise is akin to the "registry" pattern.

The registry

To have an O(1) time complexity, I decided to store plugins in a "map".

// Plugin registry.
var Plugins = map[string]Plugin{}

But it doesn't automatically solve our problems. Golang offers an init function that gets called only when we first import a package. You can have any number of init functions inside a package (one per file, for example).

So the next idea is to use the init function to register our plugins.

// file plugin_name.go
package plugins

type PluginName struct {}

...

func init() {
	Plugins["plugin"] = &PluginName{}
}

We already have a functioning proof-of-concept, but we can make it better!

Let it shine

We will define an interface for our plugins with some methods to manage the plugin life cycle.

type Plugin interface {
	Setup()
	Cleanup()
}

Then I made a function to register a plugin into the register:

// Factory to provide a closure to get the Plugin.
type PluginFactory = func() Plugin

// Register SHOULD BE called by the init() function of a plugin.
// The provider will be added to the Plugin map.
func Register(pluginName string, plugin PluginFactory) {
	if _, present := Plugins[pluginName]; present {
		log.Fatalf("Plugin %s already defined.", pluginName)
	}
	Plugins[pluginName] = plugin
}

We have to change our previous plugin to use this function:

// file plugin_name.go

func init() {
	Register("plugin", func() Provider { return &PluginName{} })

}

Consuming the plugin

To use the plugins, we can, for example, iterate on a list of configured plugins, check if they're available in the map, and call the life cycle method to set them up.

for _, pluginName := range configuredPlugins {
	if plugin, present := Plugins[pluginName]; present {
		plugin.Setup()
	}
}

We now have the tools to build a modular application in go.

Subscribe to RSS

Writing

Here are some of my thoughts.

Newsletter

Stay in the loop and get news about what I have my eyes on!

Past Issues