Overview

The core of a Web application is its server-side logic. The Web application layer itself can be comprised of many distinct layers. The typical example is a three-layered architecture comprised of presentation, business, and data layers. Figure 1 illustrates a common Web application architecture with common components grouped by different areas of concern.

Figure 1 A common Web application architecture

Design Considerations

When designing a Web application, the goals of a software architect are to minimize the complexity by separating tasks into different areas of concern while designing a secure, high-performance application.

When designing your Web application, consider the following guidelines:

Partition your application logically. Use layering to partition your application logically into presentation, business, and data access layers. This helps you to create maintainable code and allows you to monitor and optimize the performance of each layer separately. A clear logical separation also offers more choices for scaling your application.

Use abstraction to implement loose coupling between layers. This can be accomplished by defining interface components, such as a façade with well-known inputs and outputs that translates requests into a format understood by components within the layer. In addition, you can also use Interface types or abstract base classes to define a shared abstraction that must be implemented by interface components.

Understand how components will communicate with each other. This requires an understanding of the deployment scenarios your application must support. You must determine if communication across physical boundaries or process boundaries should be supported, or if all components will run within the same process.

Reduce round trips. When designing a Web application, consider using techniques such as caching and output buffering to reduce round trips between the browser and the Web server, and between the Web server and downstream servers.

Consider using caching. A well-designed caching strategy is probably the single most important performance-related design consideration. ASP.NET caching features include output caching, partial page caching, and the cache API. Design your application to take advantage of these features.

Consider using logging and instrumentation. You should audit and log activities across the layers and tiers of your application. These logs can be used to detect suspicious activity, which frequently provides early indications of an attack on the system.

Avoid blocking during long-running tasks. If you have long-running or blocking operations, consider using an asynchronous approach to allow the Web server to process other incoming requests.

Consider authenticating users across trust boundaries. You should design your application to authenticate users whenever they cross a trust boundary; for example, when accessing a remote business layer from your presentation layer.

Do not pass sensitive data in plain text across the network. Whenever you need to pass sensitive data such as a password or authentication cookie across the network, consider encrypting and signing the data or using Secure Sockets Layer (SSL) encryption.

Design your Web application to run using a least-privileged account. If an attacker manages to take control of a process, the process identity should have restricted access to the file system and other system resources in order to limit the possible damage.

Web Application Frame

There are several common issues that you must consider as you develop your design. These issues can be categorized into specific areas of the design. The following table lists the common issues for each category where mistakes are most often made.

Authentication

Designing an effective authentication strategy is important for the security and reliability of your application. Improper or weak authorization can leave your application vulnerable to spoofing attacks, dictionary attacks, session hijacking, and other types of attack.

Consider the following guidelines when designing an authentication strategy:

Identify trust boundaries within Web application layers. This will help you to determine where to authenticate.

Use a platform-supported authentication mechanism such as Windows Authentication when possible.

If you are using Forms Authentication, use the platform features, such as the forms element, when possible.

Authorization

Authorization determines the tasks that an authenticated identity can perform, and identifies the resources that can be accessed. Designing an effective authorization strategy is important for the security and reliability of your application. Improper or weak authorization leads to information disclosure, data tampering, and elevation of privileges. Defense in depth is the key security principle to apply to your application’s authorization strategy.

Consider the following guidelines when designing an authorization strategy:

Consider the granularity of your authorization settings. Building your authorization with too much granularity will increase your management overhead; however, using less granularity will reduce flexibility.

Access downstream resources using a trusted identity based on the trusted subsystem model.

Use impersonation and delegation to take advantage of the user-specific auditing and granular access controls of the platform, but consider the effect on performance and scalability.

Caching

Caching improves the performance and responsiveness of your application. However, incorrect caching choices and poor caching design can degrade performance and responsiveness. You should use caching to optimize reference data lookups, avoid network round trips, and avoid unnecessary and duplicate processing. To implement caching, you must first decide when to load data into the cache. Try to load cache data asynchronously or by using a batch process to avoid client delays.

Pool shared resources that are expensive, such as network connections, instead of caching them.

Cache data in a ready-to-use format.

Exception Management

Designing an effective exception management strategy is important for the security and reliability of your application. Correct exception handling in your Web pages prevents sensitive exception details from being revealed to the user, improves application robustness, and helps to avoid leaving your application in an inconsistent state in the event of an error.

Consider the following guidelines when designing an exception management strategy:

Do not use exceptions to control the logical flow of your application.

Do not catch exceptions unless you must handle them, you need to strip sensitive information, or you need to add additional information to the exception.

Design a global error handler to catch unhandled exceptions.

Display user-friendly messages to end users whenever an error or exception occurs.

Do not reveal sensitive information, such as passwords, through exception details.

Logging and Instrumentation

Designing an effective logging and instrumentation strategy is important for the security and reliability of your application. You should audit and log activity across the tiers of your application. These logs can be used to detect suspicious activity, which frequently provides early indications of an attack on the system, and help to address the repudiation threat where users deny their actions. Log files may be required in legal proceedings to prove the wrongdoing of individuals. Generally, auditing is considered most authoritative if the audits are generated at the precise time of resource access and by the same routines that access the resource.

Consider the following guidelines when designing a logging and instrumentation strategy:

Navigation

Design your navigation strategy in a way that separates it from the processing logic. Your strategy should allow users to navigate easily through your screens or pages. Designing a consistent navigation structure for your application will help to minimize user confusion as well as reduce the apparent complexity of the application.

Consider the following guidelines when designing your navigation strategy:

Use well-known design patterns, such as Model-View-Presenter (MVP), to decouple UI processing from output rendering.

Consider encapsulating navigation in a master page so that it is consistent across pages.

Design a site map to help users find pages on the site, and to allow search engines to crawl the site if desired.

Consider using wizards to implement navigation between forms in a predictable way.

Consider using visual elements such as embedded links, navigation menus, and breadcrumb navigation in the UI to help users understand where they are, what is available on the site, and how to navigate the site quickly.

Page Layout (UI)

Design your application so that the page layout can be separated from the specific UI components and UI processing. When choosing a layout strategy, consider whether designers or developers will be building the layout. If designers will be building the layout, choose a layout approach that does not require coding or the use of development-focused tools.

Consider the following guidelines when designing your layout strategy:

Use Cascading Style Sheets (CSS) for layout whenever possible.

Use table-based layout when you need to support a grid layout, but remember that table-based layout can be slow to render, does not have full cross-browser support, and there may be issues with complex layout.

Use a common layout for pages where possible to maximize accessibility and ease of use.

Use master pages in ASP.NET applications to provide a common look and feel for all of the pages.

Avoid designing and developing large pages that accomplish multiple tasks, particularly where only a few tasks are usually executed with each request.

Page Rendering

When designing for page rendering, you must ensure that you render the pages efficiently and maximize interface usability.

Consider the following guidelines when designing a page-rendering strategy:

Consider data-binding options. For example, you can bind custom objects or datasets to controls. However, be aware that binding only applies to rendered data in ASP.NET.

Consider using Asynchronous JavaScript and XML (AJAX) for an improved user experience and better responsiveness.

Consider using data-paging techniques for large amounts of data to minimize scalability issues.

Presentation Entity

Presentation entities store the data that you will use to manage the views in your presentation layer. Presentation entities are not always necessary. Consider using presentation entities only if the datasets are sufficiently large or complex that they must be stored separately from the UI controls. Design or choose appropriate presentation entities that you can easily bind to UI controls.

Consider the following guidelines when designing presentation entities:

Determine if you need presentation entities. Typically, you might need presentation entities if the data or data format to be displayed is specific to the presentation layer.

Consider the serialization requirements for your presentation entities, if they are to be passed across the network or stored on the disk.

Consider using presentation entities to store state related to the UI. If you want to use this state to help your application recover from a crash, make sure after recovery that the user interface is in a consistent state.

Request Processing

When designing a request-processing strategy, you should ensure separation of concerns by implementing the request-processing logic separately from the UI.

Consider the following guidelines when designing a request-processing strategy:

Consider centralizing the common pre-processing and post-processing steps of Web page requests to promote logic reuse across pages. For example, consider creating a base class derived from the ASP.NET Page class to contain your common pre- and post-processing logic.

Consider dividing UI processing into three distinct roles—model, view, and controller/presenter—by using the Model-View-Controller (MVC) or Model-View-Presenter (MVP) pattern.

If you are designing views for handling large amounts of data, consider giving access to the model from the view by using the Supervising Controller pattern, which is a form of the MVP pattern.

If your application does not have a dependency on view state and you have a limited number of control events, consider using the MVC pattern.

Consider using the Intercepting Filter pattern to implement the processing steps as pluggable filters when appropriate.

Session Management

When designing a Web application, an efficient and secure session-management strategy is important for performance and reliability. You must consider session-management factors such as what to store, where to store it, and how long information will be kept.

Consider the following guidelines when designing a session-management strategy:

If you have a single Web server, require optimum session state performance, and have a relatively limited number of concurrent sessions, use the in-process state store.

If you have a single Web server, your sessions are expensive to rebuild, and you require durability in the event of an ASP.NET restart, use the session state service running on the local Web server.

Use a remote session state service or the Microsoft SQL Server® state store for Web farm scenarios.

If you are storing state on a separate server, protect your session state communication channel.

Prefer basic types for session data to reduce serialization costs.

Validation

Designing an effective validation solution is important for the security and reliability of your application. Improper or weak authorization can leave your application vulnerable to cross-site scripting attacks, SQL injection attacks, buffer overflows, and other types of input attack.

Consider the following guidelines when designing a validation strategy:

Use client-side validation for user experience, and server-side validation for security.

Presentation Layer Considerations

The presentation layer of your Web application displays the UI and facilitates user interaction. The design should focus on separation of concerns, where the user interaction logic is decoupled from the UI components.

Consider the following guidelines when designing the presentation layer:

Consider separating the UI components from the UI process components.

Use client-side validation to improve user experience and responsiveness, and server-side validation for security. Do not rely on just client-side validation.

Use page output caching or fragment caching to cache static pages or parts of pages.

Use Web server controls if you need to compile these controls into an assembly for reuse across applications, or if you need to add additional features to existing server controls.

Use Web user controls if you need to reuse UI fragments on several pages, or if you want to cache a specific parts of the page.

Business Layer Considerations

When designing the business layer for your Web application, consider how to implement the business logic and long-running workflows. Design business entities that represent the real world data, and use these to pass data between components.

Consider the following guidelines when designing the business layer:

Design a separate business layer that implements the business logic and workflows. This improves the maintainability and testability of your application.

Consider centralizing and reusing common business logic functions.

Design your business layer to be stateless. This helps to reduce resource contention and increase performance.

Use a message-based interface for the business layer. This works well with a stateless Web application business layer.

Design transactions for business-critical operations.

Data Layer Considerations

Design a data layer for your Web application to abstract the logic necessary to access the database. Using a separate data layer makes the application easier to configure and maintain. The data layer may also need to access external services using service agents.

Consider the following guidelines when designing the data layer:

Design a separate data layer to hide the details of the database from other layers of the application.

Design entity objects to interact with other layers, and to pass the data between them.

Design to take advantage of connection pooling to minimize the number of open connections.

Design an exception-handling strategy to handle data access errors, and to propagate exceptions to business layers.

Consider using batch operations to reduce round trips to the database.

Service Layer Considerations

Consider designing a separate service layer if you plan to deploy your business layer on a remote tier, or if you plan to expose your business logic using a Web service.

Consider the following guidelines when designing the service layer:

If your business layer is on a remote tier, design coarse-grained service methods to minimize the number of client-server interactions, and to provide loose coupling.

Design the services without assuming a specific client type.

Design the services to be idempotent, assuming that the same message request may arrive multiple times.

Testing and Testability Considerations

Testability is a measure of how well your system or components allow you to create test criteria and execute tests to determine if the criteria are met. You should consider testability when designing your architecture because it makes it easier to diagnose problems earlier and reduce maintenance cost. To improve the testability of your application, you can use logging events, provide monitoring resources, and implement test interfaces.

Consider the following guidelines for testability:

Clearly define the inputs and outputs of the application or components during the design phase.

Consider using the Passive View pattern (a variation of the MVP pattern) in the presentation layer, which removes the dependency between the view and the model.

Design a separate business layer to implement the business logic and workflows, which improves the testability of your application.

Design an effective logging strategy, which allows you to detect bugs that might otherwise be difficult to discover. Logging will help you to focus on faulty code when bugs are found. Log files should contain information that can be used to replicate the issues.

Design loosely coupled components that can be tested individually.

Performance Considerations

You should identify your performance objectives early in the design phase of a Web application by gathering the non-functional requirements. Response time, throughput, CPU, memory, and disk I/O are a few of the key factors you should consider when designing your application.

Consider the following guidelines for performance:

Ensure that the performance requirements are specific, realistic, and flexible.

Implement caching techniques to improve the performance and scalability of the application.

Perform batch operations to minimize round trips across boundaries.

Reduce the volume of HTML transferred between server and client. For instance, you can disable view state when you do not need it; limit the use of graphics, and considering using compressed graphics where appropriate.

Avoid unnecessary round trips over the network.

Security Considerations

Security is an important consideration for protecting the integrity and privacy of the data and the resources of your Web application. You should design a security strategy for your Web application that uses tested and proven security solutions, and implement authentication, authorization, and data validation to protect your application from a range of threats.

Consider the use of input validation and data validation at every trust boundary to mitigate security threats such as cross-site scripting and code-injection.

Do not rely on client-side validation only. Use server-side validation as well.

Consider encrypting and digitally signing any sensitive data that is sent across the network.

Deployment Considerations

When deploying a Web application, you should take into account how layer and component location will affect the performance, scalability, and security of the application. You might also need to consider design trade-offs. Use either a distributed or a non-distributed deployment approach, depending on the business requirements and infrastructure constraints.

Consider the following guidelines for deployment:

Consider using non-distributed deployment to maximize performance.

Consider using distributed deployment to achieve better scalability and to allow each layer to be secured separately.

Non-Distributed Deployment

In a non-distributed deployment scenario, all the logically separate layers of the Web application are physically located on the same Web server, except for the database. You must consider how the application will handle multiple concurrent users, and how to secure the layers that reside on the same server. Figure 2 shows this scenario.

Figure 2 Non-distributed deployment of a Web application

Consider the following guidelines when choosing a non-distributed deployment:

Consider using non-distributed deployment if your Web application is performance-sensitive, because the local calls to other layers provide performance gains.

Consider designing a component-based interface for your business layer.

If your business logic runs in the same process, avoid authentication at the business layer.

Consider using a trusted identity (through the trusted subsystem model) to access the database. This improves the performance and scalability of your application.

Distributed Deployment

In a distributed deployment scenario, the presentation and business layers of the Web application reside on separate physical tiers, and communicate remotely. You will typically locate your business and data access layers on the same sever. Figure 3 shows this scenario.

Figure 3 Distributed deployment of a Web application

Consider the following guidelines when choosing a distributed deployment:

Do not physically separate your business logic components unless this is necessary.

Load Balancing

When you deploy your Web application on multiple servers, you can use load balancing to distribute requests so that they are handled by different Web servers. This helps to maximize response times, resource utilization, and throughput. Figure 4 shows this scenario.

Figure 4 Load balancing a Web application

Consider the following guidelines when designing your Web application to use load balancing:

Avoid server affinity when designing scalable Web applications. Server affinity occurs when all requests from a particular client must be handled by the same server. It usually occurs when you use locally updatable caches, or in-process or local session state stores.

Consider designing stateless components for your Web application; for example, a Web front end that has no in-process state and no stateful business components.

Consider using Windows Network Load Balancing (NLB) as a software solution to implement redirection of requests to the servers in an application farm.

Web Farm Considerations

A Web farm allows you to scale out your application, which can also minimize the impact of hardware failures. When you add more servers, you can use either a load-balancing or clustering approach.

Consider the following guidelines when designing your Web application to use a Web farm:

Consider using clustering to minimize the impact of hardware failures.

Consider configuring the Web farm to route all requests from the same user to the same server in order to provide affinity where this is required.

Do not use in-process session management in a Web farm when requests from the same user cannot be guaranteed to be routed to the same server. Use an out-of-process state server service or a database server for this scenario.

Pattern Map

Key patterns are organized by the key categories detailed in the Web Applications Frame in the following table. Consider using these patterns when making design decisions for each category.

Pattern Descriptions

Exception Shielding. Filter exception data that should not be exposed to external systems or users.

Façade. Implement a unified interface to a set of operations to provide a simplified, reduced coupling between systems.

Front Controller. Consolidate request handling by channeling all requests through a single handler object, which can be modified at run time with decorators.

Intercepting Filter. Create a chain of composable filters (independent modules) to implement common pre-processing and post-processing tasks during a Web page request.

Model-View-Controller. Separate the UI code into three separate units: Model (data), View (interface), and Presenter (processing logic), with a focus on the View. Two variations on this pattern include Passive View and Supervising Controller, which define how the View interacts with the Model.

Model-View-Presenter. Separate request processing into three separate roles, with the View being responsible for handling user input and passing control to a Presenter object.

Page Cache. Improve the response time for dynamic Web pages that are accessed frequently but change less often and consume a large amount of system resources to construct.

Page Controller. Accept input from the request and handle it for a specific page or action on a Web site.

Passive View. Reduce the view to the absolute minimum by allowing the controller to process user input and maintain the responsibility for updating the view.

Provider. Implement a component that exposes an API that is different from the client API, to allow any custom implementation to be seamlessly plugged in.

Service Interface. A programmatic interface that other systems can use to interact with the service.

Supervising Controller. A variation of the MVC pattern in which the controller handles complex logic, in particular coordinating between views, but where the view is responsible for simple view-specific logic.

Template View. Implement a common template view, and derive or construct views using this template view.

Transform View. Transform the data passed to the presentation tier into HTML to be displayed on the UI.

Two-Step View. Transform the model data into a logical presentation without any specific formatting, and then convert that logical presentation into the actual formatting required.

Technology Considerations

On the Microsoft platform, from an ASP.NET standpoint, you can combine the ASP.NET Web Forms model with a range of other technologies, including ASP.NET AJAX, ASP.NET MVC, Microsoft Silverlight™, and ASP.NET Dynamic Data. Consider the following guidelines:

If you want to build applications that are accessed through a Web browser or specialized user agent, consider using ASP.NET.

If you want to build applications that provide increased interactivity and background processing, with fewer page reloads, consider using ASP.NET with AJAX.

If you want to build applications that include rich media content and interactivity, consider using ASP.NET with Silverlight controls.

If you are using ASP.NET, consider using master pages to implement a consistent UI across all pages.

If you are building a data-driven Web application with pages based on the data model of the underlying database, consider using ASP.NET Dynamic Data.