Conformed dimensions across goals
A conformed dimension is one you build once and reuse across several
star schemas — a customer dimension that sales_mart and
finance_mart both join to, meaning the same thing, keyed the same
way, in both. Conforming dimensions is what lets a business ask a
question that spans marts and get one consistent answer. Kirimana makes
conformance an explicit, enforced contract property rather than a
convention you hope everyone follows.
Declare a dimension as conformed
A gold dimension opts in to reuse with one flag:
customProperties:
- property: kiri.lifecycle.state
value: gold
- property: kiri.gold.kind
value: dimension
- property: kiri.gold.scd_type
value: type_2
- property: kiri.gold.business_keys
value: [customer_id]
- property: kiri.gold.surrogate_key_strategy
value: hash
- property: kiri.gold.surrogate_key_column
value: customer_key
- property: kiri.gold.conformed
value: true
- property: kiri.gold.star_schema
value: sales_mart
kiri.gold.conformed: true is the declaration: this dimension is
reusable across reporting goals. It carries an obligation with it —
every place the dimension is re-declared must match bit-for-bit on
SCD type, business keys, and surrogate-key strategy. That triple is the
identity of a conformed dimension; if two marts disagree on any of it,
they are not actually conforming, and Kirimana treats that as an error
rather than letting two subtly different “customer” dimensions coexist.
The registry, and why it’s not a separate file
The conformed-dimension registry is not a side file you maintain — it
is derived from the contracts themselves. Every gold dimension carrying
conformed: true is an entry; the star_schema names it belongs to
are read from the facts that reference it. The contract set is the
registry, which means it can’t drift out of sync with the models,
and a review of the dimension’s contract is a review of its
conformance.
A dimension used by facts in more than one star_schema must be
conformed. That is the star-schema-conformity lint rule: a
non-conformed dimension may live in exactly one mart; the moment a
second mart’s fact references it, either it is conformed or the project
doesn’t lint clean.
Reference a conformed dimension from a fact
A fact in any mart references the shared dimension by its gold URN — it does not redefine it:
customProperties:
- property: kiri.gold.kind
value: fact
- property: kiri.gold.grain
value: [customer_key, invoice_date]
- property: kiri.gold.dimension_refs
value:
- urn:kirimana:gold:dim_customer
- property: kiri.gold.star_schema
value: finance_mart
finance_mart’s fact and sales_mart’s fact both point at
urn:kirimana:gold:dim_customer. There is one dimension contract, one
build, one surrogate-key column — and both marts join on it. That is
conformance made physical: not two copies that happen to agree, but one
dimension referenced twice.
Role-playing dimensions
A single dimension often plays several roles in one fact — a Date
dimension playing both order_date and ship_date. Register the extra
roles on the dimension with kiri.gold.role_playing_aliases, and a
fact references a specific role with the ::<alias> URN suffix:
# On dim_date:
- property: kiri.gold.role_playing_aliases
value: [order_date, ship_date]
# On the fact:
- property: kiri.gold.dimension_refs
value:
- urn:kirimana:gold:dim_date::order_date
- urn:kirimana:gold:dim_date::ship_date
An unregistered alias is caught by the role-playing-unregistered
lint rule — you can’t reference a role the dimension didn’t declare.
Role-playing is how one physical dimension serves several fact columns
without cloning itself into dim_order_date and dim_ship_date: there
is still one build, one surrogate-key column, and one conformed
identity — the fact simply joins it under two names.
Cross-goal conflict detection at PR time
The whole point of conformance is that it’s enforced where change
happens: in the pull request, before the divergence reaches the
warehouse. Governance runs in git, so kiri contract lint sees every
contract in the project at once and can reason across marts:
kiri contract lint --format pr
Two rules do the cross-goal work:
conformed-dim-conflictfires when two declarations of a conformed dimension disagree on SCD type, business keys, or surrogate-key strategy. A PR that would introduce a second, incompatible “customer” dimension fails here.star-schema-conformityfires when a dimension is referenced from facts in more than one star schema but is not markedconformed, or when a fact’sstar_schemadisagrees with its non-conformed dimensions.
Because cross-contract rules run against the whole project even when
you scope lint to one domain, a conflict introduced in finance_mart
still fires against the sales_mart dimension it collides with. lint
exits non-zero on any error-severity finding, so wiring it into CI
means a conformance conflict can’t reach main — the first mart to
define a dimension sets its shape, and the second must conform or the
build stays red.
This is the payoff of making conformance a contract property rather than a review convention: the check runs at PR time, in the same place the change is proposed, before either mart ships. Two teams working two marts can’t quietly diverge on what “customer” means, because the moment their declarations disagree the shared project fails to lint. Conformance stops being a thing a data architect polices after the fact and becomes a gate the pipeline enforces on every change — the guarantee that a cross-mart question resolves to one consistent answer is checked, not hoped for.
Where next
- Model gold — facts and dimensions — the full star-schema metadata a conformed dimension lives inside.
- Handle schema drift — keep a conformed dimension in step with the live table.