# Microkernel (Plugin) Architecture Pattern ## Problem An application needs to support many optional features, extensions, or variations, but embedding all of them into the core creates a monolith that is hard to maintain, test, and deploy. Adding new features requires modifying and redeploying the entire system. Different customers need different feature sets. ## Solution The **Microkernel** pattern splits the system into two components: - **Core System** — Minimal functionality that defines the application's identity. Contains the plugin registry, lifecycle management, and core abstractions. - **Plugins** — Independent modules that extend the core with additional features. Each plugin implements a known interface and is registered with the core at startup or runtime. The core is stable and rarely changes. New features are added by writing new plugins, not by modifying the core. ## When to Use - Product-based applications that need per-customer customization. - IDEs, editors, browsers, and other tool platforms. - Workflow engines where processing steps vary. - Applications with a stable core but frequently changing peripheral features. ## When to Avoid - When the "core" would be trivially small and most logic lives in plugins. - High-performance systems where plugin dispatch overhead matters. - When the plugin interface is unstable and changes frequently. ## Pseudocode ``` INTERFACE Plugin: METHOD name() -> string METHOD execute(context) -> result CLASS Core: plugins = {} METHOD register(plugin: Plugin): plugins[plugin.name()] = plugin METHOD execute(plugin_name, context): plugin = plugins[plugin_name] RETURN plugin.execute(context) METHOD list_plugins(): RETURN KEYS(plugins) ``` ## Python ```python from abc import ABC, abstractmethod class Plugin(ABC): @abstractmethod def name(self) -> str: ... @abstractmethod def execute(self, data: str) -> str: ... class UpperCasePlugin(Plugin): def name(self) -> str: return "uppercase" def execute(self, data: str) -> str: return data.upper() class ReversePlugin(Plugin): def name(self) -> str: return "reverse" def execute(self, data: str) -> str: return data[::-1] class WordCountPlugin(Plugin): def name(self) -> str: return "wordcount" def execute(self, data: str) -> str: count = len(data.split()) return f"Word count: {count}" class CaesarCipherPlugin(Plugin): def __init__(self, shift: int = 3) -> None: self._shift = shift def name(self) -> str: return "caesar" def execute(self, data: str) -> str: result = [] for ch in data: if ch.isalpha(): base = ord("A") if ch.isupper() else ord("a") result.append(chr((ord(ch) - base + self._shift) % 26 + base)) else: result.append(ch) return "".join(result) class TextProcessorCore: """Core system with plugin registry.""" def __init__(self) -> None: self._plugins: dict[str, Plugin] = {} def register(self, plugin: Plugin) -> None: self._plugins[plugin.name()] = plugin print(f" [Core] Registered plugin: '{plugin.name()}'") def unregister(self, name: str) -> None: if name in self._plugins: del self._plugins[name] print(f" [Core] Unregistered plugin: '{name}'") def list_plugins(self) -> list[str]: return list(self._plugins.keys()) def execute(self, plugin_name: str, data: str) -> str: plugin = self._plugins.get(plugin_name) if not plugin: return f" [Core] ERROR: Plugin '{plugin_name}' not found" result = plugin.execute(data) return result def execute_pipeline(self, plugin_names: list[str], data: str) -> str: """Run multiple plugins in sequence.""" result = data for name in plugin_names: result = self.execute(name, result) return result def main() -> None: core = TextProcessorCore() print("--- Register plugins ---") core.register(UpperCasePlugin()) core.register(ReversePlugin()) core.register(WordCountPlugin()) core.register(CaesarCipherPlugin(3)) print(f"\n--- Available plugins: {core.list_plugins()} ---") text = "Hello World from Microkernel" print(f"\n--- Execute individual plugins on: '{text}' ---") for name in core.list_plugins(): result = core.execute(name, text) print(f" {name}: {result}") print(f"\n--- Pipeline: uppercase -> reverse ---") result = core.execute_pipeline(["uppercase", "reverse"], text) print(f" Result: {result}") print(f"\n--- Pipeline: caesar -> uppercase ---") result = core.execute_pipeline(["caesar", "uppercase"], text) print(f" Result: {result}") print("\n--- Unregister 'reverse', try executing ---") core.unregister("reverse") print(core.execute("reverse", text)) print(f" Available: {core.list_plugins()}") if __name__ == "__main__": main() ``` **Output:** ``` --- Register plugins --- [Core] Registered plugin: 'uppercase' [Core] Registered plugin: 'reverse' [Core] Registered plugin: 'wordcount' [Core] Registered plugin: 'caesar' --- Available plugins: ['uppercase', 'reverse', 'wordcount', 'caesar'] --- --- Execute individual plugins on: 'Hello World from Microkernel' --- uppercase: HELLO WORLD FROM MICROKERNEL reverse: lenrekorciM morf dlroW olleH wordcount: Word count: 4 caesar: Khoor Zruog iurp Plfurnhuqho --- Pipeline: uppercase -> reverse --- Result: LENREKORCIM MORF DLROW OLLEH --- Pipeline: caesar -> uppercase --- Result: KHOOR ZRUOG IURP PLFURNHUQHO --- Unregister 'reverse', try executing --- [Core] Unregistered plugin: 'reverse' [Core] ERROR: Plugin 'reverse' not found Available: ['uppercase', 'wordcount', 'caesar'] ``` ## Go ```go package main import ( "fmt" "strings" ) // --- Plugin interface --- type Plugin interface { Name() string Execute(data string) string } // --- Plugins --- type UpperCasePlugin struct{} func (p *UpperCasePlugin) Name() string { return "uppercase" } func (p *UpperCasePlugin) Execute(data string) string { return strings.ToUpper(data) } type ReversePlugin struct{} func (p *ReversePlugin) Name() string { return "reverse" } func (p *ReversePlugin) Execute(data string) string { runes := []rune(data) for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 { runes[i], runes[j] = runes[j], runes[i] } return string(runes) } type WordCountPlugin struct{} func (p *WordCountPlugin) Name() string { return "wordcount" } func (p *WordCountPlugin) Execute(data string) string { count := len(strings.Fields(data)) return fmt.Sprintf("Word count: %d", count) } type CaesarCipherPlugin struct { Shift int } func (p *CaesarCipherPlugin) Name() string { return "caesar" } func (p *CaesarCipherPlugin) Execute(data string) string { result := make([]rune, len([]rune(data))) for i, ch := range data { if ch >= 'A' && ch <= 'Z' { result[i] = rune((int(ch-'A')+p.Shift)%26) + 'A' } else if ch >= 'a' && ch <= 'z' { result[i] = rune((int(ch-'a')+p.Shift)%26) + 'a' } else { result[i] = ch } } return string(result) } // --- Core --- type TextProcessorCore struct { plugins map[string]Plugin order []string } func NewTextProcessorCore() *TextProcessorCore { return &TextProcessorCore{plugins: make(map[string]Plugin)} } func (c *TextProcessorCore) Register(p Plugin) { c.plugins[p.Name()] = p c.order = append(c.order, p.Name()) fmt.Printf(" [Core] Registered plugin: '%s'\n", p.Name()) } func (c *TextProcessorCore) Unregister(name string) { if _, ok := c.plugins[name]; ok { delete(c.plugins, name) var newOrder []string for _, n := range c.order { if n != name { newOrder = append(newOrder, n) } } c.order = newOrder fmt.Printf(" [Core] Unregistered plugin: '%s'\n", name) } } func (c *TextProcessorCore) ListPlugins() []string { return c.order } func (c *TextProcessorCore) Execute(name, data string) string { p, ok := c.plugins[name] if !ok { return fmt.Sprintf(" [Core] ERROR: Plugin '%s' not found", name) } return p.Execute(data) } func (c *TextProcessorCore) ExecutePipeline(names []string, data string) string { result := data for _, name := range names { result = c.Execute(name, result) } return result } func main() { core := NewTextProcessorCore() fmt.Println("--- Register plugins ---") core.Register(&UpperCasePlugin{}) core.Register(&ReversePlugin{}) core.Register(&WordCountPlugin{}) core.Register(&CaesarCipherPlugin{Shift: 3}) fmt.Printf("\n--- Available plugins: %v ---\n", core.ListPlugins()) text := "Hello World from Microkernel" fmt.Printf("\n--- Execute individual plugins on: '%s' ---\n", text) for _, name := range core.ListPlugins() { result := core.Execute(name, text) fmt.Printf(" %s: %s\n", name, result) } fmt.Println("\n--- Pipeline: uppercase -> reverse ---") result := core.ExecutePipeline([]string{"uppercase", "reverse"}, text) fmt.Printf(" Result: %s\n", result) fmt.Println("\n--- Pipeline: caesar -> uppercase ---") result = core.ExecutePipeline([]string{"caesar", "uppercase"}, text) fmt.Printf(" Result: %s\n", result) fmt.Println("\n--- Unregister 'reverse', try executing ---") core.Unregister("reverse") fmt.Println(core.Execute("reverse", text)) fmt.Printf(" Available: %v\n", core.ListPlugins()) } ``` **Output:** ``` --- Register plugins --- [Core] Registered plugin: 'uppercase' [Core] Registered plugin: 'reverse' [Core] Registered plugin: 'wordcount' [Core] Registered plugin: 'caesar' --- Available plugins: [uppercase reverse wordcount caesar] --- --- Execute individual plugins on: 'Hello World from Microkernel' --- uppercase: HELLO WORLD FROM MICROKERNEL reverse: lenrekorciM morf dlroW olleH wordcount: Word count: 4 caesar: Khoor Zruog iurp Plfurnhuqho --- Pipeline: uppercase -> reverse --- Result: LENREKORCIM MORF DLROW OLLEH --- Pipeline: caesar -> uppercase --- Result: KHOOR ZRUOG IURP PLFURNHUQHO --- Unregister 'reverse', try executing --- [Core] Unregistered plugin: 'reverse' [Core] ERROR: Plugin 'reverse' not found Available: [uppercase wordcount caesar] ``` ## Related Patterns - **Strategy** — each plugin is a strategy; the core selects and executes the appropriate strategy for each operation. - **Factory** — a plugin factory or registry creates and manages plugin instances. - **Observer** — the core can notify plugins of lifecycle events via the observer pattern. - **Pipe and Filter** — microkernel is about pluggable features; pipe and filter is about sequential data transformation steps.