Concepts
What is an entity?
This documentation makes heavy use of the word 'entity', but what is an entity? In general, entities are clojure maps that represent data in your application domain, e.g. a user with an id, name and email.
Fabrikk also considers lists of entities to be entities in their own right, but this is generally an implementation detail.
This documentation frequently references the following classes of entities:
Primary Entity
The entity that is currently being built, this term is generally only used when describing the build process.
Dependent Entity
Dependent entities are ones that are required to exist before the Primary entity can be built. There may be many such entities, and those entities may themselves have dependent entities.
Dependent entities are specified using the one
and many
directives, and by passing the output of a build
or create
call as a value in a with
build option.
Referring Entity
A referring entity is one that has dependent entities. It's generally used when talking about what happens when an entity is depended on.
Templates
A template is a collection of keywords and associated values that is used to generate an entity. The values in a template can be either constants, or directives. The simplest template is a map e.g.:
However, in some cases the ordering of evaluation of values might be important, e.g. when using the derive
directive. In this case, a template can be an ordered collection of maps and/or tuples, e.g.
The tuples here are equivalent to a map with a single key in it, either can be used. The order of evaluation is dictated by the list, and is undefined within maps.
In practice, Clojure maps below 8 keys are usually array maps that have a defined order of iteration, but it's best not to rely on this.
Compiled Template
Templates can come from the following sources in order of precedence:
The main factory template
Trait templates (in the same order they're specified in Build Options)
with
arguments in build-opts
These templates are compiled into a single Compiled template before the entity is built. Compiled templates are built one template at a time, in the order of precedence, preserving the ordering of keys that are already in the compiled template. As a concrete example:
i.e. tuples in a compiled template can have their value overwritten, but their ordering is fixed once added.
To build the entity, each key-value tuple is processed in order, the value is evaluated (processing any directives as necessary), and is then assoc'ed into the result map under the key.
Build Options
These functions accept build options to vary the entity being created:
build
build-list
create
create-list
one
many
Build options have the following structure, all keys are optional:
Output Options
These functions accept output options to vary the output from a factory:
build
build-list
create
create-list
Output as options have the following structure, all keys are optional:
The output-as
option supports a wide variety of values:
Build Graph
This section gets into the weeds about the build graph that Fabrikk uses internally. Concepts here shouldn't be necessary for understanding the documentation, but may be useful for understanding the code.
Basic Structure
When you build an entity it forms a node in the build graph. When an entity depends on another entity, both entities are nodes in the build graph, and there is one edge between them for each key in the referring entity whose value depends on the dependent entity. Edges are labelled with the corresponding key, and the :associate-as
value that is used to derive the value in the referring entity. As an example consider:
The build graph here has 2 nodes - users "Alice" and "Bob" - and 2 edges between those nodes. One edge is labelled :parent
with :associate-as
:id (the primary key of the user factory), and the other edge is labelled :parent-name
with :associate-as
:name.
Associating
When building a Dependent Entity, the act of referencing it on the primary entity and inserting it into the build graph is referred to as 'Association'. More concretely, when an entity is associated:
A
key
is required, and an:associate-as
option may be passed or inferredIf necessary the build graph of the dependent entity is merged into the build graph of the primary entity (only the first time this entity is associated)
An edge is created from the primary entity to the dependent entity. This edge is labelled with the
key
and:associate-as
optionA value is derived from the dependent entity using
:associate-as
, and then assoc'ed into the primary entity under thekey
If no :associate-as
option is provided, then the factory's :primary-key
is used if present, otherwise the identity function is used (i.e. the full dependent entity is assoc'ed into the primary entity).
Last updated