Skip to content

Using Subflows to organize tasks

rosaliafx edited this page Dec 3, 2015 · 9 revisions

Using subflows is a way to organize complicated build processes by splitting it into smaller parts. Subflow is a class inherited from Rosalia.Core.Api.Subflow, it has RegisterTasks and all the rest registration methods just like Workflow.

There are three major reasons why Subflows may be useful:

1. Reduce amount of tasks in the workflow

Here we can see N + M tasks that logically belong to two groups:

using Rosalia.Core.Api;

public class DefaultWorkflow : Workflow 
{
    protected override void RegisterTasks()
    {
        var fooTask1 = Task(/*...*/);
        var fooTask2 = Task(/*...*/);
        /* ... */
        var fooTaskN = Task(/*...*/);

        var barTask1 = Task(/*...*/);
        var barTask2 = Task(/*...*/);
        /* ... */
        var barTaskM = Task(/*...*/);
    }
}

We can introduce two sublfows to simplify the process:

using Rosalia.Core.Api;

public class Foo: Subflow
{
    protected override void RegisterTasks()
    {
        var fooTask1 = Task(/*...*/);
        var fooTask2 = Task(/*...*/);
        /* ... */
        var fooTaskN = Task(/*...*/);
    }
}

public class Bar: Subflow
{
    protected override void RegisterTasks()
    {
        var barTask1 = Task(/*...*/);
        var barTask2 = Task(/*...*/);
        /* ... */
        var barTaskM = Task(/*...*/);
    }
}

public class DefaultWorkflow : Workflow 
{
    protected override void RegisterTasks()
    {
        var foo = Task("foo", new Foo());
        var bar = Task("bar", new Bar());
    }
}

2. Reduce dependencies duplication

Assume we have a workflow like this:

using Rosalia.Core.Api;

public class DefaultWorkflow : Workflow 
{
    protected override void RegisterTasks()
    {
        var parent1 = Task(/*...*/);
        var parent2 = Task(/*...*/);
        
        var bazTask1 = Task(
            "bazTask1",
            from parent1Value in parent1
            from parent2Value in parent2
            select new BazTask(parent1Value, parent2Value).AsTask());

        var bazTask2 = Task(
            "bazTask2",
            from parent1Value in parent1
            from parent2Value in parent2
            select new BazTask(parent1Value, parent2Value).AsTask());

        /* ... */

        var bazTaskK = Task(
            "bazTaskK",
            from parent1Value in parent1
            from parent2Value in parent2
            select new BazTask(parent1Value, parent2Value).AsTask());
    }
}

Here we have K tasks and all of them depends on parent1 and parent2 tasks. It is kind of annoying to write LINQ code for every of these tasks, so we can extract all baz tasks to a separate Subflow:

using Rosalia.Core.Api;

public class Baz : Subflow
{
    private Something _parent1Value;
    private Something _parent2Value;
    
    public Baz(Something parent1Value, Something parent2Value)
    {
        _parent1Value = parent1Value;
        _parent2Value = parent2Value;
    }

    protected override void RegisterTasks()
    {
        var bazTask1 = Task(
            "bazTask1"
            new BazTask(_parent1Value, _parent2Value);

        var bazTask2 = Task(
            "bazTask2"
            new BazTask(_parent1Value, _parent2Value);

        /* ... */

        var bazTaskK = Task(
            "bazTaskK"
            new BazTask(_parent1Value, _parent2Value);
    }
}

public class DefaultWorkflow : Workflow 
{
    protected override void RegisterTasks()
    {
        var parent1 = Task(/*...*/);
        var parent2 = Task(/*...*/);
        
        var bazTasks = Task(
            "baz",
            from parent1Value in parent1
            from parent2Value in parent2
            select new Baz(parent1Value, parent2Value).AsSubflow());
    }
}

3. Reuse a block of tasks

Baz subflow from the previous example can be added to the main workflow multiple times with different arguments:

public class DefaultWorkflow : Workflow 
{
    protected override void RegisterTasks()
    {
        var parent1 = Task(/*...*/);
        var parent2 = Task(/*...*/);
        var parent3 = Task(/*...*/);
        var parent4 = Task(/*...*/);
        
        var bazTasks1 = Task(
            "baz",
            from parent1Value in parent1
            from parent2Value in parent2
            select new Baz(parent1Value, parent2Value).AsSubflow());

        var bazTasks2 = Task(
            "baz",
            from parent3Value in parent3
            from parent4Value in parent4
            select new Baz(parent3Value, parent4Value).AsSubflow());
    }
}

In this way we reused tasks from Baz subflow providing different input parameters.

Subflows and LINQ

If your subflow depends on previous tasks and you have to use LINQ comprehensions syntax, it is necessary to use AsSubflow() extension to tell compiler that your object is a subflow and not ITaskResult<Subflow>:

Task(
    "Generate installation packages",
                
    from data in initTask
    from version in formatVersionTask
    // make sure AsSubflow() is called
    select new CreateInstallationPackages(data, version).AsSubflow(), 

    Default(),
    DependsOn(buildSolution));