Showing posts with label Patterns. Show all posts
Showing posts with label Patterns. Show all posts

Thursday, November 08, 2007

Workflow Foundation Usage Patterns. Part II.

Before I continue describing WF usage patterns, small personal announcement: I'm changing this blog's title from "Thoughts on Architecture" to simply "Technical Blog". This seems to be more prudent, since I am not always discussing software architecture and am not even an architect anymore (my new job title is development manager, but enough about me).

Workflow In Business Layer

When we follow the principles of layered architecture, we usually end up with two types of objects in the business layer: entities and workflows. Entities, of course, map more or less directly to domain entities - products, orders, claims, or what have you. Workflows, on the other hand, manipulate entities in order to implement specific business process. For example, business process of registering new customer may include creation of Client, Address, and CreditCard entities, validation of the last two, transmission of a welcome email, etc. Of course, there are many ways to skin the cat, but typically you would employ some sort of business workflow object.

What attracted me to implement business workflows using WF was the ability to compose workflows from custom activities. We often had a situation where the same process had to be customized for different partners. In the example above, a customer may belong to a partner which does not require credit card validation. Engineers used to create new version of business workflow by copy-pasting relevant method in the class and tweaking it. My idea was to create a set of basic WF activities that represent pieces of the process, then combine them together using WF designer rather than C#.

Contrary to the pattern I described in the previous post, this one doesn't represent a long-running process and workflow instances are never persisted to database. Another side benefit is simplified maintenace due to improved readability: it's much easier to understand the logic by looking at workflow designer than raw code. Workflows become, in a sense, a live technical documentation of the system, on the par with unit tests and Visual Studio class diagrams.

There are a couple of caveats with this approach. First of all, beware of performance penalty from using WF and be sure to stress-test your system. In my case, business workflow executed as part of a batch process, not web application, so I didn't have to worry about user experience. Second, keep in mind that WF world is very asynchronous, so you can't really treat the workflow like just another procedure call. You create workflow instance, assign input parameters and start it. If you want to get the results back from that instance, you need to do extra work with the runtime.

Wednesday, November 07, 2007

Workflow Foundation Usage Patterns. Part I.

Today during Patterns and Practices Summit Ted Neward gave a presentation on Windows Workflow Foundation. One of the points he made is that although "Workflow" as a programming concept is fairly old (arguably older than object-oriented programming), WF as a product has been out for less than a year. Thus, it is premature to talk about best practices and proven patterns.

I agree with Ted, and in this post I'd like to share my experience designing real-life systems that take advantange of WF. As an architect, I had to choose the niche for this shiny new technology. It wasn't an easy task, because WF represents extremely flexible and universal concept. On the other hand, it is a new technology, so I wanted to minimize the risk and not use WF as a cornerstone of the system. Instead, it should be pluggable, something that's easy to turn off.

Job Orchestration

Imagine an application that prepares and sends a file to several business partners, then waits for response files and processes them. Even assuming fully automated file upload/download, the whole thing can take hours or days. My original approach was to write separate jobs that handle individual tasks, such as preparing request file, processing response file, upload, and download. It worked, but it was difficult to reconstruct the business process from individual job results. In other words, we know that a job has executed successfully, but business users wanted to know where do we stand in the larger process.

In order to solve this problem, I used WF's ability to implement long-running workflows. I represent a process with a workflow that contains the sequence of job launch activities. After a job is launched, workflow idles and is persisted to SQL data storage using SqlWorkflowPersistenceService. When job completes execution, it sends a signal to workflow runtime, which loads correct WorkflowInstance from SQL and resumes it. Note that workflow itself doesn't contain any code whatsoever and can be written in pure XAML - it represents a logical sequence of jobs.

Business users were particularly pleased to see the Visio-style diagram of the workflow which clearly showed what's completed, what's running, and what's pending. For this I used the ability of WorkflowView control to save itself as an image.

[To be continued]

Sunday, February 25, 2007

Navigation Controller Pattern

When people use the term "website navigation", they usually refer to one of the two different things. First is a set of hyperlinks displayed on a web page that users can click in order to get to a specific part of the website. These links may be presented as a drop-down menu, tree-view, or a bread crumb trail. Such navigation controls are easy to incorporate, especially with ASP.Net 2.0 where they all bind to a common data source.

Another meaning of the term is related to page flow: a sequence of pages user needs to go through in order to accomplish some process. Ordering a book from Amazon.com is a simple example: from the shopping cart screen, user has to go to the shipping details page, then billing details, review order summary, order receipt, and, finally, suggested items list. An example of a complex flow is creating a will on LegalZoom.com. In both cases, entire sequence is controlled by the application -- user cannot bypass or reorder steps. Thus, page flow is part of the application's business logic while navigation controls are part of its user interface.

Traditionally, page flow is implemented either in the code-behind class or page presenter (if the application follows the MVP pattern) as a sprinkle of Response.Redirect() calls. Although this approach is straightforward for developers, it has an important limitation: page flow logic is inseparable from the rest of the application logic. Unless the system is fully documented (and we know how often that happens, right?), it is impossible to answer a question "How many pages lead to Foo.aspx" without scanning the source code. Consequently, application becomes difficult to maintain: simple business request to change default page flow becomes a challenge for a developer, especially if he or she isn't intricately familiar with the system, and requires extensive regression testing.

Since I mentioned the subject of technical documentation, one reason it’s so unpopular is because any documentation almost immediately becomes outdated. Imagine how much easier it would have been if we had a “live” system diagram. But that is a subject for a different post…

Navigation Controller design pattern provides a better alternative. As it name implies, entire page flow navigation logic is abstracted to a new class. At the core of navigation controller are two types of page transitions: default and named. Default transition will transfer user to the next web page within the flow, while named transition represents an alternative navigation route. For instance, a page that displays credit card details will transition by default to the order summary page:

IPageFlow controller = this.NavigationController;
controller.Next();

However, if user decided to add new credit card info, control should be redirected to that page instead:

IPageFlow controller = this.NavigationController;
controller.Navigate(TransitionNames.AddNewCreditCard);

Note that the string Navigate method accepts isn’t the URL of the page, but a generic value that represents the transition. The logic within navigation controller maps transition name to actual page URL.

Essentially, navigation controller is a state machine where each state is associated with a particular web page. As all state machines, it needs to have a start and end states, and maintain current state information. From the web application point of view, every user session requires a dedicated instance of the controller class.

As we know, nothing prevents a user from typing the URL directly in their browser window or clicking “Back” button. Both these actions can potentially break the intended page flow, so another function of navigation controller is to “keep” user in:

protected override void OnLoad(EventArgs e)
{
IPageFlow controller = this.NavigationController;
controller.RedirectToCurrentPage();
base.OnLoad(e);
}

Microsoft Patterns and Practices group recently released initial version of its Web Client Software Factory. The factory includes, among other things, Page Flow Application Block, which is a versatile navigation controller. As all PnP application blocks, it is build using provider model, meaning that there may be different concrete implementations of the controller. The team actually chose Windows Workflow Foundation (WF) for their implementation. In WF, page flow is represented by a state machine workflow and can be edited in the visual designer inside VS 2005. Because WF has its own persistence model, page flow instances need not be stored in user session state and can be paused and resumed as necessary. For more information, please visit Web Client Software Factory wiki on CodePlex.

Wednesday, October 25, 2006

Dependency Injection

Let's talk about dependency injection (DI). DI is, essentially, a design pattern that can be applied to tightly coupled systems. For example, imagine that several modules of our system use a cryptographic component. The code may look like this:

public class OrderManager
{
private CCryptography _Crypto;

public OrderManager()
{
_Crypto = new CCryptography();

Obvious drawback of this approach is that we cannot easily swap cryptographic algorithms - multiple references in the code will need to be changed. Using DI pattern, we would extract the interface of CCryptography class and delegate the creation of concrete object to an outside factory:

public class OrderManager
{
private ICryptography _Crypto;

public OrderManager(ICryptography crypto)
{
_Crypto = crypto;

This way, different kinds of cryptographic objects can be created and swapped at run-time; OrderManager class doesn't know anything about it (and doesn't need to know, either). Another important benefit is testability: we can now test OrderManager functionality without a fully-functional cryptographic component. All we need is a mock object that implements ICryptography interface and probably doesn't even encrypt/decrypt.

By moving object creation to a new entity, we can address additional issues. For example, by caching objects in a dictionary we may apply Singleton pattern and ensure that only one instance of cryptography component is created. We can also control the order in which objects are created. Thus, we complemented original DI pattern with the concept of lifetime container.

Folks at Microsoft Patterns and Practices group took the idea even further and created dependency injection container called ObjectBuilder (OB). OB uses reflection to analyze classes and automatically fulfill their dependency requests. So, as long as we explicitly expressed a dependency (by placing it into a constructor as in the above example or a property decorated by a special attribute), OB will know what to do. There is much more to OB than I just mentioned, so if you'd like to read more, here are a couple of links:

- Download Object Builder from CodePlex: http://www.codeplex.com/Wiki/View.aspx?ProjectName=ObjectBuilder
- Great tutorial by Sayed Hashimi: http://www.sayedhashimi.com/PermaLink,guid,d05aed4f-a211-4969-893e-7ffea324a56c.aspx