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!
 

RW

Hi, Ingo!

I didnīt guess, weīd differ on my first easy question that much :-) But thatīs ok for me. So letīs clarify the local contract question first:

Splitting the operation interface into one for sending email and one for receiving email is just fine with me. Whether Iīd do it, probably would depend on the more concrete scenario. To generally say, because sending and receiving emails is defined by two protocols (SMTP, POP3) a component concerned with both operations should reflect this distinction too, I donīt find all too convincing. Why not view it from the component clientīs perspective: The client does not want to know about such details. It just wants to deal with email messages, sending them or receiving them. Why bother with two interfaces? For the client sending and receiving are two sides of the same medal like reading from and writing to a stream.

Using a less brittle identifier for an email message to retrieve than an int sure is a good idea. I did not want to suggest, my int was an optimal choice. I just tried to keep the example simple. And since POP3 uses an index to retrieve messages during a session, I used it too.

Also I agree with you on a pair of methods like BeginSession()/EndSession() (or the usual Open()/Close()). By no means I thought my email interface was complete. Those session-related methods, though, did not add anything to the problem I was concerned with. Thatīs why I left them out.

Ok, now with this out of the way, letīs come back to my original first question I was so sure weīd easily be of the same opinion: Is it ok to use just interfaces in a component contract?

Your answer essentially is "It depends!" :-) Well, what can I say... Mostly in life this is an answer to be on the safe side. But it bounces back the decision to the one asking. Although I acknowledge, a programmerīs life is not easy, I think it does not need as difficult as usually perceived. So as a technology evangelist I want to give developers more guidance with easy to remember "rules". No, I donīt want to limit their choices, I donīt want to constrain them - but I want to make their life easier for 90% of all cases.

A while back you pointed me to the Japanese concept of Shu Ha Ri [1] so let me use it to put my position in context: My personal opinion is, many, many developers are in the Shu and maybe Ha stages. Only few have already reached the Ri level. Very few are masters, many, many are apprentices. This is not to belittle the hard work developers invest in their work. Instead for me it is the result of my perception of our industry: Our industry is still immature compared to the automobile or computer hardware manufacturing industries. And one of the singns of immaturity is a lack of rules. The patterns movement was an overdue reaction to this state of the industry. But we still need more rules, heuristics, and agreement on what one should know ("canon of knowledge"). However, what follows from a lack of rules? Itīs not everyone being a master - but rather the contrary: most everyone still being an apprentice (despite all personal experience). But I digress... ;-)

So, yes, I can agree with your "It depends!" answer - but thatīs an answer on the Ri level or software development. Of course you can use other types than an interface to define a component contract. For example, use an enum to pass a priority level to the sending method. Or due to a lack of widespread use or O/R Mapping define a Typed DataSet as part of a contract. But doing so for me is not where you should start your thinking. Instead Iīd like to put forward the Shu-level rule of: Compose your component contracts from interfaces.

And hereīs why: Because there always will be at least two implementations. One implementation is the "real one", the production component, the one the client uses in the final release of the software.

The other implementation is - a mock-up implementation the client uses during development. And if you are serious about testing, you will use mock-ups during development.

To make more clear, what I mean, hereīs a layout scetch of a small software system, which uses the email component, as Iīd set it up:

client.sln
  client.csproj
    references contracts.dll
    client.cs
  mockup.emailmanager.sln
    references contracts.dll
    sender.cs
    received.cs 

contracts.sln
  contracts.csproj
    emailmanager.cs

emailmanager.sln
  emailmanager.csproj
    references contracts.dll
    factory.cs
    smptsender.cs
    pop3received.cs
    exchangereceiver.cs

You see, there are two components (client and email manager service) and a contract. All are set up in different Visual Studio solutions. Doing this physically and visually separates the componentsī code. This allows for easier simultaneous work on the implementations and serves decoupling, because you cannot simply peek into another project in your solution to gain some knowledge about implementational details of a components which should not concern you.

Now, while developer Charles is working on his assignment to implement the client, he is in no way dependent on what developer Eve is doing. Whether Eve has already started developing her email manager component or is busy debugging it is of no real interest to Charles. He is not hampered in any way, because he does not rely on Eveīs work. Of course, in the end Charles wants to see his client to run faultless using the real email manager component from Eve. But for the most time Charles can do without it.

Instead Charles develops his own little email manager (see the mock-up project in client.sln) and tests his client againt it. Once heīs satified he simply replaces the instanciation of mock-up types with the instanciation of types from the real email manager component. (How to make those instanciations easy to change is a question of its own. Maybe we need to talk about it later.)

Do you agree this is how one should go aboud developing a software system? If yes, you need to agree, there always are more than one implementations of any component. (The above example equals a component with an assembly just for simplicityīs sake. A Component of course can consist of more than a single assembly. But that has no impact on my argument.)

Also, I disagree the .NET Framework "bind[s] by method name". It binds by method name plus fully qualified type name plus (!) assembly name. Thatīs what the reference section in an assemblyīs manifest is for. It provides a dictionary of assemblies an assembly is dependent on. And all type usages within an assembly refer to that dictionary. So when running a program and calling a method the CLR first needs to look up the type and then the assembly. If itīs not in memory then it searches it using an algorithm (so as to not rely on a central registry). That means: When I call a method on a class my code expects it to reside in a certain assembly.

As much as I like the binding by name compared to the earlier indexes into VTables it does not solve all problems. To be more precise: It does not allow me to swap between implementations, if they are located in different classes, in different namespaces, in different assemblies. Assembly references in the manifest are static. You can replace an assembly by one with the same name, thatīs fine. But I find that very limiting.

Thatīs why I think, a client should not bind to classes, but to interfaces. A class always implies a certain implementation in a certain assembly. But not an interfaces. So with interfaces in contracts a client is independent where during runtime an implementation is loaded from.

Of course, this entails a factory of some sort. You correctly pointed that out. If this factory should be specific or generic, is not important for our discussion at the moment. Also, the question how the factory can be discovered or how a factory maps an interface to an implementation, is not important at the moment. Dependency Injection or Microkernel frameworks like Spring.NET [2] or Picocontainer [3] provide answers to this question.

Now for abstract base classes: They are similar to interfaces and Iīm not all against using them in contracts. But Iīd argue they tempt to fill them in with at least some implementational details (which would not be in line with what I layed out above) and they obviously confine implementations to a specific root class and class hierarchy. I find that limiting.

So, where are we? In general we agree on many things. And youīre not opposed to describing data entities using interfaces. However, we disagree on the importance of interfaces in component contracts. I think they are more important than you think they are.

What do you say? Shall we move on to my real questoin which is concerned with service contracts as opposed to component contracts? Or to be more precise: It has to do with the relation between service contracts and component contracts.

[1] Shu Ha Ri, http://www.wadokarate.co.uk/shuhari.htm
[2] Spring.NET, http://www.springframework.net/
[3] Picocontainer, http://www.picocontainer.org

IR

Hi Ralf,

I think that we actually agree on most parts of the topic at hand. I think the main difference is that I don't fully believe (anymore) in the existence context-free "best practices". That's why I always try to create some kind of context around a suggestion.

If the context of the solution is a project in which the plan has been made to use TDD and mock-objects, then absolutely, yes! I would totally follow - and recommend - your approach. But I think that this is only one possible option out of lots of different ways to implement projects [1]. It is not necessarily (context free) good or bad to use mock ups. And the approach can be quite costly in larger enterprise systems -- let's say you have a planned set of 200 public classes. Instead of implementing just these 200 artefacts, you will have to create 200 additional interfaces and a total of 400 classes plus factories (or use one of the many available containers ... each of which has its own set of issues). Even though this could (and quite likely even would) improve the quality of the resulting code and the inter-team or inter-developer communication, I think that one has to balance the costs.

But if we agree that - if project decisions and constraints - allow the use of TDD and mock ups, then yes, I think we can shelve this part of the discussion and continue on to service interfaces vs. component interfaces.

Cheers,
-Ingo

 

[1] The problem is that there are a lot of ideas like the following in which a majority of people agree that they are context free "good things":

  • Explicit interface definition
  • Explicit message definition for web services
  • Test Driven Design
  • Mock Objects
  • Continuous risk assement
  • Agile processes with quick iterations
  • etc. etc.

Even though all these might be good things on their own, I think a boilerplate recommendation to follow all these concepts for each and every application might be wrong. After all, even though each concept in this list might increase the robustness of the resulting code, *all* of them have an associated cost - at least in terms of necessary time and effort. I think it has to be a business decision (even if this decision is often made at different levels and not necessarily by the project owner) as to whether a certain approach should be followed or whether the project is willing to take the risk of not following it. (for example "If you only have two developers, it might be beneficial if they create tests for their own code, but mock-ups only for the code with which the other developer will interface")

RW

There surely are fewer "context free best practices" than many like to think. But Iīd like to distinguish between "best practices" and "basic rules". Many things can be done a lot of ways and you need to choose, which path to follow. Nevertheless every industry has established rules how you do things most of the time - without looking left or right. But thatīs maybe a topic for another conversation ;-)

Due to a lack of rules, though, developers traditionally try to be very (!) creative all the time. That simply costs time and money - sooner or later. What they are all striving for is "perfection", which I find too lofty a goal in our times of tight schedules and tight budgets.Hereīs an example of the kind of rules I mean: Think of GOTOs. Since long they are considered harmful [1]. Every introductory programming texts warns of GOTO statements. Thatīs what apprentices learn, thatīs the appropriate message for 99% of all algorithms to be developed. Nevertheless GOTOs are right choice in rare cicumstances and in the hands of an expert.

Ok, maybe the rule "Use only interfaces in component contracts!" I stated earlier is a little bit too restrictive. So let me put it more realistic: "Leave out as much implementation details as possible from a contract!" Ok? That means, you should try very, very hard to resist the urge to put classes in a contract. Sometimes, though, classes are ok in a contract. A class without any methods for example (Data Object pattern) would be ok for me. Also an enumeration type or a delegate can of course go into a component contract. Even a Typed DataSet is fine, because its code is generated and thus just looks like a Data Object defined using a designer tool. However, Iīm a little skeptical about abstract classes. If you can keep the free of any method implementation, they are ok. But such classes tend to accumulate code over time. "Hey, letīs factor out this code from the derived classes and put it in the abstract base class." That I would not like to see in a contract - unless, maybe, this code changes only very, very, very rarely.

Now letīs get on with my original question and leave the TDD-discussion for some other time:

Ok, what was on my mind? After agreeing on the value of interfaces in component contracts (which you concede at least for TDD projects ;-), I wanted to first get agreement with you on how a service contract should look like. Then I wanted to move on to how to map a service contract onto a component contract.

So, how should a service contract look like?

As far as I can see, a service contract looks rather different from a component contract. Either the notation is completely different because in a SOA world you use WSDL/XML Schema to describe a SOA service contract. Or, when you translate the WSDL/XML Schema to a GPL like C# you end up with classes for the data to be passed to/from the service. Hereīs an Indigo (aka Windows Communication Framework) version of our email example:

namespace Remote.EmailManager.Contract
{
  [ServiceContract(...)]
  public interface IEmailSender
  {
    [OperationContract]
    void SendMessage(EmailToSendDC msg);
    ...
  }
 
  [DataContract]
  public class EmailToSendDC
  {
    [DataMember]
    public string subject;
  }
  ...

The service still is defined as an interface, but the message has become a class. Do you agree, this is the way to do it in Indigo? (most of the times ;-)

[1] Edsger W. Dijkstra, Go To Statement Considered Harmful, http://www.acm.org/classics/oct95/

IR

Absolutely. In a purely Indigo world, a service contract would quite likely look exactly like the one you have described.

But I think there's also something which we must never forget: the representation of a service contract on one side of the service boundary is up to the implementation. I could for example take the service contract defined above, turn it into WSDL, send the WSDL to the client, but implement the service in a totally different way:

[ServiceContract]
public class TestService
{
  [OperationContract(Action="*")]
  public Message Process(Message msg)
  {
    // ...
  }
}

I think in a services world, it is up to the sender or receiver of the message to define the level of abstraction (or framework support) the developers wants to receive. If she just wants to work on the level of a Message, that's fine! If she wants to work on the level of RPC, that's her choice as well.

It somehow reminds me of the strong-tagging idea you have suggested a while ago for cross-component communication. How about you?

 

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