Skip to content

Business Affiliate: Data Model

The Business Affiliate module stores an organization’s identity and contact information in business_affiliate, with each role that organization plays (vendor, customer, carrier, operator, manufacturer, other) broken out into a child row in business_role. This page is the field-complete reference for both tables and their DTOs; see Data Models — Reading Guide for the shared envelope shapes referenced throughout, and for why this module predates the occurrence pattern.

business_role is a same-module child table (parent_eid, no FK). Other modules pin a specific role, not the affiliate directly — Item pins a MANUFACTURER role and a VENDOR role (per supply), and Orders pins a VENDOR role for its supplier binding.

PlantUML diagram

ColumnTypeNullRole
idUUIDNOT NULL (PK)identity
effective_as_oftimestampNOT NULLidentity
recorded_as_oftimestampNOT NULL, default current_timestamp(3)identity
bts_author (was author)varchar(255)NOT NULLbookkeeping
eidUUIDNOT NULLidentity
previousUUIDNULLidentity
retiredbooleanNOT NULL, default falsebookkeeping
created_by, created_at_effective, created_at_recordedvarchar / timestamp(3)NOT NULLbookkeeping
tenant_idUUIDNOT NULLidentity
ba_namevarchar(255)NOT NULLpayload
legal_name, legal_legal_name, legal_country, legal_tax_id, legal_registration_id, legal_naics_codevarcharNULLpayload — CompanyInformation component
contact_salutation/first_name/middle_name/last_name/job_title/email/phonevarchar(255)NULLpayload — Contact component
contact_postal_address_address_line_1/_2/city/state/postal_code/countryvarcharNULLpayload — nested PostalAddress
contact_postal_address_geo_location_latitude/longitude/altitudedouble precisionNULLpayload — nested GeoLocation
contact_emails, contact_phones, contact_addresses, contact_sitesJSONBNULLpayload — Contact map fields
main_address_address_line_1/_2/city/state/postal_code/countryvarcharNULLpayload — PostalAddress component
main_address_geo_location_latitude/longitude/altitudedouble precisionNULLpayload
other_contactsJSONBNULLpayload — Map<String, Contact>
other_addressesJSONBNULLpayload — Map<String, PostalAddress.Value>
bts_author_sub (was oidc_sub), bts_note (was note), bts_created_by_subvarcharNULLbookkeeping

Constraints: PK id. Unique: none — see Schema notes; name uniqueness is enforced only in application code.

ColumnTypeNullRole
idUUIDNOT NULL (PK)identity
effective_as_oftimestampNOT NULLidentity
recorded_as_oftimestampNOT NULL, default current_timestamp(3)identity
bts_author (was author)varchar(255)NOT NULLbookkeeping
eidUUIDNOT NULLidentity
previousUUIDNULLidentity
retiredbooleanNOT NULL, default falsebookkeeping
created_by, created_at_effective, created_at_recordedvarchar / timestamp(3)NOT NULLbookkeeping
parent_eidUUIDNOT NULLidentity — parent-linking metadata, not a SQL FK
rolevarchar(255)NOT NULLpayload — BusinessRoleType enum
notesvarchar(8192)NULLpayload
ba_namevarchar(255)NOT NULLbookkeeping — denormalized copy of the parent’s ba_name
bts_author_sub (was oidc_sub), bts_note (was note), bts_created_by_subvarcharNULLbookkeeping

Constraints: PK id. Unique: none — see Schema notes. No embedded-component families — all payload columns are scalar.

FieldTypeStored or computedNotes
eId, name, legal, contact, mainAddress, contacts, addressesmixedStored1:1 with business_affiliate columns
FieldTypeStored or computedNotes
eId, name, role, notesmixedStored1:1 with business_role columns

BusinessAffiliateRoleDetails (read model, GET .../with-details/{entity-id})

Section titled “BusinessAffiliateRoleDetails (read model, GET .../with-details/{entity-id})”
FieldTypeStored or computedNotes
businessAffiliateBusinessAffiliateStoreddirect read
rolesList<BusinessRoleType>ComputedroleService.rolesFor(...).map { it.role }

VendorLookupResult / BusinessRoleReference.Value (response DTO, GET .../lookup/vendors — the shape used everywhere a module pins a role)

Section titled “VendorLookupResult / BusinessRoleReference.Value (response DTO, GET .../lookup/vendors — the shape used everywhere a module pins a role)”
FieldTypeStored or computedNotes
nameStringComputedjoined from the parent affiliate
eIdEntityId?Computedthe role’s own eId (aliased brTable.eId)
affiliateEIdEntityId?Computedthe parent affiliate’s eId
rIdRecordId?Computedalways null for lookup results — floating
retiredBooleanComputedalways false for lookup results
provenanceProvenance.Value?Computedaliased from the role’s own author/recorded-time
roleBusinessRoleTypeComputed@Transient — stamped by the caller, not read from any column

This BusinessRoleReference.Value shape is what appears embedded on the manufacturer field of Item and the reference field of Orders’ SupplierBinding — same DTO, per-slot role stamp.

BusinessAffiliateLookupResult, BusinessAffiliateRoles (list/lookup DTOs)

Section titled “BusinessAffiliateLookupResult, BusinessAffiliateRoles (list/lookup DTOs)”
FieldTypeStored or computedNotes
matchesList<BusinessAffiliateLookupEntry>Computedfuzzy-match query, de-duplicated and limited
rolesList<BusinessRole>Storeddirect list of child records
FromField/ColumnTargetKind
ItemmanufacturerBusinessRole (MANUFACTURER)floating; pins to a tombstone rId on role retirement
Itemitem_supply.supplierBusinessRole (VENDOR)floating; pins to a tombstone rId on role retirement
OrdersSupplierBinding.referenceBusinessRole (VENDOR)floating; pins on retirement; frozen post-Submit
business_role.parent_eidbusiness_affiliate.eidsame-module parent-childno SQL FK — BusinessRoleReferenceComponent flattens the reference into <prefix>_* columns with no FK

The <prefix>_* reference-cluster shape these consumers embed (entity_id, record_id, name, retired, provenance_*) is the one described once in Data Models — Reading Guide.

Follows the request/response call-shape convention in The Orders v2 Call Shape.

MethodPathPurposeMain refusals
(framework)create, readByEid, readByRId, update, delete, query, history, bulkStandard data-authority CRUD on BusinessAffiliate
GET{entity-id}/with-detailsThe affiliate plus its enabled roles
GET{entity-id}/rolesList roles for an affiliate
POST{entity-id}/rolesCreate a role
PUT{entity-id}/roles/{business-role}Update a role
DELETE{entity-id}/roles/{business-role}Delete a role
GETlookupFuzzy name lookup, optional role filter
GETlookup/vendors (canonical, reference-data/business-affiliate)Fuzzy VENDOR-role lookup, ready-to-link references — the FE’s vendor-linking capability

Emits UpdateEntity (BA rename), and NewEntity/UpdateEntity/DeleteEntity for BusinessRole. Item’s ItemRoleListener and Orders’ OrderSupplierRoleListener both subscribe to the role channel: a VENDOR update propagates a name change to every linking item_supply/order (Orders only touches DRAFT orders — never SUBMITTED or terminal ones); a VENDOR delete marks the reference retired; MANUFACTURER events drive the equivalent cascade in Item only.

  • No database unique constraint on affiliate name. Uniqueness on (ba_name, tenant_id) is enforced only in application code (BusinessAffiliateValidator.validateForCreate/validateForUpdate), not a SQL UNIQUE constraint.
  • Same gap on business_role — no SQL UNIQUE constraint exists on this table at all.
  • This module predates the occurrence-kind pattern. No *OccurrenceKind type exists for either BusinessAffiliate or BusinessRole; both implement the generic EntityPayload interface and surface mutations through the generic DataAuthorityNotification.NewEntity/UpdateEntity/DeleteEntity cases rather than a module-specific kind enum. (Contrast Orders, which does have OrderOccurrenceKind — the pattern exists elsewhere in the stack but wasn’t brought into this module.)