← Back to Fusion Home

Fusion API

Docs

Fusion Architecture Guide

Document Info

Field Value
Author Jason Welch
Status Active / Implemented
Created December 5, 2025
Last Updated February 24, 2026

Executive Summary

Fusion is a unified platform that replaces the fragmented integrations across Elm Street Technology's product ecosystem (IDX Broker, Outbound Engine, VoicePad, AIVA, CRM) with a clean, modular architecture.

At its core is the Party Model — an industry-standard pattern (Salesforce, SAP, Oracle) that represents any participant in the system — person, organization, or external system — in a consistent, extensible way. Around this identity core, Fusion layers Billing, Subscriptions, and Product Catalog modules that integrate with Zuora as the billing provider.

This architecture solves key challenges around:


The Problem Fusion Solves

Fragmented Identity

Today Problem
Auth0 knows who logs in But not who they are in the business
IDX Broker has agent IDs But they're flat fields, not relationships
Developer Partners Are they vendors? Customers? Admins? All three?
MLS data has listings With agents, co-agents, offices — no way to model this

Fragmented Products

Today Problem
Tight coupling between products Updates in one app break another
Complex integrations IDX B ↔ IXACT Contact relies on custom Auth0 hacks
No unified lead flow Contacts scattered across products with no routing logic
No cross-product visibility Can't answer "what does this customer subscribe to?"

Fragmented Billing

Today Problem
Zuora IDs are flat fields No structured relationship to identity
Dev partner billing is ad-hoc No clear model for aggregate vs partner-controlled
Subscription data lives in Zuora No local system of record for subscription state
Rate plan changes are unguarded No enforcement of business rules around plan dependencies

Result: We can't answer simple questions like:

Example Scenario

Natalie More is:

  1. An Auth0 user who logs into Fusion
  2. An individual agent with her own IDX Broker account (#60869)
  3. A member of SWFLGC MLS with agent ID 26563084
  4. The listing agent on property 2025017196
  5. Managed by developer partner "BulletProof Real Estate Agent"
  6. Billed via Zuora with a client_direct billing arrangement
  7. Subscribed to IDX Broker Core (Monthly) with an MLS fee

Question: How do we connect all of these identities, products, and billing relationships?


System Architecture

Module Structure

Fusion uses a modular Laravel architecture with four bounded modules:

modules/
├── Identity/        Core identity: parties, users, products, customer provisioning
├── Billing/         Billing accounts, invoices, payments, payment methods
├── Subscriptions/   Subscriptions, rate plans, catalog, amendments
└── Zuora/           Zuora API adapter implementing Billing + Subscription contracts

Each module has its own models, actions, controllers, tests, events, and service provider. Modules communicate through contracts (interfaces), never through direct class references to another module's internals.

Architecture Layers

┌─────────────────────────────────────────────────────────────────────────────────┐
│  AUTHENTICATION LAYER                                                           │
│  Auth0 (External IdP)                                                           │
│  • Handles SSO, social login, MFA                                               │
│  • Owns: sub (subject identifier), email                                        │
└───────────────────────────────────────────────┬─────────────────────────────────┘
                                                │
                                                ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│  APPLICATION LAYER                                                              │
│  users table                                                                    │
│  • Links Auth0 to internal system                                               │
│  • Owns: auth0_sub, email                                                       │
│  • References: party_id → The person behind this login                          │
└───────────────────────────────────────────────┬─────────────────────────────────┘
                                                │
                                                ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│  PARTY LAYER (Identity Module)                                                  │
│  parties — The canonical identity AND customer account                          │
│  • type: person, organization, system                                           │
│  • Owns: first_name/last_name (persons) / display_name (orgs)                   │
│                                                                                 │
│  party_identifiers — External system IDs (MLS member IDs, idx_id)               │
│  party_groups / party_group_members — Flexible grouping with roles              │
│  party_roles — Role a party plays on business objects (listing_agent, etc.)      │
│  party_product — Links parties to products with roles (owner, agent, admin)      │
└───────────────────────┬───────────────────────┬─────────────────────────────────┘
                        │                       │
         ┌──────────────┘                       └──────────────┐
         ▼                                                     ▼
┌─────────────────────────────────┐     ┌──────────────────────────────────────────┐
│  PRODUCT LAYER (Identity)       │     │  BILLING LAYER (Billing Module)          │
│                                 │     │                                          │
│  products                       │     │  billing_providers (Zuora)               │
│  • IDX Broker, Outbound Engine  │     │  billing_accounts                        │
│  • VoicePad, AIVA, CRM         │     │  • Links Party to BillingProvider         │
│                                 │     │  • external_account_id (Zuora ID)        │
│  product_accounts               │     │  • billing_type: client_direct,          │
│  • Specific account instance    │     │    partner_controlled, aggregate         │
│  • product_account_id: 60869    │     │                                          │
│  • billing_account_id → billing │     │  invoices, payments, payment_methods     │
│                                 │     │  • Full billing lifecycle                │
└─────────────────────────────────┘     └──────────────────────────────────────────┘
         │                                                     │
         └──────────────────────┬──────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│  SUBSCRIPTION LAYER (Subscriptions Module)                                      │
│                                                                                 │
│  subscriptions — Synced from Zuora via SyncAccountSubscriptions                 │
│  subscription_items — Individual charges on a subscription                      │
│                                                                                 │
│  PRODUCT CATALOG:                                                               │
│  rate_plan_groups — Organize rate plans (selection_mode: one/many)               │
│  product_rate_plans — Catalog entries with group + dependency references         │
│  product_pricing — Charges/prices for each rate plan                            │
│  mls_fee_mappings — MLS-specific fee rate plans                                 │
│                                                                                 │
│  AMENDMENT SERVICE:                                                             │
│  SubscriptionAmendmentService — Single entry point for all plan changes         │
│  • Enforces exclusive groups, required groups, cascading dependencies            │
│  • Idempotent: duplicate adds and missing removes are safe no-ops               │
└─────────────────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│  PROVIDER LAYER (Zuora Module)                                                  │
│                                                                                 │
│  ZuoraAdapter — Implements BillingProviderContract + SubscriptionProviderContract│
│  • Account CRUD, invoices, payments, payment methods                            │
│  • Subscription CRUD, rate plan add/remove/swap                                 │
│  • Product catalog sync                                                         │
│  • Mappers convert Zuora responses to provider-agnostic data objects            │
└─────────────────────────────────────────────────────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────────────────────┐
│  BUSINESS OBJECTS LAYER (Future)                                                │
│  listings, transactions, leads, contacts                                        │
│  • Connected to parties via party_roles                                         │
│  • Routed via subscription data (Contact Service)                               │
└─────────────────────────────────────────────────────────────────────────────────┘

Entity Relationship Diagram

erDiagram
    Auth0 ||--o| User : authenticates
    User ||--o| Party : "is a"

    Party ||--o{ PartyIdentifier : has
    Party ||--o{ PartyGroupMember : "member of"
    Party ||--o{ PartyRole : plays
    Party ||--o{ PartyProduct : "has access to"
    Party ||--o{ BillingAccount : "billed via"

    PartyGroup ||--o{ PartyGroupMember : contains
    PartyGroup }o--o| Party : "created by"

    PartyType ||--o{ Party : classifies
    PartySubtype ||--o{ Party : classifies
    PartyType ||--o{ PartySubtype : contains

    RoleType ||--o{ PartyRole : defines

    PartyProduct }o--|| Product : "for"
    PartyProduct }o--o| ProductAccount : references

    Product ||--o{ ProductAccount : has
    Product ||--o{ RatePlanGroup : "catalog groups"
    ProductAccount }o--o| BillingAccount : "billed via"

    BillingAccount }o--|| BillingProvider : "at"
    BillingAccount ||--o{ Subscription : has
    BillingAccount ||--o{ Invoice : has
    BillingAccount ||--o{ PaymentMethod : has
    Invoice ||--o{ Payment : "paid by"

    Subscription ||--o{ SubscriptionItem : contains

    RatePlanGroup ||--o{ ProductRatePlan : contains
    RatePlanGroup }o--o| RatePlanGroup : "depends on"
    ProductRatePlan ||--o{ ProductPricing : "priced by"
    ProductRatePlan }o--o| ProductRatePlan : "requires"

    PartyRole }o--|| Listing : "on"

Identity Module

Party — "Who Is This?"

A Party is any entity that participates in business relationships. This is both the identity and the customer account in one model — no separate "Tenant" or "Account" model needed.

Every person, company, MLS, and system in Fusion is a Party.

Party Types

Type Description Subtype Examples Real Examples
Person An individual human agent, broker, client Natalie More, Anthony Colletti
Organization A company, office, or group office, developer_partner, team Realty One Group MVP, BulletProof Real Estate Agent
System An external system or data source mls SWFLGC MLS (b001)

Party Structure

Party
├── first_name / last_name (for persons)
├── display_name (for organizations)
├── metadata (JSON — phones, emails, extra data)
├── PartyIdentifiers → External IDs (MLS agent #, idx_id)
├── PartyGroups → Group memberships (via PartyGroupMember)
├── PartyRoles → Roles on business objects (listings)
├── PartyProducts → Product access with roles
├── BillingAccounts → Billing relationships
└── User → Auth0 login (optional — not all parties log in)

PartyIdentifier — "What External IDs Do They Have?"

External identifiers from other systems that belong to a Party. Parties can have multiple IDs from multiple sources.

Party Source Type External ID
Natalie More SWFLGC MLS member_id 26563084
Realty One Group MVP SWFLGC MLS office_id 5887258
SWFLGC MLS idx_id b001
BulletProof Real Estate Agent Zuora zuora_account_id 8a368af09222dcf101922ad92eb127b2

PartyGroup — "What Groups Are They In?"

A named group of Parties used to organize entities together. Members join groups via PartyGroupMember with a role and status.

Why groups? Groups replace individual party-to-party relationships with a single, flexible many-to-many pattern. Office affiliations, developer partner client lists, brokerage teams, and employment all use the same mechanism — a PartyGroup with members who have explicit roles. This avoids a proliferation of relationship types and makes it easy to query "who belongs to what" in one place.

Group Structure

PartyGroup
├── name → Human-readable group name
├── description → Optional description
├── created_by_party_id → Party that created the group
└── members → Parties in this group (via PartyGroupMember)

PartyGroupMember (Pivot)

Field Type Description
party_group_id uuid The group
party_id uuid The party member
role string Role within the group (e.g., "admin", "member", "client")
status enum active, pending

Common Group Patterns

Developer Partner Managing Clients:

PartyGroup: "BulletProof Clients"
├── created_by: BulletProof Real Estate Agent
├── PartyGroupMember: BulletProof Real Estate Agent (role: admin)
├── PartyGroupMember: Natalie More (role: client)
└── PartyGroupMember: Bob Smith (role: client)

Office Affiliation (replaces affiliated_with relationship):

PartyGroup: "Realty One Group MVP Agents"
├── created_by: Realty One Group MVP
├── PartyGroupMember: Realty One Group MVP (role: owner)
├── PartyGroupMember: Bob Broker (role: team_lead)
├── PartyGroupMember: Natalie More (role: member)
└── PartyGroupMember: Alice Agent (role: member)

Internal Team (replaces employed_by relationship):

PartyGroup: "BulletProof Team"
├── created_by: BulletProof Real Estate Agent
├── PartyGroupMember: BulletProof Real Estate Agent (role: admin)
└── PartyGroupMember: Anthony Colletti (role: member)

PartyRole — "What Role Do They Play on Things?"

A role that a Party plays on a business object (listing, transaction, lead). Uses polymorphic relationships to connect parties to any subject type.

ID Name Category Description
1 listing_agent real_estate Represents seller on listing
2 co_listing_agent real_estate Co-agent on listing
3 buyer_agent real_estate Represents buyer
4 listing_office real_estate Office associated with listing
5 selling_office real_estate Office that brought the buyer
Listing "2025017196"
├── PartyRole: Natalie More (listing_agent, is_primary: true)
├── PartyRole: Bob Smith (co_listing_agent)
└── PartyRole: Realty One Group MVP (listing_office)

PartyProduct — "What Products Can They Access?"

Links a Party to a Product with a specific role and account instance. Multiple people can access the same product account with different permissions.

Role Meaning
owner Owns the account, can close it
admin Can manage settings and users
agent Can use the product (on a brokerage account)
developer_partner External partner with admin access

Individual Agent:

Party: Natalie More
└── PartyProduct (role: owner)
      ├── Product: IDX Broker
      └── ProductAccount: #60869

Brokerage Office:

Party: Bob Broker ──── PartyProduct (role: owner) ────┐
                                                      │
Party: Natalie More ── PartyProduct (role: agent) ────┼──▶ ProductAccount: #78901
                                                      │    Product: IDX Broker
Party: Alice Agent ─── PartyProduct (role: agent) ────┘

Developer Partner Managing Client:

Party: Natalie More ───────── PartyProduct (role: owner) ──────────┐
                                                                    │
Party: BulletProof Real Estate ── PartyProduct (role: developer_partner) ──▶ ProductAccount: #60869

User — "How Do They Log In?"

An application login account linked to Auth0. Separates authentication from identity — not every Party needs to log in (imported agents, offices, MLSs don't).

Field Description
auth0_sub Auth0 subject identifier (unique)
email Email for lookups and notifications
party_id Reference to the Party this user represents

The user's name is NOT stored on User — it lives on the Party. Access via $user->party->first_name.


Product & ProductAccount

Product is a product offering in our portfolio:

ID Name
1 IDX Broker
2 Outbound Engine
3 AIVA
4 VoicePad
5 CRM

ProductAccount is a specific instance of a product subscription:

ProductAccount
├── product_id: 1 (IDX Broker)
├── product_account_id: 60869
├── product_account_id_type: int
└── billing_account_id → BillingAccount

Customer View

The customers table is a database view (not a physical table) that aggregates data across Party, ProductAccount, BillingAccount, and Product into a flat read-only representation. Useful for listing/searching customers without complex joins.

The Customer model includes a product-aware account() relationship — when product_name is "IDX Broker", it resolves to the IDX Broker clientInfo table via product_account_id → accountID.


IDX Broker Integration

Fusion connects to the existing IDX Broker system through several touch points:

  1. Separate Database Connectionidx_connection connects to the IDX Broker MySQL database
  2. IDX Client Model (app/Models/IDX/Client.php) — reads from the clientInfo table
  3. ProductAccountproduct_account_id maps to IDX Broker's accountID
  4. PartyIdentifier — stores idx_id for MLS parties (e.g., b001 for SWFLGC MLS)
  5. Customer View — aggregates Party + ProductAccount + IDX Client data for display

Billing Module

BillingProvider

An external billing system we integrate with. Currently Zuora is the sole provider.

BillingProvider
├── code: "zuora"
├── name: "Zuora"
└── is_active: true

BillingAccount — "How Does Billing Work?"

Links a Party to a billing provider with billing configuration. Tracks the billing relationship, external IDs, and preferences.

Billing Types

Billing Type How It Works
client_direct Client pays their own bill directly
partner_controlled Dev partner's card charged per client transaction
aggregate Dev partner invoiced monthly for all their clients

Each billing type maps to different Zuora account settings:

Setting client_direct partner_controlled aggregate
auto_pay true true true
payment_term Due Upon Receipt Due Upon Receipt Net 30
batch Batch1 Batch1 Batch2
bill_cycle_day 0 (signup day) 0 (signup day) 25

BillingAccount Fields

Field Type Description
party_id uuid (FK) The party being billed
billing_provider_id int (FK) The billing provider (Zuora)
external_account_id string ID in the external billing system
billing_type enum client_direct, partner_controlled, aggregate
bill_cycle_day int Day of month for billing
currency string Currency code (default: USD)
payment_status string Current payment status
status enum active, suspended, closed
synced_at timestamp Last sync with billing provider

Related Billing Models

Model Description
Invoice Invoices generated for billing accounts
InvoiceItem Line items on invoices
PaymentMethod Payment methods attached to accounts
Payment Payment records for invoices

Subscriptions Module

The Subscriptions module is the local system of record for subscription data. While Zuora is the source of truth for billing, Fusion maintains a synced copy of subscription state to enable:

Subscription — "What Are They Subscribed To?"

A subscription belongs to a BillingAccount and contains SubscriptionItems (individual charges).

Subscription
├── billing_account_id → BillingAccount
├── external_subscription_id → Zuora subscription ID
├── status: active, cancelled, suspended, pending
├── current_term_start / current_term_end
├── synced_at → Last sync timestamp (freshness check)
└── items[] → SubscriptionItem (the actual charges)

SubscriptionItem — "What Charges Are On This Subscription?"

Each item represents a single charge/rate plan on the subscription.

Field Description
subscription_id Parent subscription
product_account_id Optional link to ProductAccount
external_item_id Zuora charge ID
name Charge name (e.g., "IDX Broker Core")
description Rate plan name from Zuora
quantity Number of units
unit_amount Price per unit
charge_type recurring, one_time, usage
billing_frequency monthly, annual
effective_start_date When this charge became active
effective_end_date When terminated (null = still active)
synced_at Last sync timestamp

Active items have effective_end_date = null. When a rate plan is removed from Zuora, the corresponding item gets an effective_end_date set.


Product Catalog: Rate Plan Groups

The product catalog organizes Zuora rate plans into groups that define selection behavior and dependencies. This is the "Fusion group method" — it maps Zuora's flat catalog into a structured, rule-enforced UI.

RatePlanGroup

Groups organize rate plans for a product with selection rules:

Field Description
product_id Which product this group belongs to
slug URL-friendly identifier (e.g., "plan-tier")
name Display name (e.g., "Plan Tier")
selection_mode one (radio — exclusive) or many (checkbox — multi-select)
is_required If true, at least one plan in this group must always be active
sort_order Display ordering
requires_group_id Another group that must have an active plan before this one can be used

ProductRatePlan

Individual rate plans from the Zuora catalog, mapped to local groups:

Field Description
product_id Product this plan belongs to
billing_provider_id Zuora
rate_plan_group_id Which group this plan is in
external_product_id Zuora product ID
external_rate_plan_id Zuora rate plan ID
name Plan name (e.g., "IDX Broker Core")
billing_frequency monthly or annual
is_default Whether this is the default plan in its group
sort_order Display ordering within group
requires_rate_plan_id Another plan that must be active before this one

How Groups Work — Example

Product: IDX Broker
│
├── RatePlanGroup: "Plan Tier" (selection_mode: one, is_required: true)
│   ├── ProductRatePlan: "IDX Broker Lite" (Monthly)
│   ├── ProductRatePlan: "IDX Broker Core" (Monthly) ← is_default
│   └── ProductRatePlan: "IDX Broker Platinum" (Monthly)
│
├── RatePlanGroup: "Add-Ons" (selection_mode: many, requires_group: "Plan Tier")
│   ├── ProductRatePlan: "Social Pro" (requires: "IDX Broker Core")
│   └── ProductRatePlan: "Home Valuation"
│
└── RatePlanGroup: "MLS Fees" (selection_mode: many, requires_group: "Plan Tier")
    ├── ProductRatePlan: "SWFLGC MLS Fee"
    └── ProductRatePlan: "CRMLS Fee"

Selection rules enforced by SubscriptionAmendmentService:

ProductPricing

Each rate plan has pricing entries (charges):

Field Description
external_charge_id Zuora charge ID
name Charge name
charge_type recurring, one_time, usage
amount Price
currency Currency code (USD)
pricing_model flat_fee, per_unit, tiered, volume

MlsFeeMapping

Maps MLS codes to Zuora rate plans for per-MLS billing:

Field Description
mls_code MLS identifier
mls_name Display name
external_rate_plan_id Zuora rate plan ID for fee
external_charge_id Zuora charge ID
amount Fee amount

Subscription Amendment Service

The SubscriptionAmendmentService is the single entry point for all subscription rate plan changes. It orchestrates business rules that the underlying actions are intentionally unaware of.

Operations

Method What It Does
addRatePlan Adds a plan; auto-swaps in exclusive groups; checks deps
removeRatePlan Removes a plan; cascades dependent removals; guards required
swapRatePlan Explicitly swaps one plan for another
removeByItem Removes by SubscriptionItem (resolves the plan internally)

Safety Guarantees

Mutation Guard

Every subscription mutation passes through the SubscriptionMutationGuard:

SubscriptionMutationGuard
├── SubscriptionStateValidator.preCheck() → Quick validation (is subscription active?)
├── SubscriptionLockService.executeWithLock() → Acquires advisory lock
└── SubscriptionStateValidator.validateForMutation() → Full validation under lock

Subscription Sync Flow

Fusion keeps local subscription data fresh by syncing from Zuora on demand:

1. API request hits SubscriptionController
2. SyncAccountSubscriptions checks freshness (configurable, default 5 min)
3. If stale → calls SubscriptionProviderContract.getSubscriptionsForAccount()
4. ZuoraAdapter queries Zuora API with expanded rate plans and charges
5. SubscriptionMapper converts Zuora response → provider-agnostic SubscriptionDetailsData
6. SyncSubscription.syncFromData() upserts local Subscription + SubscriptionItems
7. Items no longer present in Zuora get terminated (effective_end_date set)

Zuora Module

The Zuora module is a pure adapter — it implements two contracts from other modules and converts between Zuora's API format and Fusion's provider-agnostic data objects.

Contracts Implemented

Contract Module Purpose
BillingProviderContract Billing Account CRUD, invoices, payments
SubscriptionProviderContract Subscriptions Subscription CRUD, catalog, rate plans

Mappers

Mapper Converts
AccountMapper Zuora account ↔ AccountData
SubscriptionMapper Zuora subscription ↔ SubscriptionDetailsData
InvoiceMapper Zuora invoice ↔ InvoiceData
PaymentMapper Zuora payment/method ↔ PaymentData/PaymentMethodData
ProductMapper Zuora catalog ↔ ProductData/MlsFeeRatePlanData

Key Design Decision: Provider Agnostic

All data flowing between modules uses provider-agnostic data objects (SubscriptionDetailsData, RatePlanData, RatePlanChargeData, etc.). If Zuora were replaced with another billing provider, only the Zuora module would change — the Billing and Subscriptions modules would remain untouched.


Customer Provisioning

CreateCustomerAccount Flow

When a new IDX Broker customer is created, CreateCustomerAccount orchestrates the full provisioning:

CreateCustomerAccount
├── CreateMlsParty      → Party (type: system, subtype: mls) + PartyIdentifier (idx_id)
├── CreateOfficeParty    → Party (type: org, subtype: office) + PartyIdentifier (office_id)
├── CreateDeveloperPartner → Party (type: org, subtype: developer_partner) + owner Party
├── CreateAgentParty     → Party (type: person, subtype: agent) + identifiers + group memberships
├── CreateUser           → User (auth0_sub, email, party_id)
├── CreateTenant         → Tenant + party_tenant links (legacy, being phased out)
├── CreateProductAccount → ProductAccount + BillingAccount + tenant_products link
└── PartyGroup           → agent added to developer partner's client group

ProvisionCustomer (Batch Import)

For bulk imports, ProvisionCustomer provides a streamlined path:

ProvisionCustomer
├── Get or create Party
├── Find Product by name
├── Ensure BillingAccount (if Zuora ID provided)
├── Create ProductAccount with billing_account_id
└── Link Party → Product → ProductAccount via party_product

Complete Data Flow — Real-World Example

Natalie More: Individual Agent with Developer Partner

User (nataliejmore@gmail.com)
│   auth0_sub: "auth0|6762de3a385c95f62d29e2cf"
│
└──▶ Party: Natalie More (person/agent)
        │
        ├── PartyIdentifier
        │     ├── source: SWFLGC MLS (b001)
        │     ├── type: "member_id"
        │     └── external_id: "26563084"
        │
        ├── PartyGroupMember
        │     ├── group: "Realty One Group MVP Agents" (role: member)
        │     └── group: "BulletProof Clients" (role: client)
        │
        ├── PartyProduct (role: owner)
        │     ├── Product: IDX Broker
        │     └── ProductAccount: #60869
        │           └── billing_account_id → BillingAccount
        │
        ├── BillingAccount
        │     ├── billing_provider: Zuora
        │     ├── external_account_id: "8a3694b893d3e0000193da3422b80bb9"
        │     ├── billing_type: client_direct
        │     └── status: active
        │     │
        │     └── Subscription (synced from Zuora)
        │           ├── status: active
        │           ├── SubscriptionItem: "IDX Broker Core" ($49.99/mo, recurring)
        │           └── SubscriptionItem: "SWFLGC MLS Fee" ($10.00/mo, recurring)
        │
        └── PartyRole
              ├── role_type: "listing_agent"
              ├── subject_type: "listing"
              └── subject_id: "2025017196"

Developer Partner: BulletProof Real Estate Agent

Party: BulletProof Real Estate Agent (org/developer_partner)
│
├── BillingAccount
│     ├── billing_provider: Zuora
│     ├── external_account_id: "8a368af09222dcf101922ad92eb127b2"
│     ├── billing_type: aggregate
│     └── bill_cycle_day: 25
│
├── PartyGroup: "BulletProof Clients"
│     ├── BulletProof Real Estate Agent (role: admin)
│     ├── Natalie More (role: client)
│     └── Bob Smith (role: client)
│
├── PartyProduct (role: developer_partner)
│     └── ProductAccount: #60869 (Natalie's account — admin access)
│
└── PartyGroup: "BulletProof Team"
      ├── BulletProof Real Estate Agent (role: admin)
      └── Anthony Colletti (role: member)
            └── User: webteam@bulletproofrealestateagent.com

Complete Data Flow Diagram

flowchart TD
    subgraph auth [Authentication]
        Auth0[Auth0 IdP]
    end

    subgraph app [Application]
        User[User<br/>auth0_sub, party_id]
    end

    subgraph identity [Identity Module]
        Party[Party<br/>type, name]
        PartyId[PartyIdentifier<br/>MLS IDs, idx_id]
        PartyGroup[PartyGroup<br/>named groups]
        PartyGroupMember[PartyGroupMember<br/>role, status]
        PartyRole[PartyRole<br/>listing_agent]
    end

    subgraph products [Products]
        PartyProduct[PartyProduct<br/>role: owner/agent/admin]
        Product[Product<br/>IDX Broker, OE, etc.]
        ProductAccount[ProductAccount<br/>#60869]
    end

    subgraph billing [Billing Module]
        BillingProvider[BillingProvider<br/>Zuora]
        BillingAccount[BillingAccount<br/>billing_type, status]
        Invoice[Invoice]
        PaymentMethod[PaymentMethod]
    end

    subgraph subscriptions [Subscriptions Module]
        Subscription[Subscription<br/>status, term dates]
        SubItem[SubscriptionItem<br/>name, amount, charge_type]
        RatePlanGrp[RatePlanGroup<br/>selection_mode, dependencies]
        ProdRatePlan[ProductRatePlan<br/>catalog entries]
        AmendmentSvc[SubscriptionAmendmentService<br/>add, remove, swap]
    end

    subgraph zuora [Zuora Module]
        ZuoraAdapter[ZuoraAdapter<br/>BillingProvider + SubscriptionProvider]
        Mappers[Mappers<br/>Account, Subscription, Invoice, Product]
    end

    subgraph business [Business Objects — Future]
        Listing[Listings]
        Transaction[Transactions]
    end

    Auth0 --> User
    User --> Party
    Party --> PartyId
    Party --> PartyGroupMember
    PartyGroupMember --> PartyGroup
    Party --> PartyRole
    Party --> PartyProduct
    Party --> BillingAccount

    PartyProduct --> Product
    PartyProduct --> ProductAccount
    ProductAccount --> BillingAccount
    BillingAccount --> BillingProvider
    BillingAccount --> Invoice
    BillingAccount --> PaymentMethod
    BillingAccount --> Subscription
    Subscription --> SubItem

    Product --> RatePlanGrp
    RatePlanGrp --> ProdRatePlan
    AmendmentSvc --> Subscription

    ZuoraAdapter --> BillingAccount
    ZuoraAdapter --> Subscription
    Mappers --> ZuoraAdapter

    PartyRole --> Listing
    PartyRole --> Transaction

Table Specifications

parties

Column Type Description
id uuid Primary key
party_type_id int (FK) person, organization, or system
party_subtype_id int (FK) agent, broker, office, mls, etc. (optional)
first_name string For persons
last_name string For persons
display_name string For organizations
metadata json Flexible additional data (phones, emails, etc.)
created_at timestamp
updated_at timestamp

party_identifiers

Column Type Description
id uuid Primary key
party_id uuid (FK) The party this identifier belongs to
source_party_id uuid (FK) The system that issued this ID (nullable)
identifier_type string(50) member_id, office_id, idx_id, zuora_account_id
external_id string(100) The actual ID value
data_type string(20) int, varchar, guid (for proper casting)
created_at timestamp
updated_at timestamp

party_groups

Column Type Description
id uuid Primary key
name string Name of the group (max 150 chars)
description text Optional description
created_by_party_id uuid (FK) Party that created this group
created_at timestamp
updated_at timestamp

party_group_members

Column Type Description
id uuid Primary key
party_group_id uuid (FK) The group
party_id uuid (FK) The party member
role string Role within the group (default: "member")
status enum active, pending (default: active)
created_at timestamp
updated_at timestamp

Unique constraint: (party_group_id, party_id)

party_roles

Column Type Description
id uuid Primary key
party_id uuid (FK) The party playing this role
role_type_id int (FK) Type of role
subject_type string(50) Polymorphic type (listing, transaction, lead)
subject_id uuid Polymorphic ID
is_primary boolean Is this the primary party for this role?
created_at timestamp
updated_at timestamp

party_product

Column Type Description
id uuid Primary key
party_id uuid (FK) The party
product_id int (FK) The product
product_account_id uuid (FK) The specific account instance (nullable)
role string owner, admin, agent, developer_partner
created_at timestamp
updated_at timestamp

users

Column Type Description
id uuid Primary key
auth0_sub string Auth0 subject identifier (unique)
party_id uuid (FK) Reference to the party this user represents
email string Email for lookups and notifications
created_at timestamp
updated_at timestamp

billing_providers

Column Type Description
id int Primary key
code string Unique code (e.g., "zuora")
name string Display name
is_active boolean Whether provider is active
created_at timestamp
updated_at timestamp

billing_accounts

Column Type Description
id uuid Primary key
party_id uuid (FK) The party being billed
billing_provider_id int (FK) The billing provider (e.g., Zuora)
external_account_id string ID in the external billing system
billing_type string client_direct, partner_controlled, aggregate
bill_cycle_day int Day of month for billing
currency string Currency code (default: USD)
payment_status string Current payment status
status string active, suspended, closed
synced_at timestamp Last sync with billing provider
created_at timestamp
updated_at timestamp

Unique constraint: (billing_provider_id, external_account_id)

subscriptions

Column Type Description
id uuid Primary key
billing_account_id uuid (FK) The billing account
external_subscription_id string Zuora subscription ID
status enum active, cancelled, suspended, pending
current_term_start date Current term start date
current_term_end date Current term end date
cancelled_at datetime When cancelled (if applicable)
synced_at datetime Last sync from provider
external_data json Raw provider data (optional)
created_at timestamp
updated_at timestamp

subscription_items

Column Type Description
id uuid Primary key
subscription_id uuid (FK) Parent subscription
product_account_id uuid (FK) Optional ProductAccount link
external_item_id string Zuora charge ID
name string Charge name
description string Rate plan name
quantity int Number of units
unit_amount decimal(8,2) Price per unit
charge_type enum recurring, one_time, usage
billing_frequency enum monthly, annual (nullable)
effective_start_date date When charge started
effective_end_date date When terminated (null = active)
synced_at datetime Last sync from provider
created_at timestamp
updated_at timestamp

rate_plan_groups

Column Type Description
id int Primary key
product_id int (FK) Product this group belongs to
slug string URL-friendly identifier
name string Display name
selection_mode enum one (exclusive) or many (multi-select)
is_required boolean Must always have at least one active plan
sort_order int Display ordering
requires_group_id int (FK) Prerequisite group (nullable)
created_at timestamp
updated_at timestamp

product_rate_plans

Column Type Description
id uuid Primary key
product_id int (FK) Product
billing_provider_id int (FK) Billing provider (Zuora)
rate_plan_group_id int (FK) Group this plan belongs to (nullable)
external_product_id string Zuora product ID
external_rate_plan_id string Zuora rate plan ID
name string Plan name
billing_frequency enum monthly, annual
is_default boolean Default plan in group
sort_order int Display ordering within group
requires_rate_plan_id uuid (FK) Prerequisite rate plan (nullable)
synced_at datetime Last catalog sync
created_at timestamp
updated_at timestamp

product_pricing

Column Type Description
id uuid Primary key
product_rate_plan_id uuid (FK) Parent rate plan
external_charge_id string Zuora charge ID
name string Charge name
charge_type enum recurring, one_time, usage
amount decimal(8,2) Price
currency string Currency code (USD)
pricing_model enum flat_fee, per_unit, etc.
synced_at datetime Last catalog sync
created_at timestamp
updated_at timestamp

mls_fee_mappings

Column Type Description
id uuid Primary key
mls_code string MLS identifier (unique)
mls_name string Display name
billing_provider_id int (FK) Billing provider (Zuora)
external_rate_plan_id string Zuora rate plan ID for fee
external_charge_id string Zuora charge ID
amount decimal(8,2) Fee amount
synced_at datetime Last sync
created_at timestamp
updated_at timestamp

Reference Data

party_types

ID Name Description
1 person An individual human
2 organization A company, office, or group
3 system An external system (MLS, API, etc.)

party_subtypes

ID party_type_id Name Description
1 1 (person) agent Real estate agent
2 1 (person) broker Licensed broker
3 1 (person) client A customer/lead
4 2 (org) office Real estate brokerage
5 2 (org) corporation Corporate entity
6 2 (org) team Agent team
7 3 (system) mls Multiple Listing Service
8 2 (org) developer_partner Developer partner company

role_types

ID Name Category Description
1 listing_agent real_estate Represents seller on listing
2 co_listing_agent real_estate Co-agent on listing
3 buyer_agent real_estate Represents buyer
4 listing_office real_estate Office associated with listing
5 selling_office real_estate Office that brought the buyer

party_group_roles (conventions)

Role Context Description
admin All groups Can manage group and members
member General Standard group member
client Dev Partners Client managed by the group
team_lead Brokerage Teams Team leader
owner Business Owner of the represented entity

product_account_roles

Role Description
owner Owns the account, can close it
admin Can manage settings and users
agent Can use the product (brokerage account members)
developer_partner External partner with admin access

billing_types

Type Description Zuora Settings
client_direct Client pays their own bill directly auto_pay, Due Upon Receipt
partner_controlled Dev partner's card charged per client transaction auto_pay, Due Upon Receipt
aggregate Dev partner invoiced monthly for all their clients auto_pay, Net 30, bill day 25

subscription_statuses

Status Description
active Currently active subscription
cancelled Subscription has been cancelled
suspended Temporarily suspended
pending Awaiting activation

charge_types

Type Description
recurring Charged every billing period
one_time Charged once
usage Charged based on consumption

selection_modes

Mode Behavior
one Exclusive — only one plan active per group
many Multi-select — multiple plans can be active

Why This Architecture?

Benefit How It Helps
Single Source of Truth Person's name in one place (Party), not duplicated across tables
Party = Identity + Account No separate "Tenant" model — Party serves both purposes
Flexible Grouping PartyGroup allows any grouping pattern (teams, clients, affiliations)
Clear Roles on Products Explicit role on party_product (owner vs agent vs developer_partner)
MLS Ready Listings can have multiple agents with different roles via party_roles
CRM Foundation Same Party model works for leads, contacts, clients
Billing Clarity BillingAccount handles money; Party handles identity
Auth0 Clean User is just login credentials; Party is identity
Developer Partner Support PartyGroup for client management, BillingType for billing arrangements
Provider Agnostic Zuora adapter behind contracts — swap billing providers without module changes
Rate Plan Groups Structured catalog with selection rules and dependency enforcement
Subscription Safety Amendment service with locking, idempotency, and cascading business rules
Sync-on-Demand Freshness-based caching keeps local data current without constant polling
Extensible New party types, roles, products, and billing arrangements added easily

Key Principles

Principle Explanation
Party = Identity Who someone IS in the business world
PartyGroup = Organization How parties are grouped (teams, clients, etc.)
PartyProduct = Access What products they can use, with what role
BillingAccount = Billing How money flows through billing providers
Subscription = What They Pay For Local record of subscription state synced from Zuora
RatePlanGroup = Catalog Structure How rate plans are organized with selection rules
User = Login How they authenticate (Auth0)
Contracts = Boundaries Modules communicate through interfaces, not implementations
No Duplication Name lives on Party, not User; subscription data synced, not copied
Roles Define Access party_product.role defines what you can do

Glossary

Term Definition
Party Any business entity (person, org, system) that participates in relationships
PartyIdentifier An external ID for a Party (MLS #, idx_id)
PartyGroup A named group of parties with a purpose (team, client list, affiliation)
PartyGroupMember Membership record linking a Party to a PartyGroup with role and status
PartyRole Role a Party plays on a business object (listing_agent on a listing)
PartyProduct Pivot linking Party to Product with a role (owner, agent, admin)
Product A product we sell (IDX Broker, Outbound Engine, VoicePad, AIVA, CRM)
ProductAccount A specific instance/account of a product (#60869)
BillingProvider An external billing system (e.g., Zuora)
BillingAccount Links Party to BillingProvider with billing type and configuration
Subscription A billing subscription synced from Zuora, belonging to a BillingAccount
SubscriptionItem An individual charge on a subscription (rate plan charge)
RatePlanGroup Organizes catalog rate plans with selection mode and dependency rules
ProductRatePlan A rate plan from the Zuora catalog, mapped to a local group
ProductPricing Price/charge details for a rate plan
MlsFeeMapping Maps an MLS code to a Zuora rate plan for per-MLS billing
SubscriptionAmendmentService Single entry point for all subscription rate plan changes
SubscriptionMutationGuard Validates state and acquires lock before any subscription mutation
User Application login record linked to Auth0 and a Party
Customer Database view aggregating Party + Product + Billing data
ZuoraAdapter Implements billing and subscription contracts using Zuora's API

GraphQL API

The GraphQL schema is organized by resource:

graphql/
├── schema.graphql              # Root schema
├── shared/                     # Common interfaces and error types
├── user/                       # User queries and mutations
├── party/                      # Party CRUD, type queries
├── product/                    # Product queries
└── party-group/                # Party group queries and mutations

Key operations:


REST API

The Subscriptions module exposes REST endpoints:

Method Endpoint Action
GET /subscriptions List all subscriptions
GET /subscriptions/{subscription} Show subscription (auto-sync)
POST /subscriptions Create subscription
DELETE /subscriptions/{subscription} Delete subscription
POST /subscriptions/{subscription}/cancel Cancel subscription
POST /subscriptions/{subscription}/mls-fees Add MLS fee
DELETE /subscriptions/{subscription}/mls-fees/{mlsCode} Remove MLS fee
GET /parties/{party}/subscriptions List subscriptions for party

References


Document Status: Active / Implemented

Completed

  1. Identity Module — Party model with types, subtypes, identifiers, groups, roles
  2. PartyGroup and PartyGroupMember for flexible party organization
  3. Billing Module — BillingAccount, BillingProvider, Invoice, Payment, PaymentMethod
  4. Subscriptions Module — Subscription, SubscriptionItem with Zuora sync
  5. Product Catalog — RatePlanGroup, ProductRatePlan, ProductPricing, MlsFeeMapping
  6. SubscriptionAmendmentService with group enforcement and cascading dependencies
  7. Zuora Module — Full adapter with mappers for accounts, subscriptions, invoices, catalog
  8. GraphQL API for parties, groups, products
  9. REST API for subscriptions with sync-on-demand
  10. Customer provisioning (single and batch import)
  11. Product catalog sync command (fusion:sync-product-catalog)

Next Steps

  1. Build out CRM features on Party foundation
  2. Implement Contact/Lead routing service using subscription data
  3. Implement MLS data import using Party model
  4. Event-driven architecture with Pub/Sub for cross-product communication