The Plugin System
Every business feature is a plugin. A plugin is a Python package with a class, a blueprint, models, services and migrations.

The plugin class
A plugin subclasses BasePlugin and is defined in the package's __init__.py (it must be defined there, not re-exported):
from src.plugins.base import BasePlugin, PluginMetadata
class MyPlugin(BasePlugin):
@property
def metadata(self):
return PluginMetadata(name="my-plugin", version="1.0.0",
dependencies=[])
def initialize(self, config=None):
merged = {**DEFAULT_CONFIG, **(config or {})}
super().initialize(merged)
def get_blueprint(self):
from plugins.my.my.routes import my_bp
return my_bp
def get_url_prefix(self):
return "/api/v1/my"
def on_enable(self): ...
def on_disable(self): ...Lifecycle
A plugin moves through discovered → initialized → enabled. initialize() merges config; on_enable() is where you register DI providers, event handlers, data exchangers and permissions; on_disable() tears them down. The framework auto-discovers plugins from the plugins/ directory and enables those marked enabled: true in plugins.json.
File layout (new convention)
plugins/<name>/
__init__.py # the Plugin class lives here
<name>/ # source dir = the plugin id
models/ # SQLAlchemy models (extend BaseModel)
repositories/ # data access
services/ # business logic
routes.py # Flask blueprint
migrations/versions/ # plugin-owned Alembic migrations
tests/ # unit + integration with own conftest.py
populate_db.py # idempotent demo data
config.json # default config (+ debug_mode toggle)
admin-config.json # admin settings schemaRegistration
- Add the plugin to
plugins/plugins.json(enabled: true) and its config toplugins/config.json. - Put migrations in
plugins/<name>/migrations/versions/— they are auto-discovered byalembic/env.py(noalembic.iniedit needed). - Every plugin ships its own public
VBWD-platform/vbwd-plugin-<name>repository.
Declare cross-plugin dependencies in PluginMetadata.dependencies — e.g. tarot and meinchat both depend on subscription.