Module: Hecks::Projections::Model::Deviations
- Defined in:
- lib/hecks/projections/model/deviations.rb
Overview
HOW THE MODEL'S SHAPE DIFFERS FROM THE LANGUAGE'S, and why.
Every construct's emits_ir restates what the grammar declares —
and the two legitimately differ, in seven ways. These lived only
as prose in Ruby comments until spec/model_shape_conformance_spec
made them checkable; they live HERE so the generator and the gate
read one source rather than two that must agree.
A reason is carried beside each entry because the generated file will CARRY it: a deviation is exactly the kind of thing whose explanation must survive regeneration, and the only way it can is to be emitted rather than typed into the output.
Constant Summary collapse
- PARENT_REF =
The grammar is relational — a Command points UP at its Aggregate — where the model composes. An explicit
as:still keeps its_id(Command's ownentity_id, kept as data); the parent link itself mints bare now (ADR 0025) —aggregateorbluebook, whichever this category's creating command declares first — so both spellings are checked. Entity spells its own (separate, non-colliding) text twin of the parent linkowner. ->(field) { field.to_s.match?(/_id\z/) || %i[owner aggregate bluebook].include?(field) }
- JUDGE_ONLY =
The judge's own field, never the model's — contracts.rb already says so with
derived: { position: :walk }. %i[position].freeze
- CONTAINED =
What the model holds that the grammar declares elsewhere: the containment edges, stated in syntax.bluebook's Keyword rows as
context->opens. { "Bluebook" => %i[aggregates read_models policies process_managers], "Aggregate" => %i[commands entities queries value_objects], # S17, ADR 0026 — an entity may nest further entities now # (`Dispatch`, inside `Handler`) — same containment edge as # Aggregate's own `entities`, one level down. "Entity" => %i[commands entities queries], "ValueObject" => %i[members], "ProcessManager" => %i[handlers] }.freeze
- FOLDED =
One model field gathered from several declared ones — contracts.rb's own
[:folded, ...]shape, as the pair it is. { "Aggregate" => { lifecycle: %i[state_field state_start transitions] }, "Entity" => { lifecycle: %i[state_field state_start transitions] }, "Query" => { order_by: %i[order_field order_way] } }.freeze
- UNPACKED =
The inverse of a fold, and it had no name at all: one declared field opening into several the model holds apart.
{ "ReadModel" => { options: %i[wheres order_by limit] } }.freeze
- COMPUTED =
Model-only, each for its own reason rather than by oversight.
{ "Bluebook" => { ir_version: "the EMISSION's own version, not the domain's", canonical_form: "the normalisation table every reader needs beside the IR" }, "ValueObject" => { closed_set: "an empty one_of and no one_of are otherwise indistinguishable" }, "Aggregate" => { ports: "declared in the hecksagon, attached after the aggregate exists" } }.freeze
- OFF_THE_WIRE =
DECLARED, AND DELIBERATELY NOT EMITTED. The category that had no home anywhere before — each of these was a comment and nothing more.
{ "Policy" => { aggregate: "the wire format is a pinned contract, and it does not carry " \ "where a policy was written before the builder hoisted it" }, "Bluebook" => { formerly_known_as: "a rename's old name is a fact about the source", normalisations: "the normalisation table rides on canonical_form instead" }, "ValueObject" => { rows: "the language's name for a closed set's members; emitted as `members`" } }.freeze
- DYNAMIC_TAIL =
Emitted by
to_h's own merge rather than byemits_ir— the two constructs whose shape is genuinely not fixed, because the query specification layer grew options after them. { "Query" => %i[options], "ReadModel" => %i[options group_by aggregate_heads count median_field] }.freeze
Class Method Summary collapse
- .computed(name) ⇒ Object
- .contained(name) ⇒ Object
- .dynamic_tail(name) ⇒ Object
- .folded(name) ⇒ Object
-
.off_the_wire(name) ⇒ Object
The tables that carry a reason answer with names only when the caller wants the set rather than the explanations.
- .unpacked(name) ⇒ Object
Class Method Details
.computed(name) ⇒ Object
90 |
# File 'lib/hecks/projections/model/deviations.rb', line 90 def computed(name) = COMPUTED.fetch(name, {}).keys |
.contained(name) ⇒ Object
91 |
# File 'lib/hecks/projections/model/deviations.rb', line 91 def contained(name) = CONTAINED.fetch(name, []) |
.dynamic_tail(name) ⇒ Object
94 |
# File 'lib/hecks/projections/model/deviations.rb', line 94 def dynamic_tail(name) = DYNAMIC_TAIL.fetch(name, []) |
.folded(name) ⇒ Object
92 |
# File 'lib/hecks/projections/model/deviations.rb', line 92 def folded(name) = FOLDED.fetch(name, {}) |
.off_the_wire(name) ⇒ Object
The tables that carry a reason answer with names only when the caller wants the set rather than the explanations.
89 |
# File 'lib/hecks/projections/model/deviations.rb', line 89 def off_the_wire(name) = OFF_THE_WIRE.fetch(name, {}).keys |
.unpacked(name) ⇒ Object
93 |
# File 'lib/hecks/projections/model/deviations.rb', line 93 def unpacked(name) = UNPACKED.fetch(name, {}) |