SaaS Billing Example

A complete walkthrough of modeling a subscription billing platform with explicit module boundaries, financial invariants, and AI-safe change plans.

The Problem

Subscription billing platforms are deceptively complex. They span user identity, workspace management, plan tiers, payment processing, and invoice generation. In traditional codebases, the boundaries between these concerns blur over time: billing logic leaks into user controllers, workspace membership checks get duplicated across services, and downgrade rules end up scattered in half a dozen files.

When an AI agent is asked to "add team billing," it has no way to know which invariants must hold, which modules are affected, or what downstream entities need updating. It guesses from file names and comments. SysMARA makes all of this explicit.

System Structure

Modules

The system is divided into three modules with clear boundaries:

Each module declares its own entities, capabilities, and invariants. Cross-module references are explicit — for example, billing references workspaces.workspace to bind a subscription to a workspace.

Entities

Entity Module Description
user users A registered user with email, name, and role
workspace workspaces A tenant container with an owner and member list
subscription billing An active plan binding between a workspace and a billing tier
invoice billing A financial record with line items, status, and payment reference

Capabilities

The system defines 14 capabilities across its three modules:

users module

workspaces module

billing module

Key Invariants

The system enforces 8 invariants. These are not guidelines or comments — they are formal constraints that the SysMARA compiler validates and that AI agents must respect when proposing changes.

Invariant Severity Description
cannot_downgrade_with_unpaid_invoices critical A subscription cannot be downgraded while there are outstanding unpaid invoices. This prevents revenue loss from plan manipulation.
workspace_must_have_owner critical Every workspace must have exactly one owner at all times.
subscription_requires_workspace critical A subscription must reference a valid, active workspace.
invoice_amount_non_negative critical Invoice amounts must be zero or positive.
voided_invoice_immutable critical Once an invoice is voided, no fields can be modified.
one_active_subscription_per_workspace critical A workspace may have at most one active subscription at a time.
transfer_target_must_be_member error Workspace ownership can only be transferred to an existing member.
cancelled_subscription_no_new_invoices error No new invoices may be generated for a cancelled subscription.

Why cannot_downgrade_with_unpaid_invoices Matters

This invariant is a good example of why formal constraints matter for AI-driven development. Without it, an AI agent asked to "let users downgrade their plan" would generate a straightforward downgrade endpoint with no awareness that unpaid invoices create a financial risk. The invariant makes this rule explicit and machine-readable. The compiler will reject any change plan that introduces a downgrade path without checking invoice status.

Policy Priority Ordering

Access control uses 5 policies with explicit priority values. Higher priority policies are evaluated first and can override lower-priority ones:

  1. admin_full_access (priority: 100) — System administrators can perform any operation
  2. billing_admin (priority: 90) — Billing administrators can manage subscriptions and invoices
  3. workspace_owner (priority: 80) — Workspace owners can manage their own workspace and members
  4. workspace_member (priority: 50) — Members can read workspace data but not modify billing
  5. authenticated_user (priority: 10) — Base-level access for any logged-in user

Flows

Three flows define the primary multi-step processes:

workspace_onboarding

Steps: create_usercreate_workspacecreate_subscription. Ensures a new user gets a workspace and an initial subscription in the correct order.

billing_cycle

Steps: generate_invoice → payment processing (external) → invoice status update. Represents the recurring billing loop.

ownership_transfer

Steps: list_members → validate target → transfer_workspace. Ensures the transfer target is a current member before executing the transfer.

Module Boundaries

The module boundaries in this example are intentionally strict. The billing module references workspaces.workspace but does not reference users.user directly — it gets user context through the workspace's owner and member relationships. This means:

Change Plan Example: add-team-billing.yaml

Suppose you want to add team billing — a new plan tier that charges per-seat. Here is what the change plan looks like:

# change-plans/add-team-billing.yaml
name: add-team-billing
description: Add per-seat team billing tier with seat count tracking

add:
  entities:
    - name: seat_allocation
      module: billing
      fields:
        workspace_id:
          type: reference
          target: workspaces.workspace
        seat_count:
          type: integer
        price_per_seat:
          type: decimal

  capabilities:
    - name: update_seat_count
      module: billing
      entity: seat_allocation
      type: mutation
    - name: get_seat_allocation
      module: billing
      entity: seat_allocation
      type: query

  invariants:
    - name: seat_count_minimum_one
      module: billing
      severity: critical
      condition: "seat_allocation.seat_count >= 1"
      description: "A team plan must have at least one seat"

modify:
  entities:
    - name: subscription
      module: billing
      add_fields:
        plan_type:
          type: enum
          values: [individual, team]
        seat_allocation_id:
          type: reference
          target: billing.seat_allocation
          nullable: true

CLI Commands

Working with this example project using the SysMARA CLI:

Validate the system

npx sysmara validate

Parses all spec files, builds the system graph, and reports any invariant violations, unresolved references, or policy conflicts.

Analyze impact of a change

npx sysmara impact change-plans/add-team-billing.yaml

Shows which modules, entities, capabilities, and invariants are affected by the proposed change. For the team billing example, this would report impact on the billing module and the subscription entity, and flag the new invariant seat_count_minimum_one for review.

Explain a capability

npx sysmara explain capability billing.downgrade_subscription

Outputs the full context for a capability: which entity it operates on, which invariants constrain it, which policies govern access, and which flows include it. For downgrade_subscription, this would show the cannot_downgrade_with_unpaid_invoices invariant and the billing_admin and admin_full_access policies.

What This Example Demonstrates