Overview
An Entity is an item that flows through a simulation, a work order, a finished good, a pallet, a batch, a patient, a truck. Every simulation revolves around entities being created, routed, processed, and terminated. What makes entities powerful in ProDex isn’t the concept of “things flowing through a graph”: it’s that entities carry typed attributes with them, and expressions across the model can read and act on those attributes. An entity isn’t just an anonymous token; it’s a bundle of typed data that components inspect, route on, time-process differently, and aggregate. This is the mechanism that turns a static flow diagram into a model that responds to the specific work being done. Entity types are one of the first things you define when building a model — see Key concepts for where they sit in the Factory > Models hierarchy, and the Simulation overview for the modeling workflow around them.Attribute Types
Each entity type defines a set of named attributes. The schema has five canonical types (boolean, number, text, text_list, number_list); the UI splits Number into Integer and Real, so the attribute-type dropdown shows six options. Under the hood that split isn’t a sixth type: a Number attribute carries a domain field ("int" or "real", defaulting to real), and the two dropdown entries surface it.
The List types are enums, not lists. A Text List attribute doesn’t hold multiple strings: it holds one string, picked from a fixed set of allowed values declared on the entity type. Same for Number List: the attribute’s value is one number chosen from a predefined set. Think of them as constrained categorical values, not arrays.
Use Text List or Number List when you want an attribute to be categorical and you want the platform to enforce valid values. Use Text or Number when the attribute is open-ended.
Attribute types are fixed on the entity type: you can’t store a number in a Text attribute. The expression language respects these types when you reference attributes by name.
How Attributes Flow
Attributes set on an entity persist with it through the entire flow. Once assigned, downstream components can read them without any explicit propagation.- Create / assign: attributes are assigned any time a component produces a new entity. That’s on Sources (creating initial entities), Combiners (output entity of a batch), Separators (output entities of a split), Transformers (output entity of a type change), and material releases on either a Source or a Buffer in a schedule. In the model definition, Source/Combiner/Transformer carry a singular
attribute_assignment; a Separator carriesattribute_assignments(plural), keyed by output entity-type slug, because one split can emit several output types. - Read: any downstream expression in a single-entity context can reference the attribute by bare name (
priority,weight). - Change identity: a Transformer produces a new entity of a different type with its own attribute assignments. It doesn’t mutate the input entity’s attributes in place: it emits a different entity, typed differently, with its own attribute values.
- Aggregate: when entities are combined, the output entity’s attributes are computed from the set of inputs using aggregation functions like
SUM(weight)orMAX(priority)in the Combiner’s multi-entity context.
priority and product_class are valid identifiers in any expression that has an entity in scope.
Which context each assignment runs in
The single biggest authoring trap with attributes is forgetting which expression context an assignment evaluates in:
Attribute names also can’t collide with the DSL’s reserved keywords — the validator rejects an entity type whose attribute shadows one.
Assignment Strategies
Whenever a component assigns an attribute on a new entity (on a Source, Combiner, Separator, Transformer, or schedule material release), it uses one of six strategies. Not all six are valid for every attribute type; the dropdown filters to the strategies that work for the attribute you’re configuring. (The per-type restrictions are enforced by the validator and surfaced by the UI — the schema itself is looser, so hand-authored JSON can express combinations that will then fail validation.)Random Choice and Random are different strategies. Random Choice picks uniformly from a categorical choices array (boolean / text list / number list). Random samples from a probability distribution (number only). The dropdown filters them out based on attribute type, so you’ll only see the ones that apply.Strategy Details
Fixed: Same value on every entity. Useful for attributes that don’t vary across items, or as a placeholder before you wire in real variability. On Text List / Number List attributes the fixed value must be one of the declared choices. DSL Expression: The value is the result of a DSL expression evaluated when the entity is created. Use when the value depends on simulation state, other attributes, or the current time:IF(SIM_TIME < SHIFT_1_END, "day", "night") or LOOKUP(priority_by_class, product_class). Not valid for Text List or Number List: for dynamic categorical selection use Weighted with DSL-expression weights, or restructure to Random Choice.
Round Robin: The first entity gets value 1, second gets value 2, third gets value 3, fourth cycles back to value 1. Useful for evenly distributing entities across categories without randomness. Round Robin carries no choices of its own — it cycles through the choices declared on the attribute.
Random Choice: Uniform random pick from the entity type’s declared choices. Every choice has equal probability. For Boolean attributes the choice set is the implicit {true, false}.
Random: Samples from a distribution (normal, exponential, triangular, etc.). Useful for natural variability: entity weights drawn from a normal, service-time requirements drawn from triangular, or arrival intervals drawn from exponential.
Weighted: Pick from the entity type’s choices with per-choice weights. Specify a weight for each option; relative weights determine selection probability. Weights can themselves be DSL expressions for dynamic distributions.
Defining an Entity Type
Entity types are managed via the Entities button at the bottom of the Modeler’s Library panel: it opens a modal listing every entity type in the factory with Name, Description, Unit, and Attrs columns. Each type has:- A name and description
- A unit — a free-text label (default
"EA") that drives quantity labels in reporting and KPIs. It is a label only: ProDex does not perform dimensional conversion between units. - A set of typed attributes (with
choicesarrays declared on Text List and Number List attributes, optionallower_bound/upper_bound/exclude_zeroanddomainon Number attributes, and optionallengthon Text)
entities/{slug}.json in the factory’s data tree.
The same entity types back your BOMs. An entity type isn’t simulation-only. A BOM references an entity type by its slug (
entity_id), and its applies_when conditions read the type’s attributes by name — the two features share one factory-scoped registry, the same entities/{slug}.json files. Define an entity type once and it’s available to both the simulation model and the BOM/planning side; edit its attributes and both see the change.Authoring Entity Types with Dexter
When Dexter builds entity types for you — typically after profiling your uploaded data — it authors them in code:sim.boolean, sim.number, sim.text, sim.text_list, sim.number_list). Ask Dexter to create or extend entity types from a spreadsheet and this is what runs underneath.
Attributes in Results Data
Attributes are queryable after a run, but they live in a dimension table, not inline on every lifecycle row:- Each
entity_lifecyclerow carries anattributes_hash. Join it to theentity_attributetable, which has one row per(attributes_hash, attribute_name). entity_attributecolumns:attributes_hash,attribute_name,value_type("number","text", or"boolean"), and typed value columnsvalue_number/value_text/value_boolean. Booleans land invalue_boolean, nevervalue_number; Text List values land invalue_textand Number List values invalue_number(there are only three value buckets).- Rows are written the first time each distinct
attributes_hashis seen and never re-emitted; the table carries no entity identity of its own — the hash onentity_lifecycleis the only route back to entities. entity_lifecycle.attributes_jsonis populated only on material-release entry rows that carry per-release overrides; it’s null everywhere else.
Patterns
Classification attribute driving routing. Aproduct_class Text List attribute set at the Source (one of "rush" or "standard") drives a Router that sends rush items to the express line.
Numeric attribute driving processing time. A complexity_factor Number attribute (drawn from a distribution via the Random strategy) multiplies the base processing time on each Process.
Type change in a Transformer. A Transformer reassigns the entity to a new type painted_part (from raw_part), with explicit DSL-expression assignments that carry forward every attribute the downstream model cares about.
Aggregation in a Combiner. When three components are combined into an assembly, the output entity’s total_weight is SUM(weight), priority is MAX(priority), and qc_passed is ALL(qc_passed). The full aggregation set is SUM, MEAN, COUNT, MAX, MIN, MODE, N_UNIQUE, ANY, and ALL — COUNT and N_UNIQUE always return numbers, ANY and ALL are the boolean aggregations (over boolean attributes), and MAX / MIN / MODE work on numbers or text.
Tips
- Model the attributes you’ll query. If you care about “cycle time by product class” in your results,
product_classhas to be an attribute on the entity, otherwise the join isn’t available in the simulation dataframes. - Use Text List or Number List for categorical constraints. They’re enums: the platform enforces that the value is one of the declared choices. Prefer these over free-form Text when the attribute has a known finite set of valid values.
- Prefer entity attributes over state variables for per-entity data. State variables are for facts about the system. Attributes are for facts about a specific entity.
- For dynamic categorical selection, use Weighted with DSL-expression weights. That’s the only way to drive list-type attributes from expressions, since
DSL Expressionisn’t valid for list types. - Explicit attribute carryover: every output attribute on a Transformer or Separator must be assigned. There’s no auto-copy from the input.
- Stamp provenance on assignment values. Choices lists, weights, and expressions inside attribute assignments accept the same
derived/stated/assumedprovenance stamps as everything else Dexter authors — unstamped values surface in an assumptions review. entity_iddoesn’t survive transformation. Transformers, Combiners, and Separators emit new entities with new ids. If you need to trace one unit end-to-end in results, carry a stable business identifier (order number, serial) as an attribute.

