> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# What determines the order of datastore customizations?

> How Atoti FRTB sorts `DatastoreConfiguratorConsumer` beans with `@Order`, what the `ACCELERATOR`, `PROJECT` and `SIGN_OFF` constants of `DatastoreCustomisationSpringBeanOrder` mean, and which value a project customization declares

export const productName = "Atoti FRTB";

Datastore customizations are `DatastoreConfiguratorConsumer` beans, applied to the datastore in a defined order. The order decides where an appended field lands in a store. It also decides which customization wins when two of them insert after the same field. This ordering contract is available from {productName} 6.0.10.

For how to write a customization, see [Customizing the datastore with the datastore helper](./customising-the-datastore-with-the-datastore-helper).

## How are customizations ordered?

A datastore customization is a `com.activeviam.tools.datastore.DatastoreConfiguratorConsumer` bean.

`DatastoreCustomisationsConfig` injects every one of these beans as a `List<DatastoreConfiguratorConsumer>`. That configuration class belongs to the `frtb-starter` module, in the package `com.activeviam.frtb.starter.cfg.impl`. It applies each consumer to the `IDatastoreConfigurator` in list order.

No qualifier is needed. From {productName} 6.0.10 the list is no longer filtered on `@Qualifier(SP_QUALIFIER__CUSTOMISATIONS)`, and that constant is deprecated for removal. A bean that still carries it is applied as before.

Spring sorts the injected list with `AnnotationAwareOrderComparator`. That comparator reads the `org.springframework.core.annotation.Order` annotation declared on the `@Bean` factory method.

A bean that is not qualified with `customisations` is never applied.

## What are the sort rules?

Three rules follow from the Spring sort:

1. A lower order value is applied earlier. A consumer at `PROJECT` (100) is applied before a consumer at `SIGN_OFF` (200).
2. A bean with no `@Order` annotation is treated as `Ordered.LOWEST_PRECEDENCE`, which is `Integer.MAX_VALUE`. It is therefore applied after every annotated bean, not before.
3. Beans sharing the same order value have no guaranteed relative order. The sort is stable, so the result follows bean registration order, but that is not a contract. Two customizations that change the field layout of the same store must be given distinct order values.

## Which order values does {productName} use?

The order values are named constants of `com.activeviam.frtb.core.cfg.ordering.DatastoreCustomisationSpringBeanOrder`, in the `frtb-core` module.

| Constant | Value | Beans |
| - | - | - |
| `ACCELERATOR` | 90 | The accelerator's own base-store customizations. `CategoriesConfig#categoriesDatastoreCustomizer` in `frtb-activepivot` creates the `Categories` and `CategoriesSource` stores and a reference from `TradeMapping`. `FeatureDatastoreCustomisationsConfig#addCrypto2aExchangeToUnderlyingStore` in `frtb-starter` appends a crypto-exchange field to the `UnderlyingDescription` store. `FeatureDatastoreCustomisationsConfig#addContractualMaturityToSaSensitivitiesStore` in `frtb-starter` appends a contractual-maturity field to the `SASensitivities` store. |
| `PROJECT` | 100 | Reserved for project customizations. {productName} ships no bean at this value. |
| `SIGN_OFF` | 200 | `SignOffTaskConfig#signOffStores`, plus the five `signOffCustomisations` beans. |

Every consumer bean shipped by {productName} declares the matching constant rather than a bare number, as in `@Order(DatastoreCustomisationSpringBeanOrder.SIGN_OFF)`. A project customization declares `@Order(DatastoreCustomisationSpringBeanOrder.PROJECT)`.

## Why does the order matter?

The sign-off customization installs three key fields on the base store of a cube. Those fields are `Source`, `Input type` and `Version`. It is implemented by `SignOffAnalysisConfig.signOffCustomisations` in the Common Accelerator Library.

In {productName}, all five sign-off customizations append those key fields at the end of their store. They pass a null `previousField`, so none of them insert.

The following stores receive the sign-off key fields:

| Cube | Bean | Store |
| - | - | - |
| Standardised Approach | `saSignOffCustomisations` | `SASensitivities` |
| IMA Expected Shortfall | `imaSignOffCustomisations` | `IMATrades` |
| IMA DRC | `imaDrcSignOffCustomisations` | `DRCIMABase` |
| P\&L | `plSignOffCustomisations` | `PLTrades` |
| Stress calibration | `stressCalibrationSignOffCustomisations` | `StressCalibrationTrades` |

`signOffStores` is the sixth sign-off consumer. It creates the sign-off digest stores instead of changing a base store. It therefore collides with nothing at `SIGN_OFF`.

## When are customizations applied to the datastore?

A consumer usually does not modify a store when it is applied. It registers an intent on the `IDatastoreConfigurator` instead. `DatastoreCustomisationsConfig` calls `addModifications` first and `buildSchemas` second. Intents registered against a schema store are applied later, when that schema is built.

`signOffStores` is the exception. It builds the sign-off digest store descriptions while it is applied, during `addModifications`. Those descriptions are complete once `signOffStores` has run, at `SIGN_OFF` (200). A customization ordered after it is never applied to those stores.

At build time the intents are applied in a fixed phase order. Field updates come first, then removals, then insertions, then appends. The consumer order does not change that phase order. Three consequences follow:

* **Appended fields are sequenced by consumer order.** They are collected into a list in the order their customization was applied. The list is then added at the end of the store. This is why `SIGN_OFF` works for the sign-off beans: the three key fields land after every field appended at a lower order.
* **Inserted fields are positioned by their anchor field, not by consumer order.** The anchor is resolved at build time against the field list. The result is the same whichever order the consumer was applied in.
* **For insertions, consumer order decides which customization wins an anchor collision.** Insertions are held in a map keyed by the anchor field name. One store therefore carries only one insertion per anchor. When two customizations insert after the same anchor of the same store, the later consumer wins. The earlier insertion is dropped without an error.

Removals are applied before insertions. Removing a field that another customization uses as its anchor drops that insertion. That happens whatever order either consumer was applied in.

## How to choose an order value for a project customization

* An appended field lands before the sign-off key fields at any value below `SIGN_OFF` (200). `PROJECT` (100) is the band reserved for that.
* Use a value above 200 for a field that must follow the sign-off key fields.
* An inserted field is positioned by its anchor field, not by the order value. The order value matters only when another customization inserts after the same anchor of the same store.
* A customization that extends the sign-off digest stores needs a value below `SIGN_OFF` (200). `SignOffTaskConfig#signOffStores` builds those stores while it is applied, so a later customization is dropped without an error.

The example below declares a project customization at `PROJECT`.

```java theme={"languages":{"custom":["/engine/python-sdk/6.2/languages/pycon.tmLanguage.json"]}}
@Configuration
public static class ClientCustomisations {

	@Bean
	@Order(DatastoreCustomisationSpringBeanOrder.PROJECT)
	public DatastoreConfiguratorConsumer addFieldCustomization() {
		return datastoreConfigurator -> datastoreConfigurator
				.appendField("ExistingStore", new CustomField("MyNewField", ILiteralType.STRING));
	}
}
```
