7

I'm looking for an equivalent of Architect for the Go language.

With Architect, modules expose "plugins". Plugins can specify dependencies, and export an API to allow interaction with other plugins. To start an application instance, you specify a list of plugins. Dependencies are resolved, and plugins are loaded (instantiated) in order.

Since each application creates a single instance of each plugin, multiple applications can be started in the same process without colliding.

Edit: I don't need the other modules to be loaded dynamically.

Alba Mendez
  • 4,432
  • 1
  • 39
  • 55
  • Go does not allow this. It is impossible to load external Go code at runtime, as the runtime-environment assumes a closed world (i.e. that all code is known at compile time). You could do something like this with inter-process communication though. – fuz Jul 19 '14 at 12:00
  • @FUZxxl The plugin pattern can still be implemented, but you would have to `import` all modules (including dependencies) at start, and pass them when starting the app. – Alba Mendez Jul 19 '14 at 12:05
  • 1
    Packer (an image building tool) has a plugin architecture that uses RPC to speak to "plugins" (Go binaries on their own): http://www.packer.io/docs/extend/developing-plugins.html – elithrar Jul 19 '14 at 12:51
  • possible duplicate of [Develop plugins in Go?](http://stackoverflow.com/questions/12444022/develop-plugins-in-go) – nemo Jul 19 '14 at 12:55
  • 1
    @nemo I don't think so. I want an implementation of the plugin pattern in Go (be it static or dynamic), but the OP in the other question focused on loading code dynamically, it seems. – Alba Mendez Jul 19 '14 at 14:09
  • A colleague of mine is working on such a system that should be ready in a few days, if you like to wait. – Not_a_Golfer Jul 19 '14 at 16:42
  • You might not need a "system" even, for compile-time plugins. Define an interface in a package that implements the core of your app, and have the package expose `myapp.RegisterPlugin(p Plugin)`. Plugin packages all `RegisterPlugin` in their `init()` functions, and declare their dependencies by importing them. You determine what plugins you want built into your binary by importing them from your `package main`. The other path is RPC/IPC, which is not as nuts as it might sound in this microservice-y (and lowish-communication-overhead-y) day and age. – twotwotwo Jul 19 '14 at 23:20
  • @twotwotwo you're right, but there's more to that. You have to register all the plugins in the appropiate order, and make sure all their dependencies are being satisfied. That's what Architect does in good part. – Alba Mendez Jul 21 '14 at 14:54
  • @Not_a_Golfer great, let us know when it's done! I'm specifically interested in the depency injection part. – Alba Mendez Jul 21 '14 at 14:57
  • @jmendeth Go's import system will do that, if each module imports everything it depends on -- init()s of modules you import run before your own init(), and the compiler/runtime sorts things out. It means no cyclic dependencies among plugins, but if we can do it for modules we can do it for plugins too. :) – twotwotwo Jul 21 '14 at 18:08
  • @twotwotwo Precisely. The best thing about that pattern is that you don't need hard dependencies. For example, a module can depend on "bus" because it needs an event bus to work. Then, you can implement several plugins that provide "bus" (a fast process-based bus, a network bus, ...). When loading your app, you can choose to load the "bus" you want, but you need to load one, and load it before your plugin. – Alba Mendez Jul 21 '14 at 21:14

1 Answers1

3

I don't of a package that does that, but have some thoughts on how to do that - hope it'll help.

  • Use a build tag for each plugin.
  • Have each plugin (file) specify in a special comment/variable its dependencies
  • Run a pre build step that generate order of initialization (toplogical sort, fail on cycles). The output is a go file which is the function called by the plugin system initialization.
  • Have Registry and Plugin interfaces, probably something like:

    type Registry {
        // Register registers a plugin under name
        Register(name string, plugin *Plugin) error
        // Get plugin by name
        Get(name string) (*Plugin, error)
    }
    
    // Global Registry
    var GlobalRegistry Registry
    
    type Plugin interface {
        // Init is called upon plugin initialization. Will be in dependency order
        Init(reg Registry) error
        // Execute plugin command
        Exec(name string, args... interface{}) (interface{}, error)
    }
    
  • Run go build -tags plugin1,plugin2
Miki Tebeka
  • 13,428
  • 4
  • 37
  • 49