Perspective: onChange script vs binding graph for multiple derived properties — which approach scales better?

I have a Perspective view that receives data as a parameter. From that single source, I need to derive several custom properties — processed data, validation results, and state flags.

I'm trying to decide between two approaches:

Option A — Single onChange script
All derived properties are set inside one params.value onChange handler. One execution, explicit order, easy to read top-to-bottom.

Option B — Binding chain
Each custom property has its own binding with a script transform. The chain looks like this:

  • custom.A → bound to params.value, script transform decodes/normalizes the raw data

  • custom.B → bound to custom.A, script transform runs validation and returns a result object

  • custom.C → bound to custom.B, transform extracts a boolean flag from the result

  • custom.D → bound to custom.A + operator input, determines whether a change was made

  • Button enabled → bound to custom.C + custom.D

Fully declarative — each property is self-contained and updates automatically when its source changes.

Where I have concerns with Option B:

As the chain grows deeper (A → B → C → UI state), it becomes harder to trace why a UI element is in a certain state — you have to open each property's binding individually. With Option A, you read one script top-to-bottom.

My questions:

  1. Does Perspective guarantee evaluation order when a binding change cascades through a chain like A → B → C, or can you hit a state where C reads B before B has finished updating from A?

  2. At what complexity — number of derived properties or chain depth — does one approach clearly win over the other?

  3. Any general best practice for this pattern in Perspective?

  1. No order guarantees.

  2. Any jython is a performance suck, and binding transforms also have overhead.

I would architect those dependant properties as subprops of another. So, custom.group.A, custom.group.B, et cetera, with a single expression binding, using my Integration Toolkit, like so:

transform(
    {view.params.value},
    asMap(
        'A', ...nested expression using value()...,
        'B', ...nested expression using value()...,
        'C', ...nested expression using value()...,
        'D', ...nested expression using value()...
    )
)

Very similar to an expression structure binding, but fully atomic with my transform() expression function, and doesn't have any native transform overhead.

When parts depend on other parts, you can nest transform() to capture subexpressions. Perhaps like so:

transform(
    ...expression A using {view.params.value}...,
    transform(
        asList(
            ...expression B using value()...,
            ...expression D using value() and {view.custom.operatorInput}...
        ),
        transform(
            ...expression C using value()[0]...,
            asMap(
                'A', value(2),
                'B', value(1)[0],
                'C', value(),
                'D', value(1)[1]
            )
        )
    )
)