Dependency Resolution
The DependencyResolver traverses the artifact reference graph recursively, discovering every artifact that a root requires — so the package consumer never encounters a missing reference at install time.
Why Dependency Resolution Matters
A workflow in BizFirstGO is never an island. It references forms, rules, entity schemas, and often sub-workflows. If you manually export just the process definition and install it in another tenant, the workflow will fail at runtime when it tries to load a form that doesn't exist.
The DependencyResolver eliminates this problem by following every reference in every artifact until the full closure is discovered. The resulting bundle is self-contained — everything needed is inside.
How the Resolver Works
The resolver implements a breadth-first traversal with a visited set to prevent cycles:
public class DependencyResolver : IDependencyResolver
{
public async Task<DependencyGraph> ResolveAsync(
string[] rootArtifactIds,
string tenantId,
CancellationToken ct = default)
{
var visited = new HashSet<string>();
var queue = new Queue<string>(rootArtifactIds);
var graph = new DependencyGraph();
while (queue.Count > 0)
{
var artifactId = queue.Dequeue();
if (!visited.Add(artifactId)) continue; // already processed
var artifact = await _repository.GetAsync(artifactId, tenantId, ct);
var dependencies = await _extractor.ExtractReferencesAsync(artifact, ct);
graph.AddArtifact(artifact);
foreach (var dep in dependencies)
{
graph.AddEdge(artifactId, dep.Id);
if (!visited.Contains(dep.Id))
queue.Enqueue(dep.Id);
}
}
return graph;
}
}
Reference Extraction Per Artifact Type
Each artifact type has a dedicated reference extractor that knows where references are stored in that artifact's JSON structure:
| Artifact Type | References Extracted | Where Found |
|---|---|---|
ProcessDefinition |
ThreadDefinitions, AtlasForms, RuleSets, EntitySchemas | Node configurations in the workflow graph JSON |
ThreadDefinition |
AtlasForms, RuleSets, EntitySchemas | Node configurations in the thread graph JSON |
AtlasForm |
FieldLibraries, RuleSets, EntitySchemas | Field source bindings and validation rule references |
RuleSet |
EntitySchemas | Entity references in rule conditions |
EntitySchema |
None (leaf node) | — |
AppDefinition |
AtlasForms, ProcessDefinitions | Widget bindings and action references |
Dependency Graph Output
The resolved graph is a directed acyclic graph (DAG) that encodes the full dependency tree:
{
"rootArtifacts": ["proc-1001"],
"nodes": [
{ "id": "proc-1001", "type": "ProcessDefinition", "name": "EmployeeOnboarding", "depth": 0 },
{ "id": "thread-2002","type": "ThreadDefinition", "name": "ApprovalSubflow", "depth": 1 },
{ "id": "form-2005", "type": "AtlasForm", "name": "EmployeeForm", "depth": 1 },
{ "id": "rule-305", "type": "RuleSet", "name": "ApprovalRules", "depth": 1 },
{ "id": "lib-12", "type": "FieldLibrary", "name": "CommonFields", "depth": 2 },
{ "id": "ent-44", "type": "EntitySchema", "name": "EmployeeSchema", "depth": 2 }
],
"edges": [
{ "from": "proc-1001", "to": "thread-2002" },
{ "from": "proc-1001", "to": "form-2005" },
{ "from": "proc-1001", "to": "rule-305" },
{ "from": "form-2005", "to": "lib-12" },
{ "from": "form-2005", "to": "ent-44" },
{ "from": "rule-305", "to": "ent-44" }
],
"installOrder": ["ent-44", "lib-12", "rule-305", "form-2005", "thread-2002", "proc-1001"]
}
Install Order
The graph is topologically sorted to produce an install order where dependencies are always installed before the artifacts that depend on them. In the example above, EntitySchema is installed first because no artifact it depends on; ProcessDefinition is installed last because it depends on everything else.
Shared Dependencies
When multiple root artifacts reference the same dependency, that dependency appears only once in the bundle. For example, if two workflows both use the same EmployeeForm, the form is serialized once and both workflow definitions reference it by the same ID:
// Both proc-1001 and proc-1003 reference form-2005
// The bundle contains form-2005 only once
artifacts/forms/form-2005.json ← single copy
// Both workflow JSONs reference it:
// proc-1001.json: "formId": "form-2005"
// proc-1003.json: "formId": "form-2005"
Circular Dependency Detection
The resolver detects circular references (e.g., Workflow A includes Sub-workflow B which somehow references Workflow A) and aborts the export with a clear error:
{
"error": "CircularDependencyDetected",
"message": "Circular reference detected: proc-1001 → thread-2002 → proc-1001",
"cycle": ["proc-1001", "thread-2002", "proc-1001"]
}
This is a data integrity issue that must be fixed in the source tenant before the export can proceed.