The World’s Leading Microsoft .NET Magazine
   
 
The .NET Addict's Blog

My Top Tags

                                                           

My RSS Feeds








Latest Diggs - Programming

Internet Blogs - Blog Top Sites

Site Hits

Total: 2,551,611
since: 19 Jan 2005

Orchestrating a WPF App using MVC and Windows Workflow Foundation

posted Tue 03 Apr 07

I was recently working on a project where I was building an application that was essentially taking control flow direction from multiple locations. In short, it was connected to a network server and received messages indicating certain events taking place either on the server or by other connected clients. In addition, the application has a rich WPF GUI, which means the user is pushing buttons and clicking everywhere.

After a while of building this application up so that it had some decently testable functionality, I began to notice some really big problems in design. Firstly, the application was chock full of boolean flags and enumerated flags indicating mini-states. Basically my application was full of switch statements containing really convoluted branching logic. In other words, I had logic that basically looked like this:

switch (statusFlag) { .. flag1: if (state == state1) dothis(); else if (state == state2) doThat(); break; flag2:...}

It was completely hideous. What was going on was that I was doing a poor-man's state machine. I was maintaining states and sub-states as well as the rules governing the transitions between states in integer and boolean flags, and here's the kicker: even though I wrote the application it was nearly impossible to deduce the control flow from the code. This is where Windows Workflow Foundation came in like Mighty Mouse to save the day (I really did feel like my app was tied to some railroad tracks about ready to die...)

So then this became my goal: Create an overarching state machine that governs the actions of my application, and use sub-workflows for when the application is in discrete sub-states that have their own workflow logic. The requirements for this new design were:

 

  • It must be immediately obvious from looking at the workflow designer screens exactly how the application behaves in any given situation.
  • It must be extremely agile. In other words, I must be able to add states, transitions, and conditional logic to the workflows without having to re-plumb the models or the views
  • The application must adhere to the M-V-C principles. If not, Kevin must be burned alive at the stake and sacrificed to a nearby hungry dragon (with no Paladins nearby to save me)
So here's the architecture that I came up with: 

 

WF Orchestration 

Basically here's how it works. The workflow in this architecture is really something like a super-controller. It is the ultimate authority on what happens in the application. The workflow tells the "outside world" (our application) about important things that need to take place and the application tells the workflow about important events that have taken place. In order to keep things clear, I typically create a single DEX class but I separate the interfaces into "In" and "Out" interfaces to really make it easy to tell what's going on. For example, if I had a DEX that was used for communicating with the network server, I might create two interfaces called INetworkServerDataExchangeIn and INetworkServerDataExchangeOut both implemented in a single class called NetworkServerDataExchangeService.

The controller in the diagram is a mini-controller responsible for small things. In the case of WPF, there will more than likely be a mini-controller for each WPF window instance running (except subservient dialogs, which are handled by the parent window/controller). What's interesting to note here is that the DEX is able to modify the model to which the view is bound. This is because, at least in my application, there is a truckload of network traffic taking place, and if I have to bother the WF runtime every time a single event takes place (in one state, I can have as many as a thousand network events every minute) then I'm wasting resources. My rule of thumb was that if the workflow doesn't actually need to react (business logic progression) to the network event, then I'm safe modifying the model right from the DEX. For example, if I have a window on screen and I get a network event indicating that, say, my ping time has gone from 120 to 119, I can set that value in the model and be done with it. If, however, the ping time goes to 0, I need to tell the workflow about it so it can decide what to do and move the state of the application accordingly.

Let's take a sample scenario and walk through it using the architecture diagram above. The application starts up, and it does nothing. It doesn't contain any code that launches additional dialogs or anything else. All it does is launch the main state machine workflow and then takes a back seat. It rigs up the DEX so that it can listen to events fired by the DEX that indicate the workflow has called external methods, but that's about it. In an application that does a network login, the sequence might go something like this:

 

  1. Workflow drops into the default start state. The initialization code in that state sends the workflow into the Authenticating state. 
  2. The initialization code of the authentication state does a CallExternalMethod call to PromptForCredentials method on the INetworkClientDataExchangeOut interface, which is handled by the DEX class that was attached at runtime during client startup.
  3. An event handler listening to the event OnPromptedForCredentials hosted by the DEX is fired, and the dispatcher is used to forward a method call to the main GUI thread using BeginInvoke to launch the credentials dialog.
  4. When the user presses "OK" on the credentials dialog, the main GUI sends the credentials to the workflow via a method called NotifyCredentialsSupplied on the DEX, which fires the event OnCredentialsSupplied, which is part of the INetworkClientDataExchangeIn interface. 
  5. That event (OnCredentialsSupplied) is handled in an EventDriven activity within the Authenticating state. The workflow responds to this event by telling the DEX it would like to send those credentials to the network server for validation.
  6. When the DEX (which is also listening to the network server) receives a message from the network server indicating valid credentials, it tells the workflow that the credentials were accepted via the OnCredentialsValidated event in the INetworkClientDataExchangeIn interface.
  7. The workflow sees that event through an EventDriven activity (which contains a HandleExternalEvent activity) and transitions happily to the LoggedIn state.
  8. If the credentials were denied, the workflow would have responded accordingly when it received the OnCredentialsRejected event in the INetworkClientDataExchangeIn interface (now do you see why it was important to spit the in/out interfaces?)

You might be thinking at this point, "Holy crap! That's a truckload of added complexity! I could've done all of that in a single code-behind .xaml.cs file and been done three weeks ago!!!"

Well, that's correct..and incorrect. Let's analyze the complexity here:

The initial ramp-up time for coding is significantly more than just pulling the code out of your... pants. In addition, my estimation is that to get the application to a point where you can hit F5 and watch it log in takes almost four times as long as if you had written the code linearly in a 'dumb' code-behind class. 

However. If you code it using the workflow, you get some huge benefits:

 

  • The entire flow of business logic, UI control, and process evaluation is visible not only to the developers themselves, but to the designers and QA people on your team. Anybody can print out the workflows or look at them within Visual Studio itself. This becomes a tremendous advantage to complex applications, especially applications with things like "negotiation phases" between remote and local parties where a complex series of events can trigger any number of mini-states.
  • If you code it right, your application will be pausable and resumable. Not only that, but you can take advantage of workflow tracking and workflow monitoring. You immediately gain access to "free" storage of every single state change in your workflow including the event that triggered the change and the data, who triggered the event, and when.
  • Agility. If your application is designed and built around the notion that the workflow is the ultimate authority, then you can quickly and easily add states to the workflow, shuffle states around, change which events trigger which states, and so on. The next time you're knee deep in a huge, complex application and someone comes to you and says "You know, I think we should split that process into a 4-window wizard instead of a single-stage window." remember this: workflow. Which would you rather do: Yank out all your plumbing and re-write the entire app, or drop in a few more states and event responders in the workflow and quickly prototype up the additional WPF screens required?
Anyway, I think I've rambled on long enough. The moral of this blog post is this: If done properly, with care, caution, and adherance to really good design principles (such as separation of concerns, MVC, factory, singleton, etc) then you should be able to reap some huge benefits in your application by making the GUI subservient to your workflow. 

 

 

tags:                  

links: digg this    del.icio.us    technorati    reddit




1. Kevin Hoffman left...
Tue 03 Apr 07 12:05 pm

In case anybody is wondering, I tried and experiment and used my Mac instead of Visio for the diagram. I used a demo of OmniGraffle to make the illustration and I was immensely pleased with the results.


2. Leif left...
Tue 03 Apr 07 1:13 pm

"Create an overarching state machine that governs the actions of my application, and use sub-workflows for when the application is in discrete sub-states that have their own workflow logic."

Isn't that what BPEL (and similar languages / engines) do? I'm not trying to be a smart-ass, I'm *really* wondering.


3. Kevin Hoffman left...
Tue 03 Apr 07 1:44 pm

BPEL is more of a back-end situation. I realize that there are, in fact, BPEL templates for Windows Workflow Foundation, but I found those to be actually more than what I need. What I am doing here is orchestrating my GUI, not a huge enterprise-wide business process, so I decided that I was better served by using straight unmodified WF rather than the BPEL WF activities that you can now download from Microsoft.


4. Domain insider left...
Tue 03 Apr 07 11:16 pm :: http://www.domaininform.net

I think that by using this type of work flow. it will make things easier for people to work!


5. craiggy left...
Thu 12 Jul 07 1:44 am :: http://www.webandflo.com

In relation to leif's comment BPEL is more back-end. There is a BPEL for people out there but I have never used it.


6. Casey left...
Tue 29 Apr 08 6:53 pm

Care to post a simple sample appllication project?


Tag Related Posts

Building Model Classes in C# and Cocoa

Sun 15 Jun 08 3:13 P GMT-05
tags:            

NYC SharePoint Developer Needed

Mon 12 May 08 12:09 P GMT-05

CLINQ v1.1.0.0 Released!

Fri 02 May 08 5:38 P GMT-05
tags:          

The iPhone SDK key has been leaked! Oh Noez!!!1

Tue 29 Jan 08 11:36 A GMT-05
tags:        

CLINQ v1 Demo - Network Message Filtering

Wed 09 Jan 08 7:47 P GMT-05
tags:        

Building a Ledger Style for WPF Grids

Tue 21 Aug 07 3:30 P GMT-05
tags:    

The dreaded language bleed-over has begun

Tue 19 Jun 07 6:23 P GMT-05
tags:        

My first "Acropolis" Application

Mon 04 Jun 07 1:40 P GMT-05
tags:      

Exploring the Delegate Design Pattern

Mon 14 May 07 6:30 P GMT-05

Core Data - Almost too Easy?

Wed 18 Apr 07 2:23 P GMT-05

Will Silverlight be DOA?

Mon 16 Apr 07 8:02 P GMT-05

Exploring the MVC Pattern in WPF

Tue 10 Apr 07 12:51 P GMT-05
tags:                      

My Little Pony .NET Unleashed 2007

Fri 30 Mar 07 1:59 P GMT-05

An experience with the Leopard beta

Mon 26 Mar 07 7:45 P GMT-05
tags:                

Authorness

Thu 15 Mar 07 1:44 P GMT-05

WPF Bindings == WTF Bindings?

Mon 12 Mar 07 6:31 P GMT-05

On MUDs

Thu 08 Mar 07 5:00 A GMT-05
tags:                    

Cocoa Programming vs. WPF : NIB vs XAML

Tue 20 Feb 07 2:09 P GMT-05

Cocoa Bindings vs. WPF Binding

Thu 15 Feb 07 5:41 P GMT-05
tags:                

Ulysses Agenda Makes Redmond Developer News

Wed 29 Nov 06 7:10 P GMT-05
tags:                

Is the continuous beta the new model for Vista?

Tue 21 Nov 06 8:51 P GMT-05
tags:              

Ulysses Agenda - Network Engine Test 1

Mon 09 Oct 06 2:26 A GMT-05
tags:              

Ulysses Agenda : First Cut Networking Design

Thu 14 Sep 06 12:46 A GMT-05
tags: