Static Join on Properties
Materialized Joins for Denormalized, Read-Optimized Data. (PropertyStaticJoin)
1. Scope
PropertyStaticJoin (often referred to as static join) is a pattern on DataProperty that lets you:
This is effectively a materialized join:
-
It reads a field from a related DataObject (possibly in another service).
-
It writes that value into a property of the current object.
-
It does this automatically during create/update flows.
-
The value is stored in the current object and can be queried directly (DB or Elasticsearch) without additional joins.
Static joins are ideal when:
-
The joined data is not expected to change frequently, or the cost of being slightly stale is acceptable.
-
You want to avoid complex runtime joins in Business APIs or BFF DataViews.
-
You want a field (e.g.
clientId,statusName,categoryName) to be directly searchable or filterable in Elasticsearch or DB.
2. Conceptual Overview
2.1 Why Static Join?
Consider this typical pattern:
-
You have an object Order with
userId. -
You also need the user’s
clientId(tenant) inside each order to:-
Filter orders by client without joining to
user. -
Apply client-level analytics and reporting.
-
With static join:
-
You configure
Order.clientIdto be static-joined fromUser.clientId. -
When an order is created or updated:
-
Mindbricks reads the relevant
Userrecord. -
It copies
clientIdfromUsertoOrder.clientId.
-
-
Now Order has
clientIdas a direct field, and no additional join is needed at read time.
Static join is also useful for:
-
Storing parent’s tenant ID to children (e.g.,
projectId→project.clientId). -
Denormalizing frequently used labels (e.g.
countryName,categoryName). -
Pre-populating derived routing data (e.g.
organizationRegion).
2.2 Static vs Dynamic Joins
-
Static join:
-
Value is materialized and stored on the source object at write time.
-
Great for performance and filterability.
-
Needs explicit update logic when source changes (if you care about strict consistency).
-
-
Dynamic join (e.g., BFF**
ViewAggregate****)**:-
Join happens at read time.
-
Always up-to-date but more expensive and requires join context.
-
In Mindbricks, you often use:
-
Static join for core denormalized fields that are referenced everywhere.
-
BFF/DataViews for rich, multi-object read models.
3. Pattern Structure
From the ontology:
"PropertyStaticJoin": {
"__isStaticJoin.doc": "...",
"__configuration.doc": "The configuration object that defines the static join settings...",
"__isStaticJoin.default": false,
"__activation": "isStaticJoin",
"isStaticJoin": "Boolean",
"configuration": "PropertyStaticJoinConfig",
"__nullables": ["configuration"]
}
"PropertyStaticJoinConfig": {
"__jointSource.doc": "The name and optionally the service of the source object...",
"__jointRead.doc": "The name of the property in the source object whose value will be copied...",
"__jointSourceKey.doc": "The key in the source object used to identify the matching record...",
"__jointForeignKey.doc": "The property in the current object used as a reference to the source object...",
"jointSource": "DataObjectName",
"jointRead": "PropRefer",
"jointSourceKey": "PropRefer",
"jointForeignKey": "PropRefer",
"__jointRead.loadfrom": "jointSource",
"__jointSourceKey.loadfrom": "jointSource",
"__jointForeignKey.loadfrom": "currentDataObject"
}
3.1 Fields Explained
-
isStaticJoin(Boolean)-
Enables/disables static join for this property.
-
When
true, theconfigurationmust be provided.
-
-
configuration.jointSource(DataObjectName)-
The source object to join from.
-
Can be local or remote, e.g.
"user","auth:user","billing:customer".
-
-
configuration.jointRead(PropRefer)-
The property in the source object whose value you want to copy.
-
For example,
"clientId","tenantId","statusName". -
If not specified, it defaults to using the same property name as the target property (via UI
loadfromhints).
-
-
configuration.jointSourceKey(PropRefer)-
The key in the source object used to locate the record.
-
Typically
"id"or any other unique key. -
Usually
idis the primary field of the source object.
-
-
configuration.jointForeignKey(PropRefer)-
The field in the current object that refers to
jointSourceKey. -
For example, if the current object has
userIdreferencinguser.id, then:-
jointForeignKey = "userId" -
jointSourceKey = "id"
-
-
These fields tell Mindbricks how to navigate from the current record to the source object and which value to copy.
4. Execution Lifecycle & Formula Integration
The docs specify an important detail:
4.1 Order of Operations in a Create/Update API
For a given property:
-
Static join runs first (if
isStaticJoin = true).-
Mindbricks:
-
Reads
jointForeignKey(e.g.userId). -
Uses
jointSource&jointSourceKeyto fetch the related record. -
Extracts
jointReadvalue (e.g.clientId). -
Writes that value into the property’s field (or into the context under a join name).
-
-
-
Formula-based calculations (
PropertyFormulaSettings) run afterwards.-
These formulas can access:
-
The property itself (now filled with static join value).
-
Any context objects created by the static join.
-
-
This layered design lets you chain logic:
-
Use static join to fetch
clientIdfrom user. -
Use formula to compute something else using that
clientId.
5. Configuration Examples
5.1 Example 1 – Propagating Tenant/Client ID
Goal: When creating a new Order, automatically copy the clientId from the User so each order has a clientId field.
-
Resource object:
order(current DataObject) -
Source object:
auth:user -
Foreign key in**
order****:**userId→user.id -
Property to populate in**
order****:**clientId
DataProperty snippet for clientId:
{
"basicSettings": {
"name": "clientId",
"type": "ID",
"isRequired": true,
"allowUpdate": false
},
"relationSettings": {
"hasRelation": false
},
"staticJoin": {
"isStaticJoin": true,
"configuration": {
"jointSource": "auth:user",
"jointRead": "clientId",
"jointSourceKey": "id",
"jointForeignKey": "userId"
}
}
}
Result:
-
When
orderis created/updated:-
Mindbricks fetches
auth:userwhereid = order.userId. -
Reads
user.clientId. -
Stores it in
order.clientId.
-
Now you can:
-
Filter orders by
clientIddirectly, in DB or Elasticsearch. -
Use BFF DataViews that group orders by
clientIdwithout joining users.
5.2 Example 2 – Denormalizing Category Name
Goal: Store the category name on the product for direct search/sort.
-
Current object:
product -
Property:
categoryName -
Source:
productCategory -
Foreign key:
product.categoryId→productCategory.id
{
"basicSettings": {
"name": "categoryName",
"type": "String"
},
"staticJoin": {
"isStaticJoin": true,
"configuration": {
"jointSource": "productCategory",
"jointRead": "name",
"jointSourceKey": "id",
"jointForeignKey": "categoryId"
}
}
}
Now:
-
Full-text search or filters on
categoryNamedo not require a join. -
BFF or Business APIs can return the name directly from the product document.
5.3 Example 3 – Membership-Related Data
Static join also works with membership objects. Suppose projectMember has a membershipType that you want to store directly on some user-specific resource.
-
Current object:
userTask -
Foreign key:
userTask.projectId→project.id; plususerTask.userId -
Source:
projectMember -
key pair:
projectMember.projectId&projectMember.userId
Here static join can be used only if the membership object can be uniquely identified by the foreign keys (you might need a composite index and a helper API).
For more complex cases, you might prefer a BusinessApi FetchObjectAction instead of static join, but the pattern still gives you a denormalization option if you design membership keys appropriately.
6. UI & Pattern Editing Perspective
6.1 For Human Architects (UI)
In the Data Property editor:
-
Create or select the property you want to use as the target of the static join.
-
Open the Static Join section.
-
Enable “This property is a static join” (
isStaticJoin = true). -
Select:
-
Source object (
jointSource) -
Source key (
jointSourceKey– oftenid) -
Foreign key in current object (
jointForeignKey– e.g.userId) -
Source property to read (
jointRead)
-
Hints from __jointRead.loadfrom and __jointForeignKey.loadfrom make this easier by offering available properties from the referenced objects.
6.2 For AI Architects (JSON)
Ensure you set:
-
staticJoin.isStaticJoin = true -
staticJoin.configurationwith validjointSource,jointSourceKey,jointForeignKey, andjointRead(or let some defaults apply).
7. Best Practices & Pitfalls
7.1 Good Use Cases
-
Tenant/Client ID propagation to multiple entities.
-
Storing user’s main organization or region on child records.
-
Storing stable dictionary names (like category, country) for easier search and reporting.
-
Precomputing simplified routing values (e.g., a resolved region code).
7.2 Avoid Overusing Static Joins
-
If the joined value changes frequently and must always be real-time correct, static join may lead to stale data unless you design an update strategy.
-
For highly dynamic attributes, prefer dynamic joins in BFF DataViews or Business APIs.
7.3 Keep Source Data Stable
Static join works best when jointRead fields:
-
Change infrequently (e.g.,
clientIdis stable;categoryNamechanges rarely). -
Or where slight staleness is acceptable.
If required, you can create background tasks or Business APIs to resync static-joined fields when source objects change.
7.4 Watch Out for Circular Dependencies
Do not create a static join that:
-
Depends on an object that also depends on the current object in a conflicting manner.
-
For example,
Orderstatic-joining from an object that is itself static-joining fromOrderin a mutually recursive way.
8. Static Join vs Other Join Mechanisms in Mindbricks
| Mechanism | Where it lives | When it executes | Good for |
|---|---|---|---|
PropertyStaticJoin | DataProperty | Create/Update time | Denormalization & materialized fields |
ViewAggregate (DataView) | BFF Service | Read time (BFF queries) | Rich read models across many services |
FetchObjectAction | Business API workflow | Per API call, as needed | Custom, imperative joins inside workflows |
Static join complements these options:
-
Use it for fundamental fields that should exist directly on the object.
-
Use BFF & Business API actions for projection-level or one-off joins.
9. Summary
PropertyStaticJoin lets you define materialized joins at the property level:
-
You configure once:
-
which source object to join (
jointSource) -
which key fields to link (
jointSourceKey,jointForeignKey) -
which value to copy (
jointRead)
-
-
Mindbricks:
-
Automatically fetches the joined value during create/update.
-
Writes the joined value directly to the property.
-
Ensures static joins run before property formulas, so formulas can use the joined data.
-
This pattern is:
-
Simple to configure
-
Powerful for performance and reporting
-
Great for multi-tenant propagation, dictionary labels, and read-optimized schemas
-
Fully integrated with existing Mindbricks patterns and BFF/DataView architecture
Used thoughtfully, static joins dramatically reduce the need for runtime joins while keeping your patterns clean, declarative, and easy to maintain.
Last updated Dec 29, 2025