ApexBlueprint API reference: SOrchestrator
SOrchestrator is the execution engine of ApexBlueprint. It takes the blueprints assembled by SBlueprint, analyzes their dependencies via topological sort, and runs DML inserts in the correct order. The API is intentionally limited to just four methods.
For usage and typical scenarios, see Resolving Dependencies and Executing DML with SOrchestrator.
Static Factory
| Method | Purpose |
|---|---|
SOrchestrator.start() | For normal use. Uses the standard DML executor internally |
SOrchestrator.start(IDmlOperator dmlOperator) | Swap out the DML execution layer. In tests, pass new MockDmlOperator() to verify behavior without firing real DML |
// Production (real DML)
SOrchestrator orchestrator = SOrchestrator.start();
// Tests (no DML — the pattern used inside ApexBlueprint's own tests)
SOrchestrator orchestrator = SOrchestrator.start(new MockDmlOperator());
MockDmlOperator is included inside the ApexBlueprint OSS (DmlOperators/) and is used primarily when writing ApexBlueprint's own tests. For ordinary test data generation, use SOrchestrator.start() (no arguments).
Registration and Execution
| Method | Purpose |
|---|---|
add(SBlueprint blueprint) | Register a single SBlueprint into the queue. Addition order is ignored (internal topological sort determines the insertion order) |
create() | Analyze all registered blueprints and run DML inserts after dependency resolution. Returns void — capture the SOrchestrator first, then call create() on it |
SOrchestrator orchestrator = SOrchestrator.start()
.add(SBlueprint.of(Account.class).alias('parentAccount').template(Blueprints.accBasic()))
.add(
SBlueprint.of(Opportunity.class)
.set('Name', 'Test Opportunity')
.use('parentAccount', 'Id', 'AccountId')
.alias('targetOpp')
);
orchestrator.create();
Retrieving Results
| Method | Purpose |
|---|---|
getByAlias(String aliasName) | After create(), retrieve the created SObject by alias. Returns SObject (caller casts) |
Every returned record holds its Id and the fields set in the blueprint; fields you did not set are null. A record whose non-Id field was read by a later layer's use() also holds the saved values of the fields read back (v2.2.0+). Other values filled on save are not there, so query the record again if you need them.
Passing a non-existent alias returns null (not an exception), so if typos are easy to miss, guard with Assert.isNotNull(...) right after retrieval.
Account parent = (Account) orchestrator.getByAlias('parentAccount');
Opportunity opp = (Opportunity) orchestrator.getByAlias('targetOpp');
Blueprints bulked via .times(...) can be retrieved individually by their post-expansion alias names ('con_1' / 'con_2' / ...). Blueprints nested via withChildren are assigned auto-generated aliases like __Account_0_1_____Contact_1_1__, so attach an explicit .alias(...) whenever you need to retrieve them.
Common Exceptions (at create() Time)
| Situation | Exception type / message (excerpt) |
|---|---|
Circular dependency (both A.use('B', ...) and B.use('A', ...) hold) | ApexBlueprintException: Circular or invalid reference detected |
| Duplicate alias within the same blueprint chain | ApexBlueprintException: Duplicate alias detected |
Duplicate alias across different .add(...) calls | ApexBlueprintException: Duplicate alias detected |
Reference to a non-existent alias via .use(...) | ApexBlueprintException: Circular or invalid reference detected (internally the same category) |
A {#}-expanded alias that does not exist (shifted startAt or too few records) | ApexBlueprintException: Alias '...' not found. Available aliases: ... |
Child has multiple lookups but no parentIdField | ApexBlueprintException: multiple parent relationships with the same parent object |
No lookup at all between a withChildren parent and child | ApexBlueprintException: No parent relationship field found between ... |
The source record of a use() cannot be read back to copy its saved values (deleted by a trigger, a custom IDmlOperator that does not write to the database, etc.) | ApexBlueprintException: could not be read back (v2.2.0+) |
A saved value copied by use() does not fit the type of the target field | ApexBlueprintException: could not be copied (v2.2.0+) |
| Conditions that fail standard Apex DML (missing required fields, validation rule violations, etc.) | A plain DmlException, unchanged |
The type of the exception is itself the diagnosis. ApexBlueprintException means a mistake in the declaration (fix the test code); DmlException means the org refused the insert (fix the template or the org configuration).
⚠️ This is a breaking change in v2.0.0. Framework validation errors used to be plain
DmlExceptions, so existingcatch (DmlException)blocks will no longer catch them.
Large Layers Are Split Automatically (v2.2.0+)
Salesforce does not accept a single insert with more than 10 object types, or more than 10 chunks (a chunk ends where the type changes, and in a list of mixed types, also every 200 records of the same type). Up to v2.1.0, all records of a layer were inserted at once, so it failed when 11 types were lined up, when 2 types were added alternately, or when 6 types were created with 201 records each. From v2.2.0, records keep the order they were added in, and the insert is split only where a limit would be exceeded. A layer that passed before is still one insert, in the same order.
Related Documents
- Resolving Dependencies and Executing DML with SOrchestrator: How to use start / add / create / getByAlias
- API Reference: SBlueprint: The API for the blueprints you pass to
.add(...) - Relations, Bulk Generation, and Reference Patterns: Applications of
withChildren/times/{P0}etc. - Back to the ApexBlueprint Guide