Power PatternsStatic Joins
Power Patterns

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.clientId to be static-joined from User.clientId.

  • When an order is created or updated:

    • Mindbricks reads the relevant User record.

    • It copies clientId from User to Order.clientId.

  • Now Order has clientId as 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., projectIdproject.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, the configuration must 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 loadfrom hints).

  • configuration.jointSourceKey (PropRefer)

    • The key in the source object used to locate the record.

    • Typically "id" or any other unique key.

    • Usually id is 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 userId referencing user.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:

  1. Static join runs first (if isStaticJoin = true).

    • Mindbricks:

      • Reads jointForeignKey (e.g. userId).

      • Uses jointSource & jointSourceKey to fetch the related record.

      • Extracts jointRead value (e.g. clientId).

      • Writes that value into the property’s field (or into the context under a join name).

  2. 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 clientId from 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****:** userIduser.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 order is created/updated:

    • Mindbricks fetches auth:user where id = order.userId.

    • Reads user.clientId.

    • Stores it in order.clientId.

Now you can:

  • Filter orders by clientId directly, in DB or Elasticsearch.

  • Use BFF DataViews that group orders by clientId without 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.categoryIdproductCategory.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 categoryName do not require a join.

  • BFF or Business APIs can return the name directly from the product document.

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.projectIdproject.id; plus userTask.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:

  1. Create or select the property you want to use as the target of the static join.

  2. Open the Static Join section.

  3. Enable “This property is a static join” (isStaticJoin = true).

  4. Select:

    • Source object (jointSource)

    • Source key (jointSourceKey – often id)

    • 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.configuration with valid jointSource, jointSourceKey, jointForeignKey, and jointRead (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., clientId is stable; categoryName changes 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, Order static-joining from an object that is itself static-joining from Order in a mutually recursive way.


8. Static Join vs Other Join Mechanisms in Mindbricks

MechanismWhere it livesWhen it executesGood for
PropertyStaticJoinDataPropertyCreate/Update timeDenormalization & materialized fields
ViewAggregate (DataView)BFF ServiceRead time (BFF queries)Rich read models across many services
FetchObjectActionBusiness API workflowPer API call, as neededCustom, 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.

Was this page helpful?
Built with Documentation.AI

Last updated Dec 29, 2025