Portal Community

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 TypeReferences ExtractedWhere 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.

Cross-Tenant References If an artifact references another artifact from a different tenant (which should not be possible in a well-configured system, but can occur during migration), the resolver will flag this as an error. Cross-tenant references cannot be resolved automatically.