Architecture

⚡ VBWD

A sales platform for the digital world — SaaS subscriptions, CMS, shop, booking and a token economy on one self-hosted backend, two Vue front-ends and one plugin contract.

landing1cmsmainchatchattheme-switcheranalytics
One backend, two Vue front-ends — the admin dashboard.
One backend, two Vue front-ends — the admin dashboard.
Why it matters

Architecture as a business advantage

Most SaaS codebases get slower as they grow — every feature touches the core, every client needs a fork, every release is a gamble. VBWD is built the opposite way: a small, hardened core with an open set of plugins around it. That one decision is what lets you move fast for years — ship a feature without touching the core, run a hundred tenants from one codebase, and onboard a client as a config diff instead of a fork.

Ship faster

A feature is a plugin, not a core change — backend, admin and customer surface in one coordinated trio. No "please rebuild the core" tickets.

Scale cleanly

One codebase, many deployments; each tenant loads only the plugins it enables, so footprint tracks need — not platform size.

No lock-in

Source-available under BSL, every payment rail interchangeable, your data in your own Postgres. Read the code →

Three runtimes

Three runtimes. One contract. Plugins everywhere.

Every corner of VBWD is its own runtime with the same plugin contract. A feature lights up by shipping a coordinated trio — a backend plugin (API + data model), an fe-admin plugin (configuration UI) and an fe-user plugin (customer surface) — or any subset; they are independent.

vbwd-backend

Python · Flask · PostgreSQL. REST API at /api/v1/*, SQLAlchemy 2.0, dependency-injector, event bus, Alembic. 292+ tests · Gunicorn · Redis cache.

vbwd-fe-admin

Vue 3 · Pinia · Vite. The back-office — users, plans, subscriptions, invoices, gateways, analytics, plugin manager. Port 8081 · Playwright E2E.

vbwd-fe-user

Vue 3 · PluginRegistry · IPlatformSDK. Customer portal + public surfaces. Topological dependency resolution. Port 8080 · 23+ plugins.

One source of truth for plugin state — enable/disable lives in ${VAR_DIR}/plugins/; the backend writes, both front-ends mount it read-only. No localStorage drift.

Backend surface

The backend ships the core, then steps aside for plugins

  • Flask blueprint auto-mounted at /api/v1/<plugin>/* on enable, unmounted on disable.
  • SQLAlchemy 2.0 models + Alembic migrations tied to the plugin lifecycle.
  • register_event_handlers(bus) — subscribe to subscription, payment, checkout and security events. No polling.
  • Emit your own domain events; other plugins consume them without direct imports.
  • Line-item handlers plug into checkout totals: discounts, taxes, shipping, token deductions.
  • Shipping providers, categories + routing, declarative permission scopes in the RBAC matrix.
  • Namespaced get_config / set_config, JSON-schema-validated, secrets via env indirection only.
Core models

A finite, named set of aggregates — not "anything goes"

Every domain has a small, named set of SQLAlchemy 2.0 entities. Plugins add their own; the core never grows by accretion.

Identity & access

User, UserDetails, UserAccessLevel, Role, UserTokenBalance, FeatureUsage.

Plans & subscriptions

TarifPlan, TarifPlanCategory, Subscription, AddOn, AddOnSubscription.

Money

Invoice, InvoiceLineItem, PaymentMethod, Tax, Country, Currency.

Glue

plugin_config, Webhook + delivery log, routing rules, categories, audit log.

Services & DI

A typed DI container. No globals. No accidental singletons.

dependency-injector wires repositories, services and infrastructure (DB session, Redis, event bus, secrets). Every consumer asks for what it needs through constructor injection; in tests, providers are swapped for in-memory repositories, a fake bus and a deterministic clock — no service knows it is mocked.

  • AuthService, UserService, SubscriptionService, InvoiceService, RefundService, TokenService.
  • PluginService, WebhookService, EmailService, EventDispatcher.
  • One repository per aggregate; multi-aggregate writes orchestrated in services, never in ORM session magic.
Patterns

SOLID as contracts, not decoration

  • S — routes validate, services orchestrate, repositories persist, models describe.
  • O — extend by adding plugins, never by editing core.
  • L — every payment provider is interchangeable; checkout never branches by provider.
  • I — the plugin SDK is segregated: emitting events needs no knowledge of line items.
  • D — services take interfaces; the container injects SQLAlchemy in prod, in-memory in tests.
  • TDD-first — no tests, no merge. DRY — a bug fix happens once. Clean code — no abbreviations.
Security

Defaults that don't get you breached on a Tuesday

  • JWT bearer + refresh, per-deploy secret, rotatable without dropping live sessions.
  • RBAC roles + access levels; plugins declare their own scopes.
  • Per-IP / per-user rate limiting; stricter caps on login + password reset.
  • Argon2id hashing; same response on bad-password and unknown-user — no enumeration.
  • HMAC-signed outbound webhooks; inbound provider webhooks verified before any state change.
  • Idempotency keys on order/capture/refund; full audit log; secrets never in plugin_config.
Go deeper

Read the architecture, end to end

The marketing tour is the short version. The documentation has the full design — and the rest of the platform is one click away.