Skip to main content

Context Loaders

Overview

Context Loaders are a powerful BankLingo Process Engine feature that enables dynamic data loading into process contexts without hardcoding entity-specific logic. They provide a declarative, JSON-based approach to fetch data during process execution.

Key Benefits

  • 🎯 Declarative: Define data loading requirements in JSON, not code
  • 🔄 Reusable: Same loaders can be used across multiple processes
  • 🚀 Dynamic: Load any entity or execute any command at runtime
  • 📦 Type-Safe: Leverages existing entity contexts and command infrastructure
  • 🔌 Extensible: Easy to add new loaders without changing core engine

Use Cases

  1. User Task Forms: Pre-populate form fields with current entity data
  2. Service Tasks: Load reference data for validation or processing
  3. Gateway Conditions: Fetch data needed for routing decisions
  4. Event Handlers: Load entity context when external events trigger processes

Loader Formats

Context loaders support two primary formats:

1. Entity Loader Format

Load a complete entity context by entity type and ID.

{
"entity": "Loan",
"id": 12345
}

When to Use:

  • Need complete entity context with all properties
  • Entity ID is known at design time or available in process variables
  • Want to leverage typed context (LoanProcessContext, DepositProcessContext, etc.)

Supported Entity Types:

  • Loan - Loan accounts
  • Deposit - Deposit accounts
  • Transaction - Financial transactions
  • Contact / Customer - Customer/contact records
  • Transfer - Fund transfers
  • BillsPayment - Bills payment transactions
  • AirtimeRecharge - Airtime recharge transactions
  • DataRecharge - Data recharge transactions
  • LoanRequest - Loan applications
  • Teller - Teller transactions
  • Reconciliation - Reconciliation jobs
  • InsurancePolicy - Insurance policies
  • InsuranceClaims - Insurance claims
  • InwardTransaction - Inward payment transactions

2. Command Loader Format

Execute a MediatR command/query to fetch specific data.

{
"cmd": "GetLoanByIdQuery",
"context": {
"id": 12345,
"includeSchedule": true
}
}

When to Use:

  • Need specific data subsets (not entire entity)
  • Require complex queries with multiple parameters
  • Want to execute business logic during data fetch
  • Need to call external services or APIs

Examples:

// Fetch customer details
{
"cmd": "GetContactByIdQuery",
"context": {"id": 67890}
}

// Get account balance
{
"cmd": "GetDepositAccountBalanceQuery",
"context": {"accountNumber": "0123456789"}
}

// Fetch transaction history
{
"cmd": "GetTransactionHistoryQuery",
"context": {
"accountNumber": "0123456789",
"startDate": "2024-01-01",
"endDate": "2024-12-31"
}
}

Using Context Loaders

In User Task ServerScript

Context loaders are typically executed in User Task ServerScripts to pre-populate form data before displaying to users.

Example: Loan Approval Form

BPMN Definition:

<userTask id="Task_ApproveLoan" name="Approve Loan">
<extensionElements>
<banklingo:serverScript>
<![CDATA[
// Load loan context
var loader = {"entity": "Loan", "id": execution.getVariable("loanId")};
var loanContext = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
execution.getVariable("currentUser"),
execution.getAllVariables()
);

// Make context available to form
execution.setVariable("loan", loanContext);
]]>
</banklingo:serverScript>
</extensionElements>
</userTask>

Form Access:

// In your form component
const loan = processVariables.loan;

console.log(loan.loanAmount); // 50000
console.log(loan.customerId); // 67890
console.log(loan.status); // 0 (PENDING)
console.log(loan.statusDescription); // "PENDING"

In Service Task

Execute loaders within service tasks for data processing.

Example: Customer Validation Task

<serviceTask id="Task_ValidateCustomer" 
name="Validate Customer"
banklingo:class="com.banklingo.services.ValidationService"
banklingo:method="validateCustomer">
<extensionElements>
<banklingo:serverScript>
<![CDATA[
// Load customer details
var loader = {
"cmd": "GetContactByIdQuery",
"context": {"id": execution.getVariable("customerId")}
};

var customerContext = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
execution.getVariable("currentUser"),
execution.getAllVariables()
);

// Perform validation
var isValid = customerContext.kycStatus === "Approved"
&& customerContext.accountStatus === "Active";

execution.setVariable("customerValid", isValid);
]]>
</banklingo:serverScript>
</extensionElements>
</serviceTask>

With Variable Substitution

Use variable placeholders {{variableName}} to reference process variables.

Example: Dynamic Entity Loading

// Process has variables: entityType="Loan", entityId=12345

var loader = {
"entity": "{{entityType}}", // Resolves to "Loan"
"id": "{{entityId}}" // Resolves to 12345
};

var context = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
currentUser,
execution.getAllVariables()
);

Example: Parameterized Commands

// Process has variables: accountNumber="0123456789", startDate="2024-01-01"

var loader = {
"cmd": "GetTransactionHistoryQuery",
"context": {
"accountNumber": "{{accountNumber}}",
"startDate": "{{startDate}}",
"endDate": "2024-12-31"
}
};

var history = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
currentUser,
execution.getAllVariables()
);

API Reference

IContextLoaderService

The main service interface for executing context loaders.

Methods

ExecuteContextLoaderAsync
Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Parameters:

  • contextExpression - JSON string defining the loader (entity or command format)
  • createdBy - Username of the user executing the loader
  • variables - Optional process variables for parameter substitution

Returns: BaseProcessContext containing the loaded entity or command result data

Throws:

  • ArgumentException - Invalid or malformed context expression
  • InvalidOperationException - Entity not found or command execution failed
  • NotSupportedException - Unsupported entity type or legacy format

Example:

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

IsValidContextExpression
Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Validates whether a context expression is in valid JSON format.

Example:

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

GetLoaderType
Code Removed

Implementation details removed for security.

Contact support for implementation guidance.

Determines the loader type from a context expression.

Returns: "Entity", "Command", "Legacy", or "Unknown"

Example:

Code Removed

Implementation details removed for security.

Contact support for implementation guidance.


Available Context Loaders

Entity Loaders

Pre-configured loaders for all supported entity types.

CodeNameDescriptionExample
ENTITY_LOANEntity - LoanLoads loan entity context by ID{"entity":"Loan","id":12345}
ENTITY_CONTACTEntity - Contact/CustomerLoads contact/customer context{"entity":"Contact","id":67890}
ENTITY_DEPOSITEntity - Deposit AccountLoads deposit account context{"entity":"Deposit","id":54321}
ENTITY_TRANSACTIONEntity - TransactionLoads transaction context{"entity":"Transaction","id":98765}
ENTITY_TRANSFEREntity - TransferLoads transfer context{"entity":"Transfer","id":11111}
ENTITY_BILLS_PAYMENTEntity - Bills PaymentLoads bills payment context{"entity":"BillsPayment","id":22222}
ENTITY_AIRTIME_RECHARGEEntity - Airtime RechargeLoads airtime recharge context{"entity":"AirtimeRecharge","id":33333}
ENTITY_DATA_RECHARGEEntity - Data RechargeLoads data recharge context{"entity":"DataRecharge","id":44444}
ENTITY_LOAN_REQUESTEntity - Loan RequestLoads loan request context{"entity":"LoanRequest","id":55555}
ENTITY_TELLEREntity - Teller TransactionLoads teller transaction context{"entity":"Teller","id":66666}
ENTITY_RECONCILIATIONEntity - ReconciliationLoads reconciliation context{"entity":"Reconciliation","id":77777}
ENTITY_INSURANCE_POLICYEntity - Insurance PolicyLoads insurance policy context{"entity":"InsurancePolicy","id":88888}
ENTITY_INSURANCE_CLAIMSEntity - Insurance ClaimsLoads insurance claims context{"entity":"InsuranceClaims","id":99999}
ENTITY_INWARD_TRANSACTIONEntity - Inward TransactionLoads inward transaction context{"entity":"InwardTransaction","id":10101}

Command Loaders

Pre-configured loaders for common query commands.

CodeNameDescriptionExample
CMD_FETCH_LOAN_DETAILSCommand - Fetch Loan DetailsFetches loan details via query{"cmd":"GetLoanByIdQuery","context":{"id":12345}}
CMD_FETCH_CUSTOMER_DETAILSCommand - Fetch Customer DetailsFetches customer details{"cmd":"GetContactByIdQuery","context":{"id":67890}}
CMD_FETCH_ACCOUNT_BALANCECommand - Fetch Account BalanceFetches account balance{"cmd":"GetDepositAccountBalanceQuery","context":{"accountNumber":"0123456789"}}
CMD_FETCH_TRANSACTION_HISTORYCommand - Fetch Transaction HistoryFetches transaction history{"cmd":"GetTransactionHistoryQuery","context":{"accountNumber":"0123456789","startDate":"2024-01-01","endDate":"2024-12-31"}}
CMD_VALIDATE_CUSTOMERCommand - Validate CustomerValidates customer information{"cmd":"ValidateCustomerCommand","context":{"customerId":67890}}

Advanced Scenarios

Chaining Multiple Loaders

Load multiple related entities in sequence.

// Load loan first
var loanLoader = {"entity": "Loan", "id": execution.getVariable("loanId")};
var loan = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loanLoader),
currentUser,
execution.getAllVariables()
);

// Extract customer ID from loan
execution.setVariable("customerId", loan.customerId);

// Load customer
var customerLoader = {"entity": "Contact", "id": "{{customerId}}"};
var customer = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(customerLoader),
currentUser,
execution.getAllVariables()
);

// Both contexts now available
execution.setVariable("loan", loan);
execution.setVariable("customer", customer);

Conditional Loading

Load different entities based on process state.

var entityType = execution.getVariable("transactionType");
var entityId = execution.getVariable("transactionId");

var loader = {
"entity": entityType === "TRANSFER" ? "Transfer" :
entityType === "BILLS" ? "BillsPayment" :
entityType === "AIRTIME" ? "AirtimeRecharge" : "Transaction",
"id": entityId
};

var context = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
currentUser,
execution.getAllVariables()
);

Error Handling

Gracefully handle loader failures.

try {
var loader = {"entity": "Loan", "id": execution.getVariable("loanId")};
var loan = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
currentUser,
execution.getAllVariables()
);

execution.setVariable("loan", loan);
execution.setVariable("loadSuccess", true);
} catch (error) {
execution.setVariable("loadSuccess", false);
execution.setVariable("loadError", error.message);

// Log error
logger.error("Failed to load loan context", error);

// Can trigger error boundary event
throw new BpmnError("LOAD_FAILED", "Failed to load entity context");
}

Custom Command Execution

Execute custom commands with complex parameters.

var loader = {
"cmd": "CalculateLoanEligibilityCommand",
"context": {
"customerId": execution.getVariable("customerId"),
"requestedAmount": execution.getVariable("loanAmount"),
"tenor": execution.getVariable("loanTenor"),
"productCode": execution.getVariable("productCode"),
"includeAssessment": true,
"includeRiskScore": true
}
};

var eligibility = await contextLoaderService.ExecuteContextLoaderAsync(
JSON.stringify(loader),
currentUser,
execution.getAllVariables()
);

// Access eligibility result
if (eligibility.isEligible) {
execution.setVariable("maxLoanAmount", eligibility.maxEligibleAmount);
execution.setVariable("interestRate", eligibility.recommendedRate);
} else {
execution.setVariable("rejectionReason", eligibility.reason);
}

Implementation Details

How Entity Loaders Work

  1. Parse JSON: Extract entity type and ID from context expression
  2. Resolve Variables: Substitute any variable placeholders ({{variableName}})
  3. Map Entity Type: Convert entity name string to ProcessEntityType enum
  4. Query Database: Fetch entity from appropriate repository using ID
  5. Convert to Dictionary: Extract all entity properties into key-value pairs
  6. Create Context: Use ProcessContextFactory.CreateContext() to generate typed context
  7. Return Context: Return fully populated BaseProcessContext (e.g., LoanProcessContext)

Key Classes:

  • ContextLoaderService.ExecuteEntityLoaderAsync()
  • ProcessContextFactory.CreateContext()
  • Entity repositories via IUnitOfWork

How Command Loaders Work

  1. Parse JSON: Extract command name and context parameters
  2. Resolve Variables: Substitute variable placeholders in parameters
  3. Resolve Command Type: Find command class by name using reflection
  4. Create Command Instance: Instantiate command and set properties from parameters
  5. Execute via MediatR: Send command through IMediator.Send()
  6. Convert Result: Transform command result into BaseProcessContext
  7. Return Context: Return context with command result data

Key Classes:

  • ContextLoaderService.ExecuteCommandLoaderAsync()
  • MediatR IMediator interface
  • Command/Query classes implementing IRequest<T>

Variable Substitution

Variable placeholders support multiple formats:

// Format 1: Direct variable name
{"id": "loanId"} // Looks up "loanId" in variables dict

// Format 2: Curly brace placeholder
{"id": "{{loanId}}"} // Extracts variable name, looks up value

// Format 3: Direct value (no substitution)
{"id": 12345} // Uses value as-is

Substitution Rules:

  • Variables must exist in process variables dictionary
  • Type conversion is attempted automatically (string → long, etc.)
  • Missing variables throw ArgumentException
  • Invalid type conversions throw InvalidCastException

Best Practices

✅ DO

  • Use entity loaders when you need complete entity context
  • Use command loaders for targeted queries or business logic execution
  • Validate loader format before execution using IsValidContextExpression()
  • Handle errors gracefully with try-catch blocks
  • Cache loaded contexts in process variables to avoid redundant queries
  • Document custom loaders in process definition comments

❌ DON'T

  • Don't use legacy function-style loaders (e.g., FetchLoan(id)) - use JSON format
  • Don't hardcode IDs - use variable substitution for flexibility
  • Don't load large datasets - use pagination or filtering in commands
  • Don't ignore errors - always handle loader failures appropriately
  • Don't reload same entity multiple times - cache in variables

Performance Tips

  1. Load Once, Use Many Times: Cache loaded contexts in variables

    // ❌ Bad: Load multiple times
    var loan1 = await load({"entity":"Loan","id":123});
    var loan2 = await load({"entity":"Loan","id":123}); // Redundant!

    // ✅ Good: Load once, reuse
    var loan = await load({"entity":"Loan","id":123});
    execution.setVariable("loan", loan); // Cache in variable
    // Use "loan" variable throughout process
  2. Use Specific Commands for Partial Data: Don't load entire entities for single fields

    // ❌ Bad: Load entire loan just for amount
    var loan = await load({"entity":"Loan","id":123});
    var amount = loan.loanAmount;

    // ✅ Good: Use specific command
    var result = await load({
    "cmd":"GetLoanAmountQuery",
    "context":{"id":123}
    });
  3. Batch Loading: Use commands that support batch fetching

    // ✅ Good: Load multiple loans in one query
    var loans = await load({
    "cmd":"GetLoansByIdsQuery",
    "context":{"ids":[123,456,789]}
    });

Troubleshooting

Error: "Entity type is required in entity loader format"

Cause: Missing or empty entity field in loader JSON.

Solution:

// ❌ Wrong
{"id": 12345}

// ✅ Correct
{"entity": "Loan", "id": 12345}

Error: "Invalid entity type: XYZ"

Cause: Entity type doesn't match any ProcessEntityType enum value.

Solution: Use one of the supported entity types (case-insensitive):

  • Loan, Deposit, Transaction, Contact, Transfer, BillsPayment, AirtimeRecharge, DataRecharge, LoanRequest, Teller, Reconciliation, InsurancePolicy, InsuranceClaims, InwardTransaction

Error: "Entity not found: Loan with ID 12345"

Cause: No entity exists in database with specified ID.

Solution:

  • Verify entity ID is correct
  • Check if entity was deleted
  • Ensure ID variable substitution is working correctly

Error: "Command not found: GetLoanByIdQueryXYZ"

Cause: Command class doesn't exist or isn't registered with MediatR.

Solution:

  • Verify command name spelling
  • Ensure command implements IRequest<T>
  • Check MediatR registration in dependency injection

Error: "Variable not found: loanId"

Cause: Referenced variable doesn't exist in process variables.

Solution:

  • Verify variable name spelling
  • Ensure variable was set before loader execution
  • Check variable scope (process instance vs. local task)

Error: "Legacy function-style loaders are no longer supported"

Cause: Using old format like FetchLoan(12345).

Solution: Convert to JSON format:

// ❌ Legacy (deprecated)
"FetchLoan(12345)"

// ✅ New format
{"entity":"Loan","id":12345}

Migration from Legacy Loaders

If you have existing processes using legacy function-style loaders, follow this migration guide:

Step 1: Identify Legacy Loaders

Search for context expressions containing parentheses:

  • FetchLoan(loanId)
  • ValidateCustomer(customerId)
  • FetchAccountBalance(accountId)

Step 2: Convert to JSON Format

Legacy FormatNew JSON Format
FetchLoan(loanId){"entity":"Loan","id":"{{loanId}}"}
ValidateCustomer(customerId){"cmd":"ValidateCustomerCommand","context":{"customerId":"{{customerId}}"}}
FetchAccountBalance(accountId){"cmd":"GetDepositAccountBalanceQuery","context":{"accountId":"{{accountId}}"}}

Step 3: Update Process Definitions

Replace old loader calls with new JSON format in BPMN XML.

Step 4: Test Thoroughly

  • Verify variable substitution works correctly
  • Ensure entity/command resolution succeeds
  • Check error handling paths


Summary

Context Loaders provide a powerful, declarative way to load data into BankLingo processes:

  • Two formats: Entity-based and Command-based
  • 14 entity types supported out-of-the-box
  • Unlimited custom commands via MediatR integration
  • Variable substitution for dynamic parameter passing
  • Type-safe using existing ProcessContext infrastructure
  • Extensible - easy to add new loaders and commands

Use entity loaders for complete entity contexts, command loaders for targeted queries, and variable substitution for maximum flexibility.