To summarize, stateful workflows will be contained in a secondary assembly, objectflow.stateful, although I'd like to also distribute an objectflow.stateful that has objectflow.core statically linked.

Supporting common use cases

Maintaining state

To maintain state we need two things - (1) a way to exit or pause the workflow and (2) a way to have an object resume the worflow where it last left off. To complete requirement (1) we provide the Yield(object) method. To complete requirement (2) we extend the behavior or the Start(T) method. (I'm debating if it's actually a good idea to extend Start(T). Maybe it would be better to create a Continue(T) and StartOrContinue(T) and leave the behavior or Start(T) the way it works now)

State ID

The state ID is given to Yield(object) and can be any object with a meaningful .Equals(object) method. Under the hood, a single workflow will turn into several sub-workflows - everything from the beginning to the first Yield is a workflow, everything between Yield's are each a workflow, and from the last Yield to the end is a workflow.

Observing the stateful object while the workflow is not executing on it, if the state ID is null then the object is not currently in that workflow. Therefore, the actual type of StateID must support nulls, so it must be string, int?, Guid?, etc. (I'm debating if we should provide a mechanism to allow state IDs to never be null. Though, I'm not sure of the value)

Workflow ID

It is possible that a single object might want to pass through multiple workflows at the same time or in overlapping times. To support this we need to introduce a workflow identifier so that we can pull up the correct state data to resume execution. This is the reason for the GetStateId(object, object) and SetStateId(object, object) methods of the IStatefulObject interface.

The interface

This is what I have so far for the workflow interface (latest is at github). All members from IWorkflow<T> are hidden (and proxied, in a way) so that it maintains the fluent interface feel.

public interface IStatefulWorkflow<T> : IWorkflow<T>
        where T : class, IStatefulObject
{
        object WorkflowId { get; }
        IStatefulWorkflow<T> Yield(object breakPointId);
        T Continue(T data);
        bool IsMidlife(T data);

        #region overriden from base class, but returning IStatefulWorkflow
        new IStatefulWorkflow<T> Do<TOperation>() where TOperation : BasicOperation<T>;
        new IStatefulWorkflow<T> Do<TOperation>(ICheckConstraint constraint) where TOperation : BasicOperation<T>;
        new IStatefulWorkflow<T> Do(IOperation<T> operation);
        new IStatefulWorkflow<T> Do(Func<T, T> function);
        new IStatefulWorkflow<T> Do(Func<T, T> function, ICheckConstraint constraint);
        new IStatefulWorkflow<T> Do(IOperation<T> operation, ICheckConstraint constraint);
        new IStatefulWorkflow<T> Do(IWorkflow<T> workflow);
        new IStatefulWorkflow<T> Do(IWorkflow<T> workflow, ICheckConstraint constraint);
        #endregion
}

Last edited Mar 6, 2011 at 5:34 PM by tkellogg, version 1

Comments

No comments yet.