Skip to content
Art2link ESB v2.02 LTS HomeDocumentationBlogContact
Build & extend/Pipeline components
Developer Reference

Pipeline components

Build reusable message processing logic that Art2link ESB compiles and deploys automatically — no build steps, no restarts, no downtime.

A Pipeline Component is a C# class that processes messages flowing through an Art2link ESB Pipeline. Each component receives a message, applies your logic, and returns one or more messages to the next stage of the pipeline.

Art2link ESB handles compilation and deployment automatically. You write the code, save it, and reference it from a Pipeline — no build steps, no restarts, no downtime.

The architecture follows a simple chain: Port → Pipeline → Component(s) → Output. A Pipeline can contain multiple components executed in sequence. Each component operates on the output of the previous one.

Two kinds of work happen inside a component. The obvious one is transformation — reshape the body, enrich it, compress, sign, validate. The second — available only when the component runs on the inbound side of a flow — is classification: the component inspects the message and assigns it a Message Type by setting MessageType on the outgoing PipelineMessage before the bus publishes. See Components can classify the Message Type in the Pipelines article for the full contract; the code-level mechanics live in this reference.

Pipeline components are system-scoped, not application-scoped. A component lives at the platform level — one component library is visible to every Application. Any pipeline in any Application can reference any defined component by Name, and editing a component's code propagates to every pipeline that uses it across the whole system. This is the opposite of pipelines themselves, which are owned by a single Application.
SYSTEM LEVEL — SHARED ACROSS THE PLATFORM COMPONENT LIBRARY fnCompress transform fnVerifySig verify fnClassify set type … one library, one source of truth REFERENCED BY NAME APPLICATION LEVEL — PIPELINES OWNED BY ONE APPLICATION APPLICATION A PIPELINE — INBOUND_ORDERS fnVerifySig → fnClassify PIPELINE — OUTBOUND_INV fnCompress APPLICATION B PIPELINE — PARTNER_IN fnVerifySig → fnClassify PIPELINE — PARTNER_OUT fnCompress APPLICATION C PIPELINE — HR_FEED fnClassify PIPELINE — HR_DROP fnCompress → fnVerifySig Edit a component once — every pipeline above picks up the change on the next message.

QUICK START — FIVE STEPS 1 From template scaffold the component 2 Define config UI input fields 3 ExecuteAsync your logic here 4 Save AUTO-COMPILED 5 Reference add to pipeline USINGS · CONFIG · TRY/CATCH Start from the supplied scaffold — don’t redefine domain models. PUBLIC PROPERTIES Each property becomes a field in the UI. Add defaults inline. READ · TRANSFORM · RETURN Read input.Body, apply your work, return one or many PipelineMessages. NO BUILD · NO RESTART Art2link ESB compiles and deploys on save. No downtime. REFERENCED BY NAME Add by Name from any pipeline; attach pipeline to a port. The whole loop is edit → save → next message uses the new code. No deploy step.

The following classes are provided by the Art2link runtime in the CC.Art2link.Pipelines.Domain.Models.PipelineComponents namespace. Do not redefine them in your component code.

EXECUTEASYNC CONTRACT PipelineComponentInput string Body never null — defaults to "" PLUS — HANDED IN BY ENGINE: TConfig config CancellationToken token YOUR OVERRIDE ExecuteAsync protected override Task<PipelineComponentOutput> read input.Body, transform, return one or many messages PipelineComponentOutput bool Success IList<PipelineMessage> Messages IDictionary<string,string> Variables — writes back to ESB Variables string? ErrorMessage Exception? EACH ITEM: PipelineMessage string Body string MessageType — classifies the message (inbound only) YOU INHERIT FROM PipelineComponentBase<TConfig> The base handles JSON deserialization of configJson into TConfig, then calls your override above. Empty Messages list = filtered out. Multiple Messages items = fan-out. Success = false = pipeline halts.
PipelineComponentInput

The incoming message passed to your component.

PropertyTypeDescription
BodystringThe message body. Always a string, never null at runtime (defaults to empty string).
PipelineComponentOutput

The result returned by your component after processing.

PropertyTypeDescription
SuccessboolWhether the component executed successfully. When false, the pipeline stops immediately.
MessagesIList<PipelineMessage>One or more outgoing messages. Return multiple items for fan-out, or an empty list to suppress/filter.
VariablesIDictionary<string, string>Assignments written back to Art2link ESB Variables on the current message’s context. Keys are variable names; values replace whatever the variable currently holds. Visible to downstream components, maps, and ports via {{Variable.Name}}.
ErrorMessagestring?A human-readable error description. Set when Success is false.
ExceptionException?The caught exception, if any. Set when Success is false.
PipelineMessage

An individual outgoing message.

PropertyTypeDescription
BodystringThe outgoing message body.
MessageTypestringThe Message Type assigned to this message. Setting it classifies the message before publish; ignored on outbound pipelines (the type is already known by then). Must match a Message Type defined in the application.
PipelineComponentBase<TConfig>

The abstract base class your component must inherit from. It handles JSON deserialization of the configuration and delegates to your ExecuteAsync implementation.

How config deserialization works: The pipeline engine passes configuration as a raw JSON string. The base class deserializes it into your TConfig type using System.Text.Json with case-insensitive property matching. If the JSON is empty or null, a default instance of TConfig is created using its parameterless constructor.
C#Base class signature
public abstract class PipelineComponentBase<TConfig>
    : IPipelineComponent where TConfig : new()
{
    public abstract string Name { get; }

    // Called by the engine — deserializes configJson, then delegates below
    public Task<PipelineComponentOutput> ExecuteAsync(
        PipelineComponentInput input,
        string configJson,
        CancellationToken cancellationToken = default) { ... }

    // Implement this method with your logic
    protected abstract Task<PipelineComponentOutput> ExecuteAsync(
        PipelineComponentInput input,
        TConfig config,
        CancellationToken cancellationToken);
}

The configuration class defines all user-editable parameters for your component. Each public property becomes an input field in the Art2link ESB UI when the component is added to a pipeline.

Supported Property Types

Any type that is JSON-deserializable by System.Text.Json is supported: string, int, bool, double, enum, List<T>, and complex objects.

Default Values

Assign defaults directly on properties. These values are shown pre-filled in the UI when a user configures your component.

WHERE YOUR PROPERTY’S VALUE CAN BE SET — WEAKEST → STRONGEST TIER 1 — COMPONENT DEFAULT In your C# code Assigned on the property: public int MaxRetries { get; set; } = 3; TIER 2 — PIPELINE DEFAULT Set in the UI When the component is added to a pipeline. Inherited by every port that references that pipeline. MaxRetries = 5 TIER 3 — PORT OVERRIDE Wins at runtime Edited on the port that uses the pipeline. Scoped to that one port — everything else still sees Tier 2. MaxRetries = 10 Your job is to set sensible Tier 1 values. The operator wires Tier 2 and Tier 3 — see Pipelines — Defaults.
Validation

You may use attributes from the System.ComponentModel.DataAnnotations namespace to enforce constraints on configuration values.

C#Example config with validation
using System.ComponentModel.DataAnnotations;

public sealed class MyComponentConfig
{
    [Required]
    public string TargetEndpoint { get; set; } = string.Empty;

    [Range(1, 10)]
    public int MaxRetries { get; set; } = 3;

    public bool IncludeHeaders { get; set; } = true;
}
Runtime Binding with {{ }}

Configuration values support the {{ }} binding syntax, letting users reference application Variables, application Constants, and message-level promoted properties when configuring a component in the UI:

TokenResolves to
{{Variable.Name}}Per-message variable value — lives for the duration of one message’s journey.
{{Constant.Name}}Application constant for the current deployment — one value per environment.
{{Promoted.MessageType.Name}}Schema-promoted property lifted from the message body.
How binding works: Binding expressions are resolved at runtime before your component executes. Your code receives the final resolved values — it never sees the {{ }} tokens. This is configured post-deployment through the Art2link ESB UI, not in your code.

Each component must override the Name property with a unique identifier. This name is used internally by the pipeline engine to locate and invoke your component.

RuleDetails
UniquenessMust be unique system-wide across all components in the Art2link ESB instance.
PrefixNo required prefix. The fn convention (e.g., fnMyComponent) is optional.
RestrictionsNo enforced casing, character, or length restrictions.
Config class namingNo enforced naming convention between the config class and the component class.
⚠️
Changing the Name after deployment will break any pipeline that references this component. Treat it as an immutable identifier once in use. If you need to rename, create a new component and migrate pipelines manually.

Components must not throw exceptions. Instead, catch all errors and return a PipelineComponentOutput with Success = false, a descriptive ErrorMessage, and optionally the caught Exception.

When a component returns Success = false, the pipeline stops execution immediately. No subsequent components will be invoked.

C#Error handling pattern
protected override Task<PipelineComponentOutput> ExecuteAsync(
    PipelineComponentInput input,
    MyComponentConfig config,
    CancellationToken cancellationToken)
{
    try
    {
        // Your logic here
        return Task.FromResult(new PipelineComponentOutput
        {
            Success = true,
            Messages = [new PipelineMessage { Body = result }]
        });
    }
    catch (Exception ex)
    {
        return Task.FromResult(new PipelineComponentOutput
        {
            Success = false,
            ErrorMessage = ex.Message,
            Exception = ex
        });
    }
}

The shape of the Messages list governs what happens next. Two patterns matter:

FAN-OUT — ONE IN, MANY OUT Input 1 message Component splits partA partB partC NEXT COMPONENT RUNS ONCE PER ITEM SUPPRESSION — ONE IN, ZERO OUT Input 1 message Component filters out EMPTY LIST — NEXT COMPONENT GETS NOTHING RETURN SHAPE IN C# Messages = [ new PipelineMessage { Body = partA }, new PipelineMessage { Body = partB }, // … etc Messages = [] — message is suppressed // Success still true — pipeline doesn't halt
i
Suppression is not failure. An empty Messages list with Success = true means "the message has been deliberately filtered." The pipeline continues to the next component, but with nothing to process. Setting Success = false instead would halt the pipeline — see Handling Errors.

Beyond returning a transformed body, a component has two side-effect channels into the surrounding integration. Both are written into the result your component returns.

TWO SIDE-EFFECT CHANNELS CHANNEL 1 — ON PipelineComponentOutput Variables — write back to ESB Variables = { ["lastDoc"]="INV-1001" ESB Variable {{Variable.lastDoc}} Keys are application Variable names; values replace whatever the variable holds for this message. Downstream components, maps, and ports see the new value via {{Variable.Name}}. CHANNEL 2 — ON EACH PipelineMessage MessageType — classify the message MessageType = "Invoice" PUBLISHED AS type: Invoice Inbound only. Sets the Message Type before publish; must match a type defined in the application. Outbound pipelines already know the type — the field is ignored.

Both channels are populated at the same time you build the result. Here is an inbound component that produces two outgoing messages, classifies each, and writes one application variable along the way:

C#Variables + MessageType in one return
protected override Task<PipelineComponentOutput> ExecuteAsync(
    PipelineComponentInput input,
    MyComponentConfig config,
    CancellationToken cancellationToken)
{
    try
    {
        // Your logic here
        return Task.FromResult(new PipelineComponentOutput
        {
            Success = true,
            Variables =
            {
                ["lastProcessedDocument"] = "INV-1001",
            },
            Messages =
            [
                new PipelineMessage
                {
                    Body = acknowledgementResult,
                    MessageType = "AcknowledgementMessageType"
                },
                new PipelineMessage
                {
                    Body = businessResult,
                    MessageType = "BusinessMessageType"
                }
            ]
        });
    }
    catch (Exception ex)
    {
        return Task.FromResult(new PipelineComponentOutput
        {
            Success = false,
            ErrorMessage = ex.Message,
            Exception = ex
        });
    }
}
i
Classifying to a Message Type that does not exist in the application is an error. The string must match a Message Type defined in the same Application as the pipeline. See Message types for the rules; the resolution paths (component classifies / fallback applies / port errors) are diagrammed in Pipelines — Components can classify the Message Type.

AspectDetails
Target framework.NET 8
Available librariesBuilt-in .NET libraries. Additional NuGet packages can be added via the Art2link ESB UI (name and version), then referenced with using statements at the top of your component code. See the NuGet packages article for the global catalogue.
Threading modelSingle-threaded per invocation. You do not need to write thread-safe code.
CancellationTokenHonor the token passed to ExecuteAsync for any long-running or awaitable work.
Async I/OMaking HTTP calls, file access, or database queries inside ExecuteAsync is not blocked, but it is not a supported scenario. Behavior may change in future releases.
Timeouts / MemoryNot enforced at this time.
LoggingNo logging interface is available at this time. Console.Write output is not captured.
Component per fileOne component per file.

When you save a component, Art2link ESB compiles and deploys it automatically. There is no manual build step and no restart required. The new version takes effect immediately.

HOT RELOAD — SAVE-TO-LIVE PATH STEP 1 Save the component Ctrl/Cmd+S STEP 2 — AUTO Art2link compiles no build step, no restart STEP 3 — SYSTEM-WIDE Propagates everywhere every pipeline picks it up STEP 4 Next message runs new code REACHES… All pipelines across every App Ports that use them In-flight msgs? unpredictable — see warning below
⚠️
In-flight message behavior. Saving a component overwrites the previous version immediately. If a message is currently being processed by the pipeline, the outcome depends on which step the pipeline has reached at the moment of the save. The result is unpredictable on a case-by-case basis. Deploy component changes during periods of no message activity to avoid inconsistent behavior.
Version management: Previous versions are retained. Multi-version management including rollback and side-by-side comparison is planned for a future release.

Always catch exceptions

Wrap your entire ExecuteAsync body in a try/catch. Return Success = false with a meaningful ErrorMessage. Never let an exception propagate to the engine.

Choose stable Names

The Name property is an immutable contract once deployed. Use a descriptive, stable identifier. If you need to rename, create a new component and migrate pipelines manually.

Keep components focused

Each component should do one thing well. Compose complex transformations by chaining multiple components in a pipeline rather than building monolithic components.

Use configuration for variability

Move environment-specific or deployment-specific values into the config class. Combined with {{ }} binding to Constants and Variables, this makes your components reusable across environments without code changes.

Honor the CancellationToken

If your component performs any awaitable work, pass the cancellationToken through to those calls. This ensures the pipeline engine can shut down gracefully.

Deploy during quiet periods

Until multi-version management is available, save component changes when no messages are actively being processed to avoid unpredictable in-flight behavior.

Ready to build

Download the component template, implement your logic, and save. Art2link ESB handles the rest. If you run into issues, our support team is ready to help.