# The why dimension has a field on every event type

> transactions.dev · by visibility.cloud — 2026-07-31
> Written for the in-house supply-chain platform team — the staff engineer writing the buy-over-build review for the document-context layer.

One thesis: **CBV 2.0 §8.5 places `bizTransactionList` on every EPCIS event
type, which means business-document context is a field of the record — and
any architecture that keeps documents in a second store with a nightly join
is re-implementing, worse, something the standard already designed.**

## The two-database anti-pattern

The default architecture, the one that grows when nobody decides
otherwise: events land in the traceability store, documents land in the
integration platform's database, and a scheduled job joins them by order
number — usually by string-matching a reference that one side stores
trimmed and the other side stores padded. The join logic lives in
application code. It has an owner until the owner changes teams. Every
consumer that needs "which PO covers this pallet" either calls the join
service or re-implements it.

The failure is not that the join is hard. It is that the join is
*somewhere else* — so the record, read alone, cannot answer why anything
moved. And the maintenance tax compounds: every new document class, every
partner whose reference format drifts, every schema revision on either
store lands as a change request against the join layer — a layer that
produces no feature, only the continued absence of a specific bug.

## §8.5's design: the reference rides the event

CBV 2.0 §8.5 governs business-transaction identifiers and states its own
scope plainly — quoting the ratified text: *"This includes the
bizTransactionList field in all EPCIS event types."* All of them:
ObjectEvent, AggregationEvent, TransactionEvent, TransformationEvent,
AssociationEvent. The why dimension is not a corner of the model reserved
for one specialized event; it is a spine field, present wherever something
happened.

The claim is checkable without trusting this page. The official EPCIS 2.0
JSON schema — vendored and pinned at sha256
`0f46ff694efffd8d8ce840a33dfde84228add11b516b8b258f3200740ae210af`,
digests published on [the conformance
page](https://epcis.dev/conformance/) — defines `bizTransactionList` in
each event type's definition, and the pinned JSON-LD context (sha256
`5056c65f…`) resolves the entry's `type` to the CBV vocabulary
([the §7.3 enumeration](/blog/the-standard-already-named-your-paperwork/)).

## Five event types, one purchase order

The same reference, legal in the same field, on all five shapes. One
figure, compressed — the constant is the entry itself:

```json
{
  "type": "po",
  "bizTransaction": "urn:epcglobal:cbv:bt:0724142000017:PO-8812"
}
```

- **ObjectEvent** — the shipment leaves: `action: OBSERVE`,
  `bizStep: shipping`, the PO on the list beside the `desadv`. This is the
  top event of [the worked 856 compile](/blog/from-856-to-epcis-document/).
- **AggregationEvent** — the pallet is packed: `action: ADD`, SSCC parent,
  case children; the PO rides along so the packing itself is attributable
  to the order.
- **TransactionEvent** — the event type whose *entire purpose* is
  declaring an association between objects and transactions; the golden
  corpus reference shape `transaction-event-po.json` carries exactly this
  entry with a GDTI-form identifier, `urn:epc:id:gdti:0614141.06012.1234`.
- **TransformationEvent** — inputs become outputs at the co-manufacturer:
  the production run cites `prodorder` and the customer's `po`, so the
  finished-goods lot is born already attached to its commercial cause.
- **AssociationEvent** — the reusable asset is bound to its load; the
  transaction context says under which order the binding happened.

Each of the five, with that entry in place, validates against the pinned
official schema. That is the acceptance bar the `join` verb enforces on
everything it emits: conformant always, a superset only where the source
document carries more than the standard models — the two words travel
together, because a superset that stops validating is a fork.

## What the field deletes from your architecture

Put the reference on the event and the nightly join job has nothing left
to do:

1. **The join runs at write time, once,** in the compiler — [by GTIN and
   SSCC](/blog/the-gtin-is-the-join-key/), under
   [pinned mapping rulings](/blog/from-856-to-epcis-document/) — instead
   of at read time, forever, in every consumer.
2. **The record is self-contained evidence.** An auditor, a counterparty,
   or an agent reads the event and gets the why dimension with no second
   system, no service dependency, no join-logic archaeology.
3. **Downstream stays standard.** Any conformant consumer — a retailer's
   platform, a regulator's tooling, [the read-side
   product](https://visibility.cloud/asn-po-context-in-traces) that
   answers the executive's version of this question — finds the context
   in the field the standard told it to check.

## The read-side payoff

The buyer's question is never "show me the event"; it is "show me the
trace, with its paperwork." When every hop's events carry `po`, `desadv`,
`inv`, and `recadv` references on core fields, a trace is
self-documenting: this case, commissioned under this production order,
shipped under this ASN against that PO, invoiced by that document,
received against [the advice that closed the
loop](/blog/recadv-closes-the-loop/). The executive twin of this post
lives at
[visibility.cloud](https://visibility.cloud/asn-po-context-in-traces);
this layer's job is the write side — compiling the dialects onto the spine
so the read side has something to read.

If your review lands where this post argues it should — context as an
event field, compiled once, validated always — the next step is
[the door](/get-access/): say what documents and what event pipeline you
run, and the onboarding flow routes the rest.

---
Access: https://transactions.dev/get-access/ · All posts: https://transactions.dev/blog/ · Machine face: https://transactions.dev/llms.txt
