ServicesResourcesConferences Our TeamWeblogsAboutContact
     

Developer Resources

  Architecture Briefings
  .NET Remoting FAQ
  Articles
  Conversations
  Tools and Samples
  Books










Interface design in distributed solutions

NavigationPage 1  -  Page 2  -  Page 3

Feedback: Tell us how you liked our thinktecture conversations!
 

Ralf Westphal (RW)

Hi, Ingo! Right now I´m pondering some publications on Component Based Development (CBD). They are supposed to bring together my own general software architecture framework - the Software Universe [1] - as well as the more concrete Software Cells [2] and Contract First Design [3,4]. So what I´m concerned with is software at all scales from small desktop systems to large distributed ERP systems. Of course those systems differ in many regards, but I think, they also share quite some fundamental aspects and principles.

On one of those aspects I´d like to hear your opinion: the definition of interfaces or contracts on two different levels: contracts between components and contracts between services. Within the framework of the Software Universe, i.e. the holarchy of the "tangible" software artifacts like Methods, Types, Solutions etc., this concerns the Contracts between Components and Applications.

The interfaces between such two artifacts are worth a closer look, because the mark the transition from stack-based to stream-based communication. On all levels from Component downwards to single Statement communication between a client and a service uses the stack to pass parameters. Methods call other Methods and pass parameters on the stack and get results back on the stack. Likewise you can say Types and Components communicate via the stack. And this is true because below the Application level within the holarchy all holons live within the same address space. So communication means calling local procedures. It´s all very simple and easy.

Now, when you look from the Application upward the holarchy, communication switches from stack to streams. Different Applications don´t run within the same address space. So in order to call a method in a another Application and transfer data back and forth, you need to employ some other means than a stack. Especially you cannot pass addresses, e.g. object references. That means, you need to marshal all data as messages to and from the other Application. This happens usually over streams hiding a TCP/IP communication.

Communication within an Application, i.e. between Components, thus is very, very, very different than cross-process communication, i.e. between Applications. Passing data on a stack is so much easier than doing the same for example over a network. RPC might look like local calls, but it is fundamentally different.

And I think, this fundamental difference between stack and stream based communication, should show up in some way in how contracts are defined for Components or Applications. Don´t you think so, too?

Now, on the side of Components I'm quite sure, contracts should be made up of interfaces as much as possible. Here´s an example: A server Component offers to send (SendMessage()) and receive (GetMessage()) emails. Message are passed in and returned "in one piece". A contract for this service could then look like this:

namespace EmailManager.Contract
{
	public interface IEmailServer
	{
		IEmailReceived GetMessage(int index);
		void SendMessage(IEmailToSend msg);
	}
	...

The contract defines the messages to go in and out as interfaces instead of classes or structs.

But before I get to my main question, let me stop here and get some feedback from you, if we at least agree this far. Ok?

Cheerio!

Ralf

[1] Ralf Westphal, "A journey through the software universe from single statement to Software Societies" in: Software Cells, http://weblogs.asp.net/ralfw/archive/2005/05/05/405728.aspx
[2] Ralf Westphal, Software Cells, http://weblogs.asp.net/ralfw/category/9899.aspx
[3] Ralf Westphal, Contract First Design und Microkernel-Frameworks - Teil 1, dotnetpro 6/2005, http://www.dotnetpro.de/articles/onlinearticle1703.aspx
[4] Ralf Westphal, Spiken nicht erlaubt: Contract First Design und Microkernel-Frameworks - Teil 2, dotnetpro 9/2005, http://www.dotnetpro.de/articles/onlinearticle1752.aspx

Ingo Rammer (IR)

Hi Ralf! Even though I generally agree regarding the usage of interfaces for inter-component communication, I have to admit that I would create your component interfaces in a slightly different way. I have noticed that you separate the interfaces to the structural data types (the messages) in a send and a receive interface (IEmailToSend, IEmailReceived), but keep the IEmailServer interface as a single interface for sending and receiving operations.

This is something I would quite likely do differently based on the observation of the real world infrastructure surrounding your application. There are protocols which only allow sending (SMTP), protocols which only allow receiving (POP3, IMAP) and infrastructure elements which allow both (Exchange Server's programmability model for example). I would therefore as a first step split this logic:

namespace EmailManager.Contract
{
  public interface IEmailSender
  {
    void SendMessage(IEmailToSend msg);
    ...
  }

(Just as a side note: passing an int for the message ID might not be sufficient as this ID would not be scoped. It is therefore not deterministic if a new message is received on the server side while your application is enumerating messages. Here, I would quite likely introduce a BeginSession() and EndSession() operation which can later be used to somehow qualify the meaning or scope of the message index. A similar thing would also be true for example for DELE operations in POP3, when the actual deletion of the message only occurs if you successfully disconnect from the server by sending a QUIT command.)

But let's get to the question of interfaces: When defining interfaces, I usually follow an approach where I identify implementations which might be variable at runtime (i.e. cases where there might be more than one implementation for a given interface). I know that there are different approaches for creating interface and class hierarchies, like the one you seem to be following which suggests that everything which is shared beyond the boundary of a component should be based on an interface. I would argue that this would in most cases substantially increase the effort without providing a tangible benefit. As long as you know for sure that there will always only be a single implementation, I think it is perfectly ok to bind directly to the implementation.

I believe that a strict definition ("always use interfaces over component boundaries") was necessary in the past, in COM for example [1], mainly because of the binary binding mechanisms used. In environments like Java and .NET (which bind by method name, not by VTable-position) this might be different. In a number of cases, I think that it might be reasonable to bind directly to an implementation or to an abstract base class instead of an interface.

In the .NET Framework for example, a lot of the calls into the base class library itself (which is certainly a component different from the main application) uses base classes (like System.IO.Stream) or directly use the exposed implementation classes (like System.Windows.Forms.Form, System.Windows.Forms.MessageBox, but even System.Web.Mail which would reflect a similar use case like the one you are suggesting.)

To summarize my approach: I usually tend to follow OO-design principles even (or especially) on the component boundary.

But even in this case, I think that one needs to evaluate whether or not using interfaces in this way makes sense. If IEmailSender is implemented by a class for which there is no specific factory available, it might not make tremendous sense. After all, if the client would have to use code like the following, the complete idea of run-time decoupling might be wasted:

public class Client
{
  public static void Test()
  {
      IMessageSender snd = new SmtpMessageSender();
      snd.SendMessage(...)
  }
}

I would therefore quite likely extend the destination component to also include a matching factory:

public class EmailConnectionFactory
{
  public IEmailSender GetSender(string protocol)
  {
     // figure out which instance to return
  }
 
  public IEmailReceiver GetReceiver(string protocol)
  {
     // figure out which instance to return
  }
}

I would then maybe even combine this with a configuration file like the following to provide for an even larger degree of runtime decoupling:

<configuration>
  <emailProtocolHandlers>
     <protocol name="SMTP">
        <sender typeName="My.Component.SmtpSender" />
     </protocol>
     <protocol name="POP3">
        <sender typeName="My.Component.Pop3Receiver" />
     </protocol>
     <protocol name="EXCHANGE">
        <sender typeName="My.Component.ExchangeSender" />
        <sender typeName="My.Component.ExchangeReceiver" />
     </protocol>
  </emailProtocolHandlers>
</configuration>

But as I said before, I simply think that there can not be a generic answer like "always use interfaces on your component boundaries" or even like "always decouple using a provider pattern" or similar.

A developer might for example just want to create a reusable SMTP sender. If he would just go ahead and create a class like the following, I think it might be reasonable for his team and employer if he'd go ahead and encapsulate it in a reusable component. Even without any factories, interfaces or other means for runtime decoupling:

public class SmtpSender
{
  public static void SendMessage(string destinationAddress, 
  string senderAddress, string subject, string body)   {     // implementation removed   } }

What do you think about this approach? To use interfaces only when you know that there can or will be more than one implementation?

[1] Even as a combination of IDL interfaces and C++ pure virtual classes.

 

NavigationPage 1  -  Page 2  -  Page 3

Feedback: Tell us how you liked our thinktecture conversations!
 






 
© 2002 - 2006 by Thinktecture, Ingo Rammer and Christian Weyer. All rights reserved. | Contact | Impressum