Distributed Design Patterns

Over the past few years, the concept of design patterns reusable architecture "recipes" that can be applied to solve similar problems in multiple applications has received a good deal of interest. Unfortunately, design patterns are usually demonstrated with toy code that can't be applied directly to most applications.

To truly understand design patterns, you need to realize that different design patterns are designed for different application layers, and many design patterns are already hard-wired into .NET. If you want to create a remote singleton component, for example, you won't add any logic to the class constructor. Instead, you'll use a configuration file to specify the appropriate .NET Remoting activation type.

We'll look at three design patterns that are keenly important for pluggable applications, where components are built separately and snapped together to create a larger system. These design patterns are particularly useful with distributed applications.

Interfaces

Interfaces specify the public properties and methods that a component must provide. More precisely, interfaces define the outward appearance of a class. They don't contain any information about how a property or method actually works.

Conceptually, you can think of an interface as a control panel for a class. Like the buttons on a microwave oven, the interface controls define the range of actions the user can perform. The actual implementation that translates the user's key presses into specific electronic settings is less obvious and is hidden from view.

When you design a distributed system, it helps to create an interface before you actually develop a component. This step forces you to lock down the method that different classes will communicate and the division of functionality between classes before you write any implementation code. Interfaces are also useful because an object can implement more than one interface. The client can choose the appropriate interface and not be distracted by methods that aren't important for the task at hand.

Note

Interfaces aren't fully supported in XML Web services, where every method requires a unique URI endpoint. You can use an interface to develop your Web service, but a client can't use it to interact with your service.


Interfaces also have some specialized uses with distributed applications. They allow parts of an application to be developed independently. For example, you can code a client that communicates with a server object using the server object interface. At the same time, you can code a server that communicates with a client using the client's interface. Without interfaces, you would probably need to copy the server and client assembly files back and forth and update project references endlessly (as you might have experienced if you modified some of the examples in Chapter 4).

In this part of the book, Part II, you'll see two critical examples of interfaces at work:

  • In Chapter 11, we'll use interfaces to simplify communication with .NET Remoting.

  • In Chapter 15, we'll use interfaces to build a modular Windows Forms application that downloads itself piece by piece from a server.

Another reason that you might use interfaces is to tame versioning problems. This technique is a standard part of traditional COM programming, but it's also useful in .NET. Finally, you might need interfaces to expose .NET objects to COM clients. Chapter 9 introduced this topic.

Factories

Factories are classes that create other classes. Factories abstract away the class creation process. This is particularly useful in a system that uses .NET Remoting. Using a factory, the client can create a remote object. The class factory can examine an additional piece of information perhaps a configuration file setting value and decide whether to return a local instance of the object or return a proxy that communicates with a remote instance. This location transparency makes it easy to migrate from one architecture to another and hides the .NET Remoting details from the client application. You'll see location transparency in action in the next chapter.

Facades

In all enterprise applications there is a tension between upgradability and performance optimization. The closer the middle-tier components match the database, the easier it is to optimize database queries and indexes but the more difficult it is to modify the structure of the database or introduce an entirely new data source or data access methodology. This tension can never be completely resolved, but facades offer one technique that can help.

A facade is a layer between the service provider components and the client. Here's the distinction: A service provider encapsulates a service, such as a database operation. A facade encapsulates a higher-level task, such as committing an order (as shown in Figure 10-5).

Figure 10-5. Using a facade

graphics/f10dp05.jpg

However, the facade layer can't ignore database realities. If a given business task corresponds to several database operations, however, it's inefficient to obtain a new connection for each operation. Instead, the facade layer needs to retrieve a connection object from the data source layer.

The next three code listings offer a helpful comparison. Listing 10-5 shows the traditional service provider based approach to creating a new order.

Listing 10-5 Client code using a service provider
 Dim Customer As New CustomerDetails() Dim Order As New OrderDetails() ' (Code to fill these objects omitted.) Dim CustDB As New CustomersDB() Dim OrdDB As New OrdersDB() If Not CustDB.Exists(Customer) Then     CustDB.AddCustomer(Customer) End If OrdDB.AddOrder(Order) 

Listing 10-6 shows one way to write the equivalent code using a business facade.

Listing 10-6 Client code using a business facade
 Dim Customer As New CustomerDetails() Dim Order As New OrderDetails() ' (Code to fill these objects omitted.) Dim Bus As New OrderFacade() Bus.ProcessOrder(Order, Customer) 

Now the business facade (shown in Listing 10-7) takes over the processing, inserting the customer if necessary:

Listing 10-7 The business facade
 Public Class OrderFacade     Public Sub ProcessOrder(Customer As CustomerDetails, _       Order As OrderDetails)                Dim CustDB As New CustomersDB()         Dim OrdDB As New OrdersDB()         If Not CustDB.Exists(Customer) Then             CustDB.AddCustomer(Customer)         End If         OrdDB.AddOrder(Order)     End Sub End Class 

In this case, adding the facade enables you to move some of the business logic away from the client, which is always beneficial. It also increases the overall speed of the operation because fewer network calls are required (only one rather than two or three). Technically, the facade approach is less flexible because it only allows a client to use operations in a set combination. However, if the client needs to be able to call these methods individually, you can add more methods to your facade.

It is possible to introduce one further refinement to the facade. Currently, each individual database operation creates a new connection. Because facades tend to aggregate several database operations, this adds up to additional overhead and inefficiency. You can improve the situation by creating a dedicated helper class that provides and opens a connection object at the beginning of the façade method and closes it at the end. You then pass this connection object to the service provider layer.

Note that the connection object is returned as the base System.Object type. This ensures that you don't tightly couple the business facade to a specific data technology. The connection can represent an ADO.NET connection object or a message queue, or it can just be a null reference. Before you implement the approach shown in Listing 10-8, remember to profile its performance. If you keep the connection open while you are performing a time-consuming processing or validation task, performance might suffer.

Listing 10-8 A facade that aggregates connections
 Public Class OrderFacade     Public Sub ProcessOrder(Customer As CustomerDetails, _       Order As OrderDetails)         Dim DB As New DBHelper()         Dim Conn As Object = DB.GetConnection()         Dim CustDB As New CustomersDB(Conn)         Dim OrdDB As New OrdersDB(Conn)         If Not CustDB.Exists(Customer) Then             CustDB.AddCustomer(Customer)         End If         OrdDB.AddOrder(Order)         DB.ReleaseConnection(Conn)     End Sub End Class 

In theory, facades enable you to alter the data layer without changing the client code. In practice, however, this isn't always possible because the client is always tied to a specific representation of the data. In other words, if you modify the database by splitting information into different tables or introducing new stored procedures, the client won't be affected. If you add new information, however, the client will probably need an updated version of the Customer­Details object and might need user interface changes to request or display this information where appropriate. If used properly, however, facades almost always help to standardize an application's design.

Note

A good example of how a facade can help is if you need to migrate the design of an e-commerce system from synchronous-based communication through a data access layer to asynchronous connection using message queues. If you've designed your facade carefully, you can implement this change without disturbing the client at all.


Facades and Interception

Another advantage of facades is that they allow you to add interception services. For example, you can use a facade to decouple the error-checking and logging functionality from your data access code, rendering it more reusable and extensible. Listing 10-9 shows an example of this approach, where the facade integrates the logging, validation, and database access tasks using distinct components.

Listing 10-9 A facade that incorporates logging and validation
 Public Class OrderFacade     Private Validation As New ValidationServices()     Private Logging As New LoggingServices()     Public Sub ProcessOrder(Customer As CustomerDetails, _       Order As OrderDetails)         Dim CustDB As New CustomersDB()         Dim OrdDB As New OrdersDB()         If Not CustDB.Exists(Customer) Then             ' If the customer is not valid, this method throws an             ' exception that will be handled by the client.             Validation.Test(Customer)             CustDB.AddCustomer(Customer)         End If         ' If the customer is not valid, this method throws an         ' exception that will be handled by the client.         Validation.Test(Order)         OrdDB.AddOrder(Order)         Logging.Log("Order Successful")              End Sub End Class 

Facades and XML Web Services

The previous examples show the code for various facade classes, but they don't specify whether the facade is an XML Web service or a remote MarshalBy­RefObject. (It can be either.) The important point to remember is that the client will communicate infrequently with the facade, whereas the facade will probably communicate frequently with the service provider classes. For example, one call from the client to a facade might result in several method invocations on various database components. This means that the communication overhead between the facade and the service providers should be as low as possible.

Ideally, the service providers will be ordinary classes that the facade can create in-process. This is the pattern used in all the case studies in the third part of this book. All database access code is encapsulated by a dedicated component that runs on the server. Outside clients never access this component directly. Instead, they use a higher-level XML Web service method.

Facades and Transactions

Yet another reason that you might use facades is to add a transactional capability to higher-level processes that incorporate multiple database operations. Without a facade, a transaction might not be possible. If it is, the client is forced to initiate and control it. This means that a distributed transaction is required to coordinate the communication between the client and server-side database objects. With a facade, transactions are much more efficient because they can be implemented entirely on the server side.

You can add transactional support to a facade in a number of ways. One approach is to create two additional methods in your database class: StartTransaction and CommitTransaction. The StartTransaction method returns a transaction object. All the other methods in the database class accept the transaction object as a parameter. If the transaction object is provided, they use it to enlist any commands. If it isn't (and contains a null reference instead), they just run normally. Listing 10-10 shows sample facade code that uses a transaction. A similar approach is used in the case study in Chapter 17.

Listing 10-10 A facade that uses provider-controlled transactions
 Public Class OrderFacade     Public Sub ProcessOrder(Customer As CustomerDetails, _       Order As OrderDetails)         Dim CustDB As New CustomersDB()         Dim OrdDB As New OrdersDB()         Dim DB As New DBHelper()         Dim Transaction As Object         Transaction = DB.StartTransaction()         If Not CustDB.Exists(Customer) Then             CustDB.AddCustomer(Customer, Transaction)         End If         OrdDB.AddOrder(Order, Transaction)         DB.CommitTransaction(Transaction)     End Sub End Class 

Note that the transaction object is retained in the OrderFacade class as a generic object. This allows the facade to be independent of the database code, which might use a SqlTransaction object, an OleDbTransaction object, or something entirely different.

Another option is to use a COM+ transaction, which requires slightly more overhead (because it uses a two-stage commit process) but doesn't require a specially designed database component. This technique (shown in Listing 10-11) is particularly easy if your facade is implemented as an XML Web service, in which case you can just use the TransactionOption property of the <WebMethod> attribute. The transaction is automatically committed unless an exception is thrown. This technique is used in Chapter 18.

Listing 10-11 A facade that uses a COM+ transaction
 Public Class OrderFacade     <WebMethod(TransactionOption := TransactionOption.RequiresNew)> _     Public Sub ProcessOrder(Customer As CustomerDetails, _       Order As OrderDetails)         ' (Code omitted.)     End Sub End Class 


Microsoft. NET Distributed Applications(c) Integrating XML Web Services and. NET Remoting
MicrosoftВ® .NET Distributed Applications: Integrating XML Web Services and .NET Remoting (Pro-Developer)
ISBN: 0735619336
EAN: 2147483647
Year: 2005
Pages: 174

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net