Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Distributed Command Pattern - an extension of command pattern for connected systems

0.00/5 (No votes)
25 Jan 2005 1  
Distributed Command Pattern is a pattern for connected systems which implements command pattern. It frees designers from thinking about the communication and helps them concentrate on implementing commands as if it is a regular desktop application. The framework takes care of the communication.

Introduction

Distributed Command Pattern is a proposed pattern for solving the extra architectural design issues which need to be considered for connected systems than designing regular applications. The goal of this pattern is to �Have the same design for both standalone and connected systems�. This pattern allows developers to concentrate on designing a regular application following the Command Pattern [Gamma 1995] without ever thinking that this application is going to connect with other systems. This pattern frees a developer from thinking about the transport and communication protocol while designing the commands, and keeps the architecture simple. Designers need not worry about how to send necessary data to the server when some command gets executed and how to process received data from the server and then reflect on the UI of the connected applications. When a command gets executed locally on one running application, distributed design pattern takes care of the hurdles of executing the same command in all connected systems simultaneously in order to keep them in the same state. From a designer�s point of view, the architecture remains as simple as a regular desktop application designed using regular command pattern having no network awareness, yet it performs all the necessary communications in order to execute the commands on all connected applications as if the command has been invoked internally.

Overview of Command Pattern

Command Pattern is a very widely used design pattern for both web and desktop applications. It allows developers to think and design architecture in terms of actions performed by the user and the system. For example, when a file is opened, a FileOpenCommand is issued and necessary code is written only for that command. This simplifies architecture design because when implementing an action, the job needed to do is narrow and focused. Command Pattern also forces design of a very reusable system because the small command classes are reusable by design and can be invoked either by the user or by any system. For example, a user can issue FileOpenCommand by selecting �Open� from �File� menu, or it can be automatically invoked by a plug-in or by a macro, or by another command which needs to open a file. As a result, the command is written only once and maintained in only one place, but it offers wide reusability.

The problem of distributed systems

While designing the architecture of connected systems, the design needs to consider how commands are executed not only in the application where it originated, but also on all other connected systems. For example, in a chat application which is connected to a chat room, if one user writes a message, the message needs to be shown on the user�s screen and also simultaneously on screens of all other users who are present on the chat room. This raises an architectural design issue where a command needs to be invoked on the originating application and necessary data be sent to the server in order to broadcast the message to all other connected users. Each connected user needs to receive the data from server, translate it, and then show it on the screen.

This situation results in extra architectural design and coding for the communication performed for each action. First of all, following the command pattern, we can only perform action on the same application, more specifically, on the same process. Then we need to prepare for a network transmission. So, there is some specific protocol developed to maintain communication with the server and the client. Both the server and the client maintain a code base to understand this protocol. When a message is received from the network, the message needs to be processed and then some specific code needs to be written in order to perform actions based on the message.

Here is a design which shows how a typical chat application is developed:

Figure 1 - Regular Connected Applications.

The drawbacks of this design are:

  • For each action, there is a command, and there is a protocol for transmitting necessary data to the server and also for the clients to receive data from the server.
  • If a command is changed, the protocol also needs to be modified in order to reflect the changed execution.
  • For each action, the developer needs to think of three issues:
    • A command needs to be developed in order to perform the action in the application locally.
    • A message for sending to the server and the logic for packing necessary data.
    • A receiver for receiving that message, the logic for processing it and performing necessary action.

Distributed Command Pattern

Distributed command pattern solves the problems by introducing an architecture where the same command is invoked automatically on connected systems when any one system executes a command. This pattern offers a way to invoke a command in-process on a connected system from another connected system. There is no need to send a custom message containing required data to the server to notify about the command that is executed, and there is also no need for clients to read and parse a message in order to extract necessary data and then act according to the message. The very command that is executed on a system is serialized and transmitted to other systems and then executed locally in order to simulate the same situation that occurred on the originating system.

Distributed Command Pattern has the following benefits:

  • A Command class is developed exactly the same way as it is developed for simple standalone systems which have no network awareness.
  • There is no need for designers to develop custom protocols or design messages in order to send or receive necessary data for each command.
  • When a command is invoked, it not only gets executed on the same process, but also on other connected processes and connected computers over the network simultaneously.
  • It enables connected systems to be �in-sync� with all others without knowing about others� presence.
  • It frees developers from thinking and implementing how to have all connected systems in-sync with each other as the exact same command is executed in all processes just as if the command was invoked locally in each connected system.
  • Developers need not worry whether the command is invoked from the same process or is coming from another process.
  • It reduces the amount of code needed for communication as there is a unified way to send or receive any command.
  • It is possible to seamlessly implement undo/redo of commands. If one application performs �undo�, all connected system perform �undo� exactly the same way.

Here is an illustration, how a command gets executed on all connected systems simultaneously:

Figure 2 - Distributed Execution of commands

Here is a sample code of a Command class which shows that the command actually has no awareness of connected systems:

[Serializable]
public class SendMessageCommand : UICommand, ICommand
{
    public string From;
    public string Message;

    public SendMessageCommand(
string from, 
string message)
    {
        this.From = from;
        this.Message = message;
    }

    void ICommand.Execute()
    {
        base.UI.ChatHistoryTextBox.AppendText( this.From 
                     + " says:" + Environment.NewLine );
        base.UI.ChatHistoryTextBox.AppendText( this.Message 
                                   + Environment.NewLine );
    }
}

Development of Distributed Command Pattern

The development of distributed command pattern is shown in step by step, gradually showing how a regular command pattern becomes a distributed command pattern.

Step 1. Regular command pattern

Figure 3 - Command Pattern

This is the regular command pattern. Here, the Invoker creates a Command and directly calls the Command�s Execute method to execute the command.

ICommand command = new FileSaveCommand( fileContent );
command.Execute();

There is another variance of the command pattern where a command is not directly executed by the invoker. It is executed via a command executor, namely Command Fa�ade. Here�s how the variance looks like:

Figure 3a - Another variant of command pattern

The difference of this pattern is that, a command is not directly executed by the invoker. Invoker creates the instance of the Command and passes the command to the Command Facade. Command Facade actually calls the Execute method of the Command.

ICommand command = new FileSaveCommand( fileContent );
CommandFacade.Execute( command );

The benefit of this pattern is that, Command Facade can intercept any command execution and it can provide commands with some context and additional metadata which commands may need during execution. It also acts as a central command execution chamber. In Distributed Command Pattern, this specific feature is essential.

Step 2. Introducing a bus

In Distributed Command Pattern, the command fa�ade is actually a command bus. Command Bus is created following the idea of Message Bus pattern [Snell 2004]. Message Bus introduces a bus which knows how to format a message and how to send the message via some channel. In DCP, Command Bus is also like a Message Bus. It maintains a pipeline of command executors which actually perform the execution of the command. Whenever a command is sent to the Command Bus, it informs all executors in the pipeline to execute the command. The executors work as channels and actually do the execution of the command.

Figure 4 - Command Bus

The command bus does not change the way a command is executed. Invoker writes the same code as before:

ICommand command = new FileSaveCommand( fileContent );
CommandBus.Execute( command );

However, inside the Command Bus, it calls all executors in the pipeline to execute the command:

CommandBus.execute( ICommand command ) 
{ 
    foreach( ICommandExecutor executor in this.Pipeline ) 
    { 
        executor.Execute( command ); 
    } 
}

Step 3. Introducing executors

Executors provide the execution environment for commands. Although the commands contain necessary code to be executed, command executors provide necessary support to the commands during execution. For example, imagine a Windows form which has a text box. A command needs to add some text to the text box when executed. But the command is a separate class and it has no reference of the text box on the form. So, the form, being a command executor, intercepts the command from the command bus, and provides the command object with the reference to the text box. It then executes the command.

class MainForm : ICommandExecutor 
{
    ICommandExecutor.Execute( ICommand command ) 
    { 
        TextAddCommand cmd = command as TextAddCommand; 
        cmd.TheTextBox = this.MessageTextBox; 
        cmd.Execute(); 
    } 
}

This is one type of local executor, where the command is executed in-process. There is another type of command executor which is called �DistributedCommandExecutor� which has the responsibility to send or receive commands via transporters. This type of executors are responsible for keeping all connected applications in-sync and provide the command distribution service.

However, the distributed executor does not execute a command directly. It just sends the command using a transporter to another connected application. On the other end, there is another distributed command executor which receives the command from its own transporter. It then notifies the command bus that a command is received and the bus again sends the command through the pipeline for execution. This time, the local executor on the other end receives the command and executes the command in its own context. This results in execution of the same command on another computer just as if the command was created and executed there.

Figure 5 - High Level Architecture

Step 4. Introducing transporter

Transporter provides the communication service. It sits between a distributed command executor and the network communication library to provide transparent message transportation service. A transporter is created by the application according to the communication media it uses, and the application itself attaches the transporter with a distributed command executor. For example, when a client application connects to a server using a TCP/IP connection, it uses a transporter which has the ability to send and receive data using Sockets in binary mode. After successfully establishing a connection, a distributed executor is created for the transporter and the executor is put in the command bus for transmitting commands. For example:

ITransporter transporter = new BinaryTransporter( address, port );
ICommandExecutor executor = new BinaryDistributedExecutor( transporter );
CommandBus.AddExecutor( executor );

CommandBus.Execute( new HiCommand() );

The above pseudo code shows how a distributed executor is associated with a transporter. When the executor is added in the bus, it is ready to receive and send any command that is executed on the bus. As a result, when a command is executed on the bus, the command is sent to the distributed executor and the executor sends the command to the transporter which in turn transmits the message using the Socket it has created.

The architecture of Distributed Command Pattern is a type of pipeline architecture which can be viewed in this way also:

Figure 6 - Pipeline nature of the Distributed Command Pattern

Technical Architecture

The technical architecture of the DCP framework is as follows:

Figure 7 - Technical Architecture

Sequence of execution

When a command is sent to the command bus, the following execution occurs:

  • Command bus passes the command to each executor in the command pipeline.
  • If the executor is a local command executor, it executes the command directly.
  • If the executor is a distributed command executor, it decorates the command using a RemoteCommand class (Decorator Pattern [Gamma 1995]). This class contains the actual command and additionally contains a transporter ID which identifies from where the command is being sent.
  • The remote command is then sent to a transporter in order to send the command to a destination using some communication protocol.
  • On the other end, the transporter layer receives the message. It creates the command object from the message.
  • The command is sent to the distributed command executor which is attached with the transporter. The executor receives the command, and then it modifies the �Source� property of the RemoteCommand to set the transporter ID from which it received the command.
  • The executor notifies its command bus about the arrival of the command.
  • That command bus notifies all of its command executors in its pipeline to process the command.
  • During this time, the executor which originally notified the command bus about the received command again gets called by the command bus as it is in its pipeline. But it sees from the source property of the remote command that, it is that executor which sent the command to the bus. So, it does not again send the command to the transporter and thus prevents an infinite loop.

How to apply this pattern

Applications interested to implement this pattern needs to do the following:

  • Create the command bus only once, and once created, it does not change.
  • Create the transportation layer which implements ITransporter only once, and once created, it does not change.
  • Create a Local Command Executor in order to execute commands locally, and once created, it does not change usually.
  • Create a Distributed Command Executor in order to send commands to other connected applications using the transporter, and once created, it does not change usually.
  • Develop as many commands as needed according to requirement.

Sample chat application

A good example of the Distributed Command Pattern is a chat application. The chat application has two modes � server move and client mode.

Figure 8 - Sample Chat Application with DCP

Server Mode

The server starts a transporter to listen for connection from other clients. It also creates a local command bus and registers itself to receive the commands sent to the bus in order to execute the command locally.

// 1. Create the command bus and attach myself as one of the command executors

this._MyBus = new CommandBus( true );
this._MyBus.AddExecutor( this );
 
// 2. Establish a collaboration server, this is the transporter

this._Server = new CollaboratorServer( 
            int.Parse( myPortTextBox.Text ),
            Guid.NewGuid().ToString(), SERVER_NAME,
            new PacketReceiveHandler( PacketReceived ),
            new NewClientHandler( NewClientConnected ), 
            new ClientClosedHandler( ClientClosed ) );
 
// 3. Goto listen mode

this._Server.Listen();

Whenever a client connects to the server, the transporter created for the client is attached with a newly created Distributed Command Executor and then it is added in the command bus. As a result, any command that is sent to the bus gets executed both locally and is also sent to the connected client.

private bool NewClientConnected( CollaborationClient client, 
                      out string uniqueID, out string name )
{
    uniqueID = Guid.NewGuid().ToString();
    name = uniqueID;

    // This client will participate in distributed command execution

    ICommandExecutor executor = new DistributedExecutorHTTP( client );
    this._MyBus.AddExecutor( executor );

    // Accept the client

    return true;
}

If the client sends any command, then the bus receives the command and then it sends the command through the pipeline again for execution. The command gets executed locally on the server and also the command is broadcasted to all other clients connected to the server. As a result, if one client says, �Hi�, all the clients and the server show �Hi�.

Client Mode

The client first creates a Command Bus and attaches itself as a command executor in order to execute commands locally.

When it connects to the server, it creates a Distributed Command Executor and adds the executor to the command bus. As a result, any command executed on the client is also sent to the server.

// 1. Create the command bus and attach myself as one executor of commands

this._MyBus = new CommandBus( false );
this._MyBus.AddExecutor( this );
 
// 2. Establish connection

this._Client = new CollaborationClient( 
            serverTextBox.Text,
            int.Parse( serverPortTextBox.Text ), 
            Guid.NewGuid().ToString(),
            nameTextBox.Text, 
            new PacketReceiveHandler( PacketReceived ),
            new ClientClosedHandler( ClientClosed ) );
 
// 3. This client will provide distributed command execution service

this._MyBus.AddExecutor( new DistributedExecutorHTTP( this._Client ) );
 
// 4. Create the command to inform new client

ICommand command = new NewClientCommand( 
            new ConnectedClient( this._Client.UniqueId, nameTextBox.Text ) ); 
 
// 5. Execute the command

this._MyBus.Execute( command );

Executing commands - Sending message

Executing a command is very simple and requires no extra effort to keep all the clients and server in sync. For example, in order to send a message, the following code is sufficient:

this._MyBus.Execute( new SendMessageCommand( nameTextBox.Text, 
                                        messageTextBox.Text ) );

Similarly, in order to disconnect and notify everyone about leaving the chat room, this code is sufficient:

this._MyBus.Execute( new ClientLeftCommand( client ) );

Local Command Executor

Local command executor receives commands from the command bus and executes them locally. For example, the main form (the only form in the app) is a local command executor. It basically receives a command, decorates it with necessary UI support, and executes the command. For example, the following generic code is sufficient for a local command executor:

void ICommandExecutor.Execute(ICommand command)
{
    ICommand actualCommand = command;
 
    // If this is a remote command,

    // then the actual command is packed inside it

    if( command is RemoteCommand )
    {
        RemoteCommand remoteCommand = command as RemoteCommand;
        actualCommand = remoteCommand.ActualCommand;
    }
 
    �
    �

    // Call the execute method on UI thread

    if( this.InvokeRequired )
        this.Invoke( new MethodInvoker( command.Execute ) );
    else
        command.Execute();
}

The Transportation Layer � HTTP Transportation

This application has a very handy transportation layer which communicates using HTTP protocol. There are two classes, one is �CollaborationServer� which acts as a server, and another is �CollaborationClient� which acts as a client to the server.

The transportation layer is completely generic. It accepts HTTP packets and sends the packets over the network. This library can also be used in a wide variety of applications in order to exchange both textual and binary data. It can also be used to create an HTTP server without writing any communication code at all as it already uses HTTP protocol to exchange data. Here�s a sample basic conversation:

Request:

GET / HTTP/1.1
Correlation-ID: 0B83745D-2AAB-4bce-8AC9-B8A590F07768

Response:

HTTP/1.1 200 Ok
Correlation-ID: 0B83745D-2AAB-4bce-8AC9-B8A590F07768

This library is a modified version of the original Bi-Directional HTTP Connection [Szymanski 2004]. It is customized and enhanced to make a client-server collaboration library. The specialty of this library is it opens only one TCP/IP socket and provides both ways communication over the same socket. As a result, it is a firewall friendly solution for peer-to-peer communication.

The collaboration library is fully object oriented. It sends or receives commands as HTTP messages. There is an HTTPMessage class which contains details about the message being sent or received. This HTTPMessage is inherited by a generic class called �Packet�. There are two types of packets, PacketRequest which is used to send a message to another party and a PacketResponse which is returned by the other party.

Figure 9 - Message and HTTP Packet hierarchy

An application only needs to extend PacketRequest and optionally GenerciSuccessPacket in order to provide its own messages. For example, WhatIsYourNameRequest class can extend PacketRequest to send request for identification from the other end and the other end can return MyIdentityResponse (extends GenericSuccessPacket) in order to respond to this request.

The CommandPacketRequest is the packet which takes a command and serializes it and converts the command to an HTTP Message. Similarly, it gets an HTTPMessage and constructs a command from the message body.

/// <summary>

/// HTTP Request Packet which encapsulates a distributed command

/// </summary>

public class CommandPacketRequest : PacketRequest
{
    private RemoteCommand _Command;

    private static BinaryFormatter _Serializer = new BinaryFormatter();

    public RemoteCommand Command
    {
        get { return this._Command; }
        set { this._Command = value; }
    }

    public CommandPacketRequest( HttpMessage msg ) : base( msg )
    {
        // Deserialize the message body and extract the command from it

        MemoryStream stream = new MemoryStream( base.Body );
        this._Command = _Serializer.Deserialize( stream ) as RemoteCommand;
    }

    public CommandPacketRequest(RemoteCommand command)
    {
        // Serialize the command

        MemoryStream stream = new MemoryStream( 256 );
        _Serializer.Serialize( stream, command );

        // Store the data in the body of the message

        base.Body = stream.GetBuffer();
    }
}

The following are the classes which provide Distributed Command execution over HTTP protocol:

Figure 10 - DistributedCommandExecutorHTTP and Transporter

What is in the sample application

In the sample application, you will get the following:

  • A ready to use framework for Distributed Command Execution.
  • A very powerful communication library which offers bi-directional HTTP communication.
  • A ready to use chat application to chat with others in the network.

Reference

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here