Conceptual to denormalized (v0.4)
File: 12-conceptual-to-denormalized.xdbml · Target: MongoDB document model
One model holding relationships at three stages of refinement, which is what the v0.4 relationship work is for. customers - Campaign is an entity-level relationship (spec §11.16): both endpoints name entities, nothing has been decided about a campaign yet, and no cardinality is inferred from the operator. orders.customerID > customers.customerID is an ordinary foreign key carrying the documentation a conceptual or logical model needs -- roles and verbs reading it in both directions (§11.14), explicit cardinality, and constraint_type: non_identifying recording that the foreign key is not part of the order's primary key (§11.15). The remaining five relationships are foreign masters (§11.10): orders keeps copies of the customer name and shipping address, and each line item keeps the product name as sold, so a read needs no join. Each copy is declared separately, since a foreign master takes one attribute on each side, and one of them crosses an array with the explicit .[*] form. None of the foreign masters reaches a generator; they record where each duplicated value is mastered.
Source
xdbml: 0.4
// ---------------------------------------------------------------------------
// Order management, from concepts to a denormalized document model.
//
// The relationships here sit at three different stages of refinement, which
// is the point of the example: one model can hold all three at once.
//
// customers -- Campaign named as entities, no attribute endpoints
// Customer -> Order a foreign key, with roles, verbs and cardinality
// Order -> Customer foreign masters recording denormalized copies
//
// Sections 11.10 through 11.16 of the specification cover the constructs used.
// ---------------------------------------------------------------------------
Project order_management {
targets: [MongoDB]
Note: 'Orders keep a copy of the customer name and shipping address so a read needs no join.'
}
// --- Concepts -------------------------------------------------------------
//
// Campaign has been identified as a concept and connected to Customer, but
// nobody has decided what a campaign holds yet. An entity-level relationship
// records the connection without forcing a cardinality or a key (11.16).
Collection Campaign { }
// --- Entities -------------------------------------------------------------
Collection customers {
Note: 'One document per registered customer. Master of name and address.'
customerID objectId [pk]
name string [not null]
email string [unique, not null, tags: ['pii']]
address object {
street string
city string
country string [default: 'BE']
}
}
Collection orders {
Note: 'One document per order. Carries copies of the customer fields it displays.'
orderID objectId [pk]
orderDate Date [not null]
status string [not null, default: 'pending']
// The key reference: this is what locates the master document.
customerID objectId [not null]
// Copies. Each one is mastered in customers and is declared as such below,
// so a change to a customer can be traced to every place it is repeated.
customerName string
shipTo object {
street string
city string
country string
}
lineItems array [
lineItem object {
sku string [not null]
qty int32 [minimum: 1]
price Decimal128
// Denormalized from the product catalogue at the time of sale.
productName string
}
]
}
Collection products {
productID objectId [pk]
sku string [unique, not null]
name string [not null]
listPrice Decimal128
}
// --- Conceptual relationship ----------------------------------------------
//
// Direction is not stated, so the line carries no marker at either end, and
// no cardinality is inferred from the operator (11.16.1).
Ref: customers - Campaign [
source_role: 'audience',
target_role: 'reach',
note: 'Campaigns target customers; the shape of a campaign is still open.'
]
// --- Referential relationships --------------------------------------------
//
// Roles and verbs read the relationship in both directions (11.14), and the
// constraint type records that the foreign key is not part of the order's
// primary key (11.15).
Ref places: orders.customerID > customers.customerID [
source_role: 'shopping cart', source_verb: 'is bought by',
target_role: 'buyer', target_verb: 'places',
source: '0..*', target: '1..1',
constraint_type: non_identifying
]
Ref sold_as: orders.lineItems.[*].sku > products.sku [
source_verb: 'sells',
target_verb: 'is sold in',
constraint_type: non_identifying
]
// --- Foreign masters ------------------------------------------------------
//
// One per duplicated attribute (11.11). None of these reaches a generator:
// they document where each copied value is mastered.
Ref: orders.customerName > customers.name [foreign_master]
Ref: orders.shipTo.street > customers.address.street [foreign_master]
Ref: orders.shipTo.city > customers.address.city [foreign_master]
Ref: orders.shipTo.country > customers.address.country [foreign_master]
Ref: orders.lineItems.[*].productName > products.name [foreign_master]