Skip to main content

Billing Service — Domain Model

Status: populated Owner: Platform Engineering + Finance Last updated: 2026-04-18

1. Aggregates

BillingEvent (root)

One chargeable message event. Immutable after insert.

FieldTypeNotes
billingEventIdUUIDv4Identity
messageIdUUIDv4Dedup key; FK to orch.sms_messages (logical)
tenantIdUUIDv4Isolation boundary
accountIdUUIDv4Billing subject
operatorIdstringUsed to resolve pricing + cost
directionBillingDirectionMT (mobile-terminated outbound)
segmentCountnumberDetermines charge when per-segment pricing
pricingTableIdUUIDv4FK to resolved pricing_tables row
customerPriceMoneyRevenue recognized
operatorCostMoneyCost booked
marginMoneyComputed: customerPrice - operatorCost
currencyISO 4217e.g. USD
chargedAtInstantFrom DLR event
createdAtInstant

PricingTable (root)

Versioned pricing configuration per billing segment.

FieldTypeNotes
pricingTableIdUUIDv4Identity
accountTierAccountTierSTARTER, GROWTH, ENTERPRISE, CUSTOM
operatorIdstringCarrier/operator identifier
directionBillingDirectionMT
pricingModelPricingModelPER_SEGMENT or FLAT_PER_MESSAGE
unitPriceMoneyPrice per segment or per message
currencyISO 4217
effectiveFromdateInclusive
effectiveTodate | nullnull = currently active
createdBystringAdmin userId

OperatorCost (root)

Platform's cost for a given operator/direction, used for margin modelling.

FieldTypeNotes
operatorCostIdUUIDv4Identity
operatorIdstring
directionBillingDirection
costPerSegmentMoney
currencyISO 4217
effectiveFromdate
effectiveTodate | null

Invoice (root)

Monthly aggregated invoice for one account.

FieldTypeNotes
invoiceIdUUIDv4Identity
tenantIdUUIDv4
accountIdUUIDv4
periodStartdateFirst day of month
periodEnddateLast day of month
totalMessagesnumber
totalSegmentsnumber
subtotalAmountMoney
currencyISO 4217
statusInvoiceStatusDRAFTFINALIZEDVOID
s3Keystring | nullObject store key for PDF
generatedAtInstant | null

UsageSummary

Pre-aggregated usage bucket (hourly) for query efficiency.

FieldTypeNotes
summaryIdUUIDv4
tenantId / accountId / operatorIdidentifiersGrouping dimensions
bucketHourTIMESTAMPTZTruncated to hour
messageCount / segmentCountnumbersCounters
totalCustomerPrice / totalOperatorCostMoney

2. Value Objects

VOInvariant
Moneyamount (Decimal, ≥ 0) + currency (ISO 4217); arithmetic only within same currency
AccountTierEnum: STARTER, GROWTH, ENTERPRISE, CUSTOM
BillingDirectionEnum: MT
PricingModelEnum: PER_SEGMENT, FLAT_PER_MESSAGE
InvoiceStatusEnum: DRAFT, FINALIZED, VOID

3. Domain Services

ServicePurpose
PricingResolverResolves effective PricingTable by (accountTier, operatorId, direction, currency, chargedAt) with Redis cache
CostResolverResolves effective OperatorCost by (operatorId, direction, chargedAt)
MarginCalculatorcustomerPrice.subtract(operatorCost) with currency assertion
InvoiceGeneratorAggregates summaries, renders PDF template, handles S3 upload

4. Domain Events

EventTrigger
billing.invoice.generated.v1Invoice persisted as FINALIZED and PDF stored

5. Invariants

  • messageId is unique in billing_events — no double billing.
  • Exactly one PricingTable row is active per (accountTier, operatorId, direction, currency) at any point in time (enforced by partial unique index).
  • Invoice transitions: DRAFT → FINALIZED (cron only), FINALIZED → VOID (platform.finance only).
  • margin = customerPrice - operatorCost; negative margin is allowed (flagged by alert).
  • Currency of customerPrice must match PricingTable.currency.