Should Every Pump Keep a Copy of the Supplier’s Phone Number?
A copied contact becomes stale when the supplier changes its number. Separate facts owned by a pump from shared current facts and intentional historical snapshots.
14 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
A Document Boundary Also Became an Update Boundary
BOTIE
The maintenance example puts that decision under pressure. A pump’s installed sensor configuration belongs with the pump. A supplier’s current phone number is shared. A purchase-time supplier name may be a historical snapshot that should remain unchanged. Their relationships look similar on a screen but require different update rules.
Sources for this history
One Phone Number Changed, Fifty Pump Records Did Not
Botie opens P1 and finds its supplier phone number inside the pump document. That is convenient until the supplier changes the number. Fifty other pump documents still contain the old contact. Which copy should the technician trust?
Keeping related data together can save a lookup. It also gives us copies to maintain. We need to decide whether a field describes this pump, a shared entity, or the state of that entity at a particular time.
The directory is a supplied local map. It models reference resolution without a database server or cross-document transaction.
Step 1 of 2
Three related facts have different owners
Decide what each field is intended to mean before choosing a location.
| Fact | Meaning | Possible representation |
|---|---|---|
| Installed sensor channels | Configuration of this pump | Bounded embedded array |
| Supplier phone | Current shared contact | Reference to supplier record |
| Name on purchase order | Name when the purchase happened | Intentional snapshot |
Installed sensor channels
- Meaning
- Configuration of this pump
- Possible representation
- Bounded embedded array
Supplier phone
- Meaning
- Current shared contact
- Possible representation
- Reference to supplier record
Name on purchase order
- Meaning
- Name when the purchase happened
- Possible representation
- Intentional snapshot
Step 2 of 2
Follow the reference before using the contact
P1 retains the supplier’s stable ID.
- 1
Pump document
supplier_id S1 - 2
Supplier directory
S1 has phone 222 - 3
Lookup
Resolve S1 - 4
Screen assembly
Attach current supplier; retain purchase snapshot
The right boundary follows ownership and change
Reference resolution obtains current shared data while the historical snapshot remains unchanged.
Resolve the current supplier through its identity
Read supplier_id from the pump, look it up in the current supplier directory, and attach the result without changing purchase history.
BOTIE
Pump with resolved supplier information
{
"asset_id": "P1",
"supplier_id": "S1",
"purchased_name": "Old Pumps",
"supplier": {
"phone": "111"
}
}Follow the operations
1. Pump and historical snapshot
{ "asset_id": "P1", "supplier_id": "S1", "purchased_name": "Old Pumps" }2. Current supplier directory
{ "S1": { "phone": "111" } }3. Read a document path
S1
4. Look up one exact key
{ "found": true, "value": { "phone": "111" } }5. Unresolved supplier marker
{ "missing": true }6. Use a lookup or its fallback
{ "phone": "111" }7. Attach a named object
{ "asset_id": "P1", "supplier_id": "S1", "purchased_name": "Old Pumps", "supplier": { "phone": "111" } }8. Result
{ "asset_id": "P1", "supplier_id": "S1", "purchased_name": "Old Pumps", "supplier": { "phone": "111" } }
The browser runs an explicit model of the mechanism. It does not connect to MongoDB or Redis, simulate their query planners, or establish their concurrency guarantees.
1. Ask which object owns the fact
A pump’s mounting position changes when that pump moves. A supplier’s contact changes for every customer using that supplier. Those facts have different owners even if one screen shows them together.
Embed a fact when its lifecycle and access pattern fit the parent. Use a reference when the related object needs an independent identity or is shared across many parents. These are design questions, not absolute rules that every supplier must live separately.
Document ownership helps us locate the write that should change a fact.
2. Distinguish a current copy from a historical snapshot
The purchase order might need the supplier name as it appeared on the day of purchase. Updating that name every time the supplier rebrands would alter the historical record. Store it as a named snapshot with its meaning clear.
By contrast, a field labelled current_supplier_phone promises current information. If duplicated, it needs an update process and a freshness allowance. Field names and timestamps should help readers tell those promises apart.
Denormalisation is a choice about repeated data. It is manageable only when we know which copies should change.
3. Resolve a reference by identity
The pump stores supplier_id. A directory maps that ID to the current supplier object. Read the reference, look up the matching object, then assemble the view. Do not match the supplier by list position or by a display name that can change.
A missing reference needs a visible outcome. Our exercise attaches {"missing": True} rather than silently choosing the first available supplier. Real services may reject a write that creates an invalid reference, retain a tombstone, or show an unresolved relationship.
The reference itself is not a guarantee that the related record still exists.
Reordering the directory does not change the partner
Both pumps can refer to the same supplier without copying its phone.
suppliers = {"S2":{"phone":"999"}, "S1":{"phone":"222"}}
assets = [{"asset_id":"P1","supplier_id":"S1"},{"asset_id":"P2","supplier_id":"S1"}]
for asset in assets:
print(asset["asset_id"], suppliers[asset["supplier_id"]]["phone"])P1 222
P2 222Both results come from S1’s current record. The demonstration does not make a network request or guarantee a consistent snapshot across concurrent changes.
Check yourself
Questions on Parts 1 to 3
Question 1 of 2
Which field should a current contact lookup change?
Use the information below.
P1 stores supplier_id S1 and purchased_name Old Pumps. S1’s current name is North Pumps. purchased_name means the name printed on the original order.4. Keep growing histories from enlarging the parent forever
A few installed channels form a bounded array. A lifetime of inspection events can grow without a useful limit. Continually embedding those events enlarges the parent and can make reads transfer data the screen never uses.
MongoDB documents have a 16 MiB limit, but staying below that limit is not the whole design test. Consider update frequency, the size of requested subsets and the lifetime of the history. A bounded recent subset plus separately stored full history can be appropriate if its duplication rules are explicit.
The largest legal document is not automatically a comfortable working size.
5. Understand the consistency boundary you selected
MongoDB guarantees atomic writes to a single document. A change to multiple separate documents is not made atomic merely because they share an ID. Where an invariant requires several documents to change together, consider a supported transaction and its deployment requirements, or redesign the invariant and workflow.
Embedding can make some related changes fit one document, but it also puts concurrent writes and growth inside that boundary. Weigh those costs against the reads it simplifies.
A local dictionary lookup cannot demonstrate database atomicity.
6. Inspect the real reference pattern
A MongoDB reference can be an ordinary field containing another document’s identifier. The database does not automatically enforce a relational foreign-key constraint for that field. Applications need an integrity policy.
The following pair shows the intended representation. Reading the asset and supplier separately is one option; an aggregation $lookup can also combine collections when appropriate. Measure and explain the selected access path in the actual deployment.
Keeping a reference does not mean the application must never use a server-side join.
Run in your own environment
Separate current supplier data from the purchase snapshot
These are example documents for two collections. The wrapper is explanatory JSON, not a MongoDB insert command.
In a disposable MongoDB database, store the asset and supplier in their respective collections and query by the shown identifiers.
{"asset":{"_id":"P1","supplier_id":"S1","purchased_name":"Old Pumps"},"supplier":{"_id":"S1","name":"North Pumps","phone":"222"}}The stable supplier_id links current contact data. purchased_name is intentionally historical.
Separate current supplier data from the purchase snapshot. Documentation checked .
7. Test deletion as well as a phone change
The workshop changes the directory while leaving the pump unchanged. The current contact must follow the directory, and the purchase name must stay put. Then try a supplier ID absent from the directory.
Those cases expose different shortcuts: reusing an embedded current contact, joining by position, and pretending every reference resolves. Keep all three in the tests for the view builder.
The next lesson queries inside arrays, where keeping facts beside their identity becomes just as important.
Check yourself
Questions on Parts 1 to 7
Question 1 of 2
Which concern follows?
Use the information below.
Every pump embeds every inspection ever recorded, with no retention bound.Try it with Botie
Build with Botie
Write and run Python