# Resolving Dependencies and Executing DML with SOrchestrator

`SOrchestrator` is the **engine that turns blueprints assembled by `SBlueprint` into real records**. It automatically handles the parent → child insertion order, copying parent Ids into children, and resolving values referenced via aliases.

This page walks through the four methods of `SOrchestrator` (`start` / `add` / `create` / `getByAlias`) in order, and ends with a list of common pitfalls in real-world usage.

## SOrchestrator.start(): Initialize the Builder

Every operation begins with this static method. It returns an empty `SOrchestrator` instance; you `.add(...)` blueprints to it, then call `.create()` to execute.

**Signature:** `SOrchestrator.start()`

```apex
SOrchestrator orchestrator = SOrchestrator.start();
```

## .add(blueprint): Register a Blueprint in the Queue

`.add(...)` registers a single `SBlueprint` into SOrchestrator's queue.

**Signature:** `.add(SBlueprint blueprint)`

```apex
SOrchestrator.start()
    .add(
        SBlueprint.of(Account.class)
            .template(Blueprints.accBasic())
            .alias('parentAccount')
    )
    .add(
        SBlueprint.of(Opportunity.class)
            .set('Name', 'Test Opportunity')
            .set('StageName', 'Prospecting')
            .set('CloseDate', Date.today().addDays(30))
            .use('parentAccount', 'Id', 'AccountId')
            .alias('targetOpp')
    );
```

### Behavior Notes

- **Addition order is ignored.** SOrchestrator analyzes the dependencies internally and re-derives the insertion order via topological sort
- It is fine to write the child before the parent. You can write blueprints in "the most readable order"
- Multiple `.add(...)` calls can be chained on the same SOrchestrator
- If you express parent-child relationships within a single blueprint via `withChildren`, registering the parent alone is enough — children come along with it (see [Relations, Bulk Generation, and Reference Patterns](/apex-stem/docs/apex-blueprint-relations-and-bulk))

## .create(): Resolve Dependencies and Run DML

`.create()` analyzes the registered blueprints and **executes the DML inserts in the correct order**. By the time this call returns, every record is in the database and Ids have been issued.

**Signature:** `.create()`

```apex
SOrchestrator orchestrator = SOrchestrator.start()
    .add(/* ... */)
    .add(/* ... */);
orchestrator.create();
```

### Behavior Notes

- Internally runs a **topological sort** and inserts the depended-upon side (parents) first
- Id references like `.use('alias', 'Id', 'AccountId')` copy the parent's Id (issued right after its insert) into the child's field before inserting the child
- DML failures throw the usual Apex DML exceptions
- **The return value is `void`.** You cannot append `.create()` to the `.add(...)` chain and assign it to a variable. Capture the `SOrchestrator` in a variable first, call `orchestrator.create()`, then call `getByAlias(...)` on that same variable

## .getByAlias(name): Retrieve a Created Record

After `create()`, you can retrieve the created records by alias. You can bring "the Account you just created" to your hand without writing a SOQL query, which keeps assertion code short.

**Signature:** `.getByAlias(String aliasName)` (returns `SObject`)

```apex
SOrchestrator orchestrator = SOrchestrator.start()
    .add(
        SBlueprint.of(Account.class)
            .template(Blueprints.accBasic())
            .alias('parentAccount')
    );
orchestrator.create();

Account parent = (Account) orchestrator.getByAlias('parentAccount');
Assert.isNotNull(parent.Id);
```

### Behavior Notes

- The return value is `SObject`, so the caller casts it to the desired SObject type
- **Passing a non-existent alias returns `null`** (not an exception). Typos are easy to miss, so guard with `Assert.isNotNull(...)` right after retrieval as a safe default
- Records bulked via `.times(n)` with a `'{#}'` placeholder alias (e.g. `'con_{#}'`) can be retrieved individually via the expanded aliases like `'con_1'` / `'con_2'` / ... (see [Relations, Bulk Generation, and Reference Patterns](/apex-stem/docs/apex-blueprint-relations-and-bulk))

## Example: A Complete Test Workflow

Combining `start` → `add` → `create` → `getByAlias` into a single test yields:

```apex
@isTest
static void testOppCreationWithAccount() {
    SOrchestrator orchestrator = SOrchestrator.start()
        .add(
            SBlueprint.of(Account.class)
                .template(Blueprints.accBasic())
                .alias('parentAccount')
        )
        .add(
            SBlueprint.of(Opportunity.class)
                .set('Name', 'Test Opportunity')
                .set('StageName', 'Prospecting')
                .set('CloseDate', Date.today().addDays(30))
                .use('parentAccount', 'Id', 'AccountId')
                .alias('targetOpp')
        );
    orchestrator.create();

    Account parent = (Account) orchestrator.getByAlias('parentAccount');
    Opportunity opp = (Opportunity) orchestrator.getByAlias('targetOpp');

    Assert.areEqual(parent.Id, opp.AccountId);
    Assert.areEqual('Test Opportunity', opp.Name);
}
```

Points:

- **Not a single SOQL in the test body.** Assertion targets are retrieved directly via `getByAlias(...)`
- We `.add(...)` the `Account` first here, but if we instead `.add(...)` the `Opportunity` first, the result would be identical — SOrchestrator re-orders things via dependency analysis
- The parent Id is copied into the child by the `.use('parentAccount', 'Id', 'AccountId')` declaration alone. The procedure of "insert the parent, grab its Id into a variable, assign it to the child's AccountId..." disappears

## Common Pitfalls

### Circular Dependencies

If you create a dependency where both `A.use('B', ...)` and `B.use('A', ...)` hold, the topological sort cannot resolve it and `.create()` fails with `Circular or invalid reference detected`. Reconsider the design — either make one side a one-way reference, or replace the parent-child link with `withChildren`.

### Duplicate Aliases

If `.alias('foo')` appears in two places within the same `SOrchestrator`, you'll hit `Duplicate alias detected`. When bulking via `.times(n)`, declare aliases with a placeholder like `'foo_{#}'` so that unique aliases are issued after expansion.

### Reference to a Non-Existent Alias

If you reference an alias that hasn't been declared anywhere — e.g. `.use('typoAlias', ...)` — the call fails at `.create()` with the same family of errors (`Circular or invalid reference detected`). Alias typos are easy to miss, so it pays to develop the habit of reviewing "the first argument of `.use`" alongside "its corresponding `.alias`" as a pair.

### Don't Rely on Auto-Generated Aliases for Retrieval

Omitting `.alias(...)` is fine for registering a blueprint; internally it gets an **auto-generated alias** like `__Account_0_1__`. However, retrieving these via `getByAlias` later is impractical. **If you plan to retrieve a blueprint via `getByAlias`, always attach an explicit `.alias(...)`** as a rule.

## Related Documents

- [Declaring a Single Record with SBlueprint](/apex-stem/docs/apex-blueprint-sblueprint-guide): How to build the blueprints you pass to `.add(...)`
- [Relations, Bulk Generation, and Reference Patterns](/apex-stem/docs/apex-blueprint-relations-and-bulk): Applications of `withChildren` / `times` / `{#}` / `{P0}` `{P1}`
- [API Reference: SOrchestrator](/apex-stem/docs/apex-blueprint-api-sorchestrator): Full method signatures
- [Back to the ApexBlueprint Guide](/apex-stem/docs/apex-blueprint-guide)
