Analyzing application architecture patterns, just for fun...
Been thinking a lot lately about application design patterns and how people go about scaling apps. Seems to me that to understand how to scale and in order to select a tool for scaling your app, you first have to understand how your application is consumed by the user. An application can do many things (pretty much limited only by the developer's imagination since and this has been true since the inception of the computer). But, when we get into a business context, an application needs to do a few things and do them well.
Applications need to be able to scale, not lose work (be highly available), and IT needs to be able to control and manage those applications. It is almost like one could make a chart of approaches / patterns and the associated ability to deliver on these dimensions:
1. Database / stateless architecture pattern scales till the DB breaks underneath. The availability of stateless apps is coupled to the availability of the DB underneath as well. And manageability--well--the DB is one of the most detested and simultaneously revered pieces of code in the datacenter. In short, stateless works but exhibits huge trade-offs on all three dimensions
2. Message-oriented middleware scales till the network breaks or the app nodes run out of memory trying to keep track of everything each other are doing. The availability of such solutions is tightly coupled to the availability of the underlying message bus. And, operations has varying levels of quality with respect to tools, depending on the bus.
3. File-sharing scales till the file server breaks. And its availability is at least as good as that of a DB since most DB's run on top of some sort of NAS or SAN in the first place. Again, there are tools to administrate the file server, but here app manageability starts to get sketchy. In my experience, file-based architectures with some form of marshaling to-and-from storage tends to be fragile and tightly coupled to a particular object model and class definition. Change your app and lose all your state in case you thought about this up front.
4. Peer-to-peer replication. This is just a special case of messaging.
Now, as far as I am concerned, all these architectures have their place. And, the point of this discussion is to see if we can separate the good usage from the bad. So, in thinking about architecture patterns and appropriate uses of various technologies, it occurs to me that we should look in from the app at the requirements of scalability / availability, and manageability as opposed to judging these approaches in a vacuum.
This is what I came up with so far. The app tends to be built to do one of two things:
1. provide a bunch of loosely coupled services under one umbrella. Those services can be fired in any order, at any time, without regard to what the user has done before and what the user might want to do next.
2. provide a bunch of tightly coupled services. Those services are invoked in particular orders such that there are logical (maybe even fixed) paths through the application.
In the first model, we need an approach scaling out our application onto multiple nodes wherein scalability, availability, and manageability can be guaranteed across this spread out application world. Specifically, we need all nodes to be able to take over for each other. From an availability perspective we need a share-everything world whereas from a scalability perspective and manageability perspective, we want a share-nothing world. Given that we are trying to allow a bunch of scaled out nodes to do the same types of tasks yet in arbitrary ordering, we must allow availability to take priority meaning all nodes need to be ready to take over for each other on a failure.
In the second model, we have quite the opposite. Application methods / workflow stages know who their predecessors and successors are. If workflow is asynchronous and event driven, we can actually find a way to stop sharing data at all and push data and work to coincident app nodes. Sure, the application can still be scaled out across hardware nodes, but in this case we needn't push everything everywhere just in case we fail or another node gets work that was destined for us. Remember, we always know in such an architecture where work is coming from and going to so we might as well handle routing and failure by hand.
In short, there are 2 patterns that matter: shared world loosely coupled apps (first model) and partitioned world tightly couple apps with workflow (second model).
What's the point you ask? I think it is quite interesting. DB-based stateless (O/R mapped), MOM, etc. apps are not architectures. They are implementations. The architecture question you need to answer is, "do I have a share-everything problem or a workflow problem?"
More later...