Software as a Service


I have been asked lately about my feelings on SaaS, or Software as a Service. To be perfectly honest, I do not think about it much, I just do it. But, that is typical once one has made a paradigm shift in thinking. But, I contemplated and thought of an example that might fit. Hope this helps.
 

The Monolith

In general, many applications start as a bit of a monolith. This is certainly true in the thinking stage, where the appliction is a series of tasks that have to be performed. And, it is definitely true when viewed  from the perspective of an end user. It is also very true with junior programmers.

One way we avoid the monolith is to start refactoring as soon as we see "code smells". The most common one being repetitive code (same algorithm in two or more places in your code). At first, we move the code into its own routine in the same class. Then, we move it to its own library, for reusability. Over time, we begin to look at our applications differently and we start to break them out, and creating libraries becomes "second nature".

If we take this one step further, and move it into its own application, and and add a "service boundary", we have SaaS. We have truly separated out a portion of our software as a service for other portions, or apps. There is an internal fight when we do this, at least normally, as most programmers are focused on performance. This fight is further compounded by a severe misunderstanding of SOA and how it relates to SaaS.

SaaS will remove some performance, as you have added a boundary. When you have a boundary, you end up "marshalling" data across the boundary as you work, which costs cycles. While the app does "run slower", it gives you a lot more options. In short, yes, we are trading some performance, but this trade off gives us more flexibility and greater reusability. It also increases our scalability and often our extensibility (if for not other reason than we can create additional service boundaries and use factory methods).

I am currently dealing with a group that has developed two applications for a single project. One was supposed to be a local application and the other a server application. They delivered both as server applications. And, while there is a web service call, it is simply there because they did not know enough about .NET to use Flex-.NET remoting calls. When I outlined the system, using a local call to a service that called a server side service, this was the answer I got back (obfuscated message to avoid getting into project specifics):

On the other hand, analyzing your proposed solution for the {project} we have the following items:

  • We can see that the system could have a series of inconveniences which could result in it not function[ing] properly
  • All of the synchronizations will begin to slow down the system.

There are some valid concerns in the entire email, but the focus of the developer, as I continued this conversation on IM, was that the use of a web service boundary will slow down the application and an Internet failure will create problems on the local system. For that reason, it is suggested that the application be a single monolith on the server. Please note I am not picking on this developer, as I find this thinking is very common.

For the rest of this post, I am going to picture an application that pulls packets of some sort off the wire and performs three steps:

  1. Pulls raw packets off the wire (the specifics are not necessary for understanding, so it coudl be push or pull)
  2. Parses the raw packets into information (makes a message)
  3. Gets additional information out of the message using other software (this could actually be multiple processes)

As a monolith, the application might be compiled as a console application run on the server, forcing the user to keep the box logged in. While that might sound funny, I do have one of those right now, as delivered by a vendor. It will have to become a windows service eventually, of course, but it works for now.

As we go through this post, this is the model we are looking at:

Process1

and here is how it looks as a Monolith:

Process2

Our first step towards SAAS is dividing this out into its own methods. And then dividing those methods into classes, which we will hit with the pipeline model.

The Pipeline

Our first move out of the monolith is to segment into sections. The application still functions as a monolith, but work is done by distinct components. We now have steps in the process defined in their own classes. So the model looks more like this:

 Process3

Now, the main application is a pipeline, calling different pieces in order to get the message. If this is a push operation, the start of the process is triggered by an incoming message, which slightly changes the diagram above, but you can see how the pipeline goes vertically down the left and other classes are called in order. With WF, you can actually make this process quite complex, so it is no longer a simple pipeline, but that is a topic for another day.

This is a bit of an aside, thrown in for good measure, as it is a preferable way to handle things. The diagram already shows the use of interfaces, so let’s delve into why this is important, for those who might not be familiar with software built on interfaces.

If we are lucky (using the word luck meaning we prepared correctly), we also have factory methods set up at certain points so we can switch out software (as long as it adheres to a specific interface). As an example of flexibility with factory methods, lets examine the processor. If one of my steps in the pipeline was processing, as we have in the pix, geocoordinates (lat/long), I would want to create an interface:

public interface IProcessor
{
   
Message ProcessMessage(string message);
}

This would allow me to set up multiple classes that adhere to this interface. Note that I might also use a base class, but it is not necessary for this example. Below are some classes

public class SimpleProcessor : IProcessor
{
   
public Message ProcessMessage(string message)
    {
       
throw new System.NotImplementedException();
    }
}

public class SimpleProcessor : IProcessor
{
   
public Message ProcessMessage(string message)
    {
       
throw new System.NotImplementedException();
    }
}

This would be illustrated as follows (very crude and quick UML):

Process4

With this, we have made the software more configurable, as we can configure it to different implementations of each of the steps, but we are still dealing with a monolith. It may be a configurable monolith, but it is still a monolith.

Adding Service Boundaries

The most common way of adding services is to leave the pipeline alone and add service boundaries on the "side pipes". This means we wrap pieces like the Processor in services (I am not showing the factory methods here). The picture looks like this.

Process5

But notice that there is still a single point of failure in the main pipe. We also end up adding a lot of error checking code off of the pipe to ensure things are moving properly. And, there is no way to easily queue up in case of network failure. As this is likely to be on our local network, at least for now, this is not a major concern, but it is something we have to consider.

Inversion

I am a bit loathe to use this particular word here, as one might think of Inversion of Control. And, while it could fit, that is not what I am focused on here. I am talking about inverting the pictured model and seeing other places where we can break up the work. In the pipeline, we broke off services as part of the pipe, but we can also break our application into smaller applications by separating the steps. The picture now looks like this (greatly simplified, of course):

Process6

We already had the ability to separate the "services" and put them on multiple boxes, so it may not seem like we have much of an improvement here. But consider these examples:

Examples:

  • User sign up – should be able to pick up in the middle, pulling from partially filled tables or temporary tables
  • Shopping cart – Saved to shopping cart until transaction is complete, where it becomes an order
  • Almost any workflow

We simply need to add the ability to save off at each service, which is a fairly simple addition. One benefit already realized in the above diagram is the ability to separate the processes on multiple boxes, or even farms. That is a huge improvement in scalability.

But what if we need the ability to either call different processes, based on the message type, or to call processes only when they are not overloaded. As an example, we need the user information from the raw message immediately, but we do not have to finish all user processing before that information is useful to someone. We might opt for a model like this (crude drawing warning):

Process7

The model will not work for every single application, of course, as you have to have the ability to save off a message and work on it later. But, many applications are like this, benefitting from the message before the entire "workflow" is complete. And, if the processor has lag, the ability to immediately benefit from having the partially processed message now may be monumental. I actually have an application in mind when I envision this architecture, so I am a bit giddy about the ramifications. Disappointed

Summary

There are three takeaways from this post. First, that a rudimentary understanding of refactoring and separating concerns is the first key to understanding SAAS. The second is that you are already doing this, you are simply thinking in terms of class boundaries and not service boundaries; it should be easy to find the boundaries that can become services and just do it. Finally, that you can skin the cat in a variety of ways to increase your scalability. Most things that allows us to move work off to another machine increase our scale.

Now, some may have a difficulty in what can become a service. My suggestion is to think of the pieces of code you are writing and examine which could be used by more than just your application. You then think of whether placing a service boundary there makes sense. If you have no candidates for services in your application, you are either working very small (utility applications) or you do not have your work divided out into a proper class structure. In the former, you are fine; do not separate out small utlitity apps until you find something that has value as reusable software. In the later, you may need to rethink your processes and architecture and retool the classes so you can divide out.

One complaint I have heard to SAAS is it slows things down (already mentioned). And, this is true. Even if you place the services on a single box, placing web service calls is more expensive than in process calls. But, it is not so expensive that I would not consider it if you have an application that may need to scale across multiple machines in the future. With WCF, the calls do not add that much weight; if you are not a google (who is?), the overhead of a few microseconds per call is unlikely to slow down your application that much.

One question that comes up, especially when we think WCF is whether or not you shoudl use SOAP or binary transport. MY suggestion is to ask this question: "Do I ever see this functionality being used by a client?" If the answer is yes, you are better to go ahead and accept the slightly higher "lag" that comes with choosing SOAP over binary, as the interoperability is the key. You can, of course, change the transport in the future, if you so desire, or even set up the ability to use either, through a factory method … but that is a topic for another post. Open-mouthed

Peace and Grace,
Greg

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: