In The Quest for ASP.NET Scalability, Michèle Leroux Bustamante looks at some of the architectural and design decisions that may affect ASP.NET application scalability. In addition, she looks at how you can use Enterprise Services and MSMQ to reduce the effect of those scalability problems.
This article gives a useful overview of the areas you need to consider when looking at web application scalability. It covers the threading models for ASP.NET running with IIS 5 or IIS 6 and looks at physically scaling the application tiers.
The only comment I would take issue with is this (emphasis mine):
_An enterprise solution should at least have two tiers. In a two-tier scenario, the Web server tier could host...well...the Web server (usually IIS), the Web application, and possibly business and database access components. The database server tier allows you to offload the heavy lifting of the database engine to another, usually more powerful piece of equipment. In reality, two tiers is rarely enough for an enterprise solution that takes a beating. Chances are there will be some business processing that puts greater demand on system resources, such as file I/O operations, heavy number crunching, and integrated system calls. So, **there will traditionally be at least three tiers allowing the Web server to focus on simpler requests**, delegating business component interactions to a distributed application tier._
During the processing of these business operations, what exactly is the web server tier doing? Dealing with all these simpler requests isn’t usually very taxing. I wonder if tradition might have more to do with it than initially appears. Because we were introduced to 3-tier application design as a solution to the internet-scale problems of client-server systems, it was natural to think that this logical architecture applied in the physical realm too. I remember thinking that that was somehow automatically the case.
Today, though, I believe it is normally better to keep the business logic on the web tier and scale horizontally at that level unless there are good architectural (not specifically scalability) reasons for doing otherwise (this has been discussed before and I’ve mentioned security and service orientation as possible reasons).