Phestus v0.1 is just weeks away from being released! Explore the latest modules and documentation.Learn more

Introduction

Introduction

Plugins are the primary way Phestus packages related modules and providers into a single installable unit.

A plugin can contain:

  • One or more modules
  • One or more providers
  • Dependencies on other plugins, modules, or providers
  • A manifest describing the plugin
  • Lifecycle hooks for initialization and shutdown
  • Capability declarations connecting providers to the modules they implement

This allows a plugin to represent a complete piece of functionality without requiring developers to manually assemble every part of it.

Why Plugins Exist

Phestus separates capabilities from their implementations.

A module defines what a system can do, while a provider implements that capability.

For example, an event module may define the event system while a Redis provider implements that system using Redis.

A plugin can package those pieces together:

Plugin
├── Module
└── Provider

This means a developer can install a plugin that provides a complete implementation without needing to manually register each provider and module.

Plugins as Composition Units

A plugin is not itself a module or provider.

Instead, it is a composition layer around them.

For example:

const plugin: PhestusPlugin = {
    manifest: {
        slug: "redis-events",
        name: "Redis Events",
        version: "0.1.0",
        provides: [
            {
                moduleSlug: "event",
                providerSlug: "redis",
            },
        ],
    },

    providers: [
        redisProvider,
    ],
};

The plugin declares that the redis provider implements the event module.

The module itself may already be installed separately:

const phestus = new Phestus({
    modules: [
        eventModule,
    ],

    plugins: [
        redisEventsPlugin,
    ],

    service,
    logger,
    eventBus,
});

The plugin therefore does not need to own every component it uses. It can provide an implementation for an existing module or package a module and its provider together.

What a Plugin Contains

The PhestusPlugin interface contains four primary areas:

interface PhestusPlugin {
    manifest: PluginManifest;

    modules?: PhestusModule[];
    providers?: PhestusProvider[];

    initialize?(context: PhestusContext): Promise<void>;
    shutdown?(context: PhestusContext): Promise<void>;
}

Manifest

Every plugin must define a manifest.

The manifest identifies the plugin and describes its dependencies and capabilities.

interface PluginManifest {
    slug: string;
    name: string;
    version: string;

    provides: PluginCapability[];

    dependencies?: PhestusDependency[];
}

Modules

Plugins can provide modules.

modules?: PhestusModule[];

This is useful when a plugin introduces an entirely new capability and therefore needs to ship both the module definition and its provider.

Providers

Plugins can provide providers.

providers?: PhestusProvider[];

This is the most common way for a plugin to connect an implementation to an existing module.

Lifecycle Hooks

Plugins may also participate in the Phestus lifecycle:

initialize?(context: PhestusContext): Promise<void>;

shutdown?(context: PhestusContext): Promise<void>;

Use these hooks when the plugin itself needs to perform startup or shutdown work.

Registering Plugins

Plugins are registered through the main Phestus configuration:

const phestus = new Phestus({
    plugins: [
        myPlugin,
    ],

    service,
    logger,
    eventBus,
});

Phestus registers the plugin, then registers any modules and providers supplied by that plugin.

After registration, the plugin and its components are available through the runtime:

phestus.getPlugin("my-plugin");
phestus.getModule("my-module");
phestus.getProvider("my-provider");

Plugins can therefore act as the installation boundary while modules and providers remain independently accessible through Phestus.

When to Create a Plugin

A plugin is useful when functionality needs to be distributed as a reusable package.

Typical examples include:

  • Database providers
  • Queue providers
  • Event providers
  • Authentication providers
  • Payment integrations
  • External API integrations
  • Complete feature packages containing new modules and providers

If the functionality is only a capability definition, it generally belongs in a module.

If the functionality implements an existing capability, it generally belongs in a provider.

If those pieces need to be distributed together, they belong in a plugin.

The Plugin Model

The overall relationship can be thought of as:

Plugin
   │
   ├── Modules
   │     └── Define capabilities
   │
   ├── Providers
   │     └── Implement capabilities
   │
   ├── Dependencies
   │     └── Define required components
   │
   └── Lifecycle
         ├── initialize()
         └── shutdown()

Plugins provide the composition layer that brings these pieces together into a reusable unit.