Agile UI Development in .NET: Model Commands
In my series on agile UI development in .NET, we have seen quite a lot so far (table of contents). But up to now, we never made a call to the model (business logic, services and so on). This is the topic of this post: Model Commands.
A Model Command encapsulates a single action hat is execute against the model. This can be a query to request data, an action that modifies data, communication with a completely different part of the system or anything else your application has to do on the model.
Let’s get back at my sample of sending a message in ProCollEE. The following code show the Model Command that sends a message:
/// <summary>
/// Command for sending messages.
/// </summary>
public class SendMessageModelCommand : ISendMessageModelCommand
{
/// <summary>
/// The message service used to send messages.
/// </summary>
private readonly IMessageService messageService;
/// <summary>
/// Initializes a new instance of the <see cref="SendMessageModelCommand"/> class.
/// </summary>
/// <param name="messageService">The message service.</param>
public SendMessageModelCommand(IMessageService messageService)
{
Contract.Requires<ArgumentNullException>(messageService != null, "messageService");
Contract.Ensures(this.messageService != null);
this.messageService = messageService;
}
/// <summary>
/// Gets or sets the message to send.
/// </summary>
/// <value>The message to send.</value>
public string Message
{
get; set;
}
/// <summary>
/// Gets or sets the channel on which the message has to be sent.
/// </summary>
/// <value>The channel.</value>
public IEnumerable<string> Channels
{
get; set;
}
/// <summary>
/// Gets the time when the server received the message.
/// </summary>
/// <value>The time when the server received the message.</value>
public DateTime? SentAt
{
get; private set;
}
/// <summary>
/// Sends the message.
/// </summary>
public void Execute()
{
Contract.Ensures(this.SentAt != null);
this.SentAt = this.messageService.SendMessage(this.Message, this.Channels);
}
}
The SendMessageModelCommand takes a IMessageService as dependency in the constructor. This service is used to send the message to the server.
The message and the channels to send the message on are set with properties. When the command is executed then it simply calls the service and passes on the information to send. Finally, the time when the server accepted the message is passed back in the property SentAt.
Note: go to UI Commands to have a look how the command is used.
Why Dedicated Command?
You may ask yourself, why I split this simple code into a dedicated command. There are several reasons:
Single Responsiblity Principle
Sending a message is for itself a responsibility. However, you may argue that its too simple and leads to unnecessary complexity. While this may be true for this simple example, model commands can be quite complex in real world scenarios.
Reusablity
Another advantage of a dedicated command is that you can reuse it in other parts of your application.
Let me make an example: in an application that manages customers there may be several places where you have to store addresses; edit a customer, edit order address, … Now you can reuse the command to save an address in these scenarios.
Batch Processing
Commands can be batched, too. Several commands can be added to a batch and executed at once. Thus simplifying mass operations.
Decouple Model From UI Layer
Model commands separate the UI layer (presenter, UI commands, …) from the domain model and act as a natural layer border that prevents change chains (a change in one place in the code results in changes through all “layers” in the application – e.g. from database up to the UI.
Command Processor
Each command is execute by the command processor and not directly by the client code. This allows tracking of all commands in a single place, e.g. log them.
Additionally, the command processor can be extended to support undo commands that can be used to undo the modifications of a command.
Conclusions
Model commands add a lot of advantages to the UI design pattern I’m advertising in this series on agile UI development and force decoupling and reusability in a project.
