Wednesday, May 28, 2008

MVC Using Workflow (WF) as Model/Controller and ASP.NET as View

Download source code for this article
Agenda

Since workflow aims to provide an application business logic engine, it seems natural that we should be able to plug to it a presentation technology of our choice.

As usual, let's start from the requirements to our solution:
  1. Total separation between View and Workflow (Model/Controller) logic. We assume that Workflow is provided by an external team.
  2. Workflow should be developed with no assumptions on the presentation technology used.
  3. The components should be plugged by configuration.
Workflow definition

We assume it is an event driven state machine. This is how it looks for our example:

For each State we will create an interface with:

    1. Initialize() method, which is responsible for state initialization (in our case opening a form to collect data from the user).

    2. Several events that can happen in this state (user submits data).

    3. SubmitXXX methods, each of them raising a corresponding event.

    For example, for a State where we need to collect a customer name we will create a following interface:

    [ExternalDataExchange] //required for Workflow binding 
    public interface IGetNameState
    {
    event EventHandler NameReceived;

    void Initialize(Guid instanceId);
    void SubmitName(Guid instanceId, string first, string last); //raises NameReceived event.
    }

    At the end of the StateInitializationActivity we invoke the corresponding Initialize() method:


    to show the appropriate view.

    Once we are done for each State, we can provide our workflow to the Presentation team.

    Presentation

    First we must admit that the controller provided by the Workflow does not show any form. Further, it cannot do it by definition, since it does not know how! Instead it calls a relevant Initialize() method to be implemented by the concrete Controller.


    That means that we must provide a concrete Controller implementation to perform the actual navigation, i.e:

    void IGetNameState.Initialize(Guid instanceId)
    {
    HttpContext.Current.Server.Execute
    ("~/GetName.aspx?InstanceId=" + instanceId, false);
    }

    Later on we will see how we plug this Controller in by configuration.

    Now for each state the correct form is shown to the user, but his actions are not handled. Let's do it:

    protected void Button1_Click(object sender, EventArgs e)
    {
    WorkflowRuntime workflowRuntime = (WorkflowRuntime)Context.Application["WorkflowRuntime"];

    workflowRuntime.SubmitName(
    new Guid(Request.QueryString["InstanceId"]),
    txtFirstName.Text, txtLastName.Text);

    // Once we call Workflow, it's responsible to route the request
    Response.End();
    }
    Initialization and Configuration
    • Initialization:

      In the Application_Start handler we initialize the WorkflowRuntime:

      WorkflowRuntime workflowRuntime = new WorkflowRuntime("WorkflowRuntime"); //passing the configuration section name 

      workflowRuntime.StartRuntime();

      Application["WorkflowRuntime"] = workflowRuntime;

      and in Application_End we stop it:

      WorkflowRuntime workflowRuntime = (WorkflowRuntime)Application["WorkflowRuntime"];
      workflowRuntime.StopRuntime();
    • Configuration:

      Let's configure ExternalDataExchangeWorkflowService with our concrete Controller type:
      <ExternalDataExchangeWorkflowServices>
      <Services>
      <!-- The concrete Controller implementation -->
      <add type="Workflow.RuntimeServices.Controller, Controller, Version=1.0.0.0, Culture=neutral, PublicKeyToken=3a221384c77ffbe5"/>
      </Services>
      </ExternalDataExchangeWorkflowServices>
      <WorkflowRuntime>
      <Services>
      <add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConfigurationSection="ExternalDataExchangeWorkflowServices"/>
      </Services>
      </WorkflowRuntime>
      Summary

      This tutorial helps to build a skeleton implementation of ASP.NET front-end for Workflow Controller. In our design we put a special attention on separation and correct interaction between the two. A very important property is that the Workflow does not dictate the implementation to the Presentation layer, its work can be performed in one or several steps. Only when all the information required by the Workflow is collected, it is invoked. For example, to collect the user full name, consisting of first and last names, the presentation may open one or two forms; provide any validation logic it wants and only at the end Submit the information. Such approach separates the Big application flow from the Presentation, opening a possibility to customize/update/change technology/upgrade it in any possible way.



    No comments: