Retire the Tenets

John Heintz and I continue to be in mostly violent agreement. It’s kinda like me saying “You da architect! Look at my massive scale EAI Mashup!” and having him respond “No, you da architect! The SOA tenets drive me bonkers!” Makes you wonder what would happen after a few beers. What’s the architect version of Tastes Great, Less Filling? ^[Not that you would catch me drinking Miller Lite. Ever.]

Speaking of the tenets, John gives them a good shredding:

Tenet 1: Boundaries are Explicit
(Sure, but isn’t everything? Ok, so SQL based integration strategies don’t fall into this category. How do I build a good boundary? What will version better? What has a lower barrier to mashup/integration?)

Tenet 2: Services are Autonomous
(Right. This is a great goal, but provides no guidance or boundaries to achieve it.)

Tenet 3: Services share schema and contract, not class
(So do all of my OO programs with interface and classes. What is different from OO design that makes SOA something else?)

Tenet 4: Service compatibility is based upon policy
(This is a good start: the types and scope of policy can shape an architecture. The policies are the constraints in a system. There not really defined though, just a statement that they should be there.)

Ah, I feel better getting that out.

As John points out, the four tenets aren’t particularly useful as guidance. They’re too high level (like Mt. Rainier high) to be really actionable. They’re like knowing a pattern’s name but not understanding how and when to use the actual pattern. However, I don’t think the tenets were ever intended to be guidance. Instead, they were used to shift the conversation on how to build distributed applications just as Microsoft was introducing the new distributed application stack @ PDC03.

John’s response to the first tenet makes it sound like having explicit boundaries is obvious. And today, maybe it is. But back in 2003, mainstream platforms typically used a distributed object approach to building distributed apps. Distributed objects were widely implemented and fairly well understood. You created an object like normal, but the underlying platform would create the actual object on a remote machine. You’d call functions on your local proxy and the platform would marshal the call across the network to the real object. The network hop would still be there, but the platform abstracted away the mechanics of making it. Examples of distributed object platforms include CORBA via IOR, Java RMI, COM via DCOM and .NET Remoting.

The (now well documented and understood) problem with this approach is that distributed objects can’t be designed like other objects. For performance reasons, distributed objects have to have what Martin Fowler called a “coarse-grained interface”, a design which sacrifices flexibility and extensibility in return for minimizing the number of cross-network calls. Because the network overhead can’t be abstracted away, distributed objects are a very leaky abstraction.

So in 2003, Indigo folks came along and basically said “You know the distributed object paradigm? The one we’ve been shipping in our platform since 1996? Yeah, turns out we think that’s the wrong approach.” Go back and check out this interview with Don Box from early 2004. The interviewer asks Don if WCF will “declare the death of distributed objects”. Don hems and haws at first, saying “that’s probably too strong of a statement” but then later says that the “contract, protocol, messaging oriented style will win out” over distributed objects because of natural selection.

The tenets, IMHO, were really designed to help the Windows developer community wrap their heads around some of the implications of messaging and service orientation. These ideas weren’t really new – the four tenets apply to EDI, which has been around for decades. But for a generation of Windows developers who had cut their teeth on DCOM, MTS and VB, it was a significant paradigm shift.

These days, with the tenets going on four years old, the conversation has shifted. Platform vendors are falling over themselves to ship service/messaging stacks like WCF and most developers are looking to these stacks for the next systems they build. Did the tenets do that? In part, I think. Mainstream adoption of RSS was probably the single biggest driver of this paradigm shift, but the tenets certainly helped. Either way, now that service orientation is mainstream, I would say that the tenets’ job is done and it’s time to retire them. Once you accept the service-oriented paradigm, what further guidance do the tenets provide? Not much, if any.

Another InitImportantThing Approach

I thought of another approach to the InitImportantThing problem that I blogged about yesterday. I think it’s a bit harder to code, but it’s certainly explicit and avoids the magic method that Jon dislikes so much.

The crux of the problem is that ServiceHostBase needs a valid ServiceDescription in order to operate. The WCF team chose to provide said description to ServiceHostBase via the abstract CreateDescription method. But as we saw, ServiceHostBase can’t call CreateDescription from it’s own constructor. So instead, derived classes are forced to call InitializeDescription in their own constructor. Since that call isn’t enforced by the compiler, it’s easy to forget to include it. Since the exception that gets thrown doesn’t really tell you what went wrong, it’s easy to spend hours trying to figure it out.

So here’s a better approach: since the ServiceHostBase needs a valid ServiceDescription in order to operate, why not pass it in as a constructor parameter?

ServiceHostBase has a protected constructor with no parameters. But since it needs you to call InitializeDescription in your derived class constructor, it really needs the ServiceDescription, a collection of ContractDescriptions (also returned from CreateDescription) and a collection of base addresses (passed into InitalizeDescription). If these were parameters on ServiceHostBase’s constructor, it could validate that information directly, without needing abstract or magic methods.

The one problem with this approach is that the creation of a ServiceDescription is non-trivial. ServiceHost’s implementation of CreateDescription generates the ServiceDescription by reflecting over the service type. You still need that code, but now you would call it from the base constructor initializer instead. That means it has to be a static method, but otherwise it would work just fine. Here’s yesterday’s code, updated for this approach:

public abstract class Base
{
    public Base(string importantThing)
    {
        if (string.IsNullOrEmpty(importantThing))
            throw new Exception();

        _importantThing = importantThing;

    }

    private string _importantThing;

    public string ImportantThing  
    {  
        get { return _importantThing; }  
    }
}

public class Derived : Base
{
    private object _data;

    public Derived(DateTime dt) : base(CreateImportantThing(dt))
    {
        _data = dt;
    }

    private static string CreateImportantThing(DateTime dt)
    {
        //this is obviously trivial, but could be much
        //more complicated if need be
        return dt.ToLongDateString();
    }
}

This seems like the best approach to me. You remove the un-obvious magic method call requirement when deriving your own service host while still enforcing the data consistency check in the base class during construction. Best of both worlds, right?

So I wonder why the WCF team didn’t do it this way?

Feasible Service Reuse

Yesterday, I posted about services and reuse. More to the point, I posted why I don’t believe that business services will be reusable, any more than business objects were reusable. However, “can’t reuse business services” isn’t the whole story, because I believe in different kinds of reuse.

The kind of reuse I was writing about yesterday is typically referred to as “black box reuse”. The idea being that I can reuse the item (object or service) with little or no understanding of how it works. Thomas Beck wrote about colored boxes on his blog yesterday. Context impacts reuse – the environments in which you plan to reuse an item must be compatible with what the item expects. However, those contextual requirements aren’t written down anywhere – at least, they’re not encoded anywhere in the item’s interface. Those contextual requirements are buried in the code – the code you’re not supposed to look at because we’re striving for black box reuse. Opaque Requirements == No Possibility of Reuse.

As I wrote yesterday, David Chappell tears this type of reuse apart fairly adeptly. Money quote: “Creating services that can be reused requires predicting the future”. But black box reuse this isn’t the only kind of reuse. It’s attractive, since it’s simple. At least it would be, if it actually worked. So what kind of reuse doesn’t require predicting the future?

Refactoring.

I realize most people probably don’t consider refactoring to be reuse. But let’s take a look at the official definition from refactoring.com:

Refactoring is a disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior. Its heart is a series of small behavior preserving transformations. Each transformation (called a ‘refactoring’) does little, but a sequence of transformations can produce a significant restructuring. Since each refactoring is small, it’s less likely to go wrong. The system is also kept fully working after each small refactoring, reducing the chances that a system can get seriously broken during the restructuring

Two things about this definition imply reuse. First, refactoring is “restructuring an existing body of code”. It’s not rewriting that existing body of code. You may be making changes to the code – this certainly isn’t black box reuse – but you’re not scrapping the code completely and starting over. Second, refactoring is making changes to the code “without changing its external behavior”. You care about the code’s external behavior because somewhere, some other code is calling the code you’re refactoring. Some other existing piece of code that you don’t want to change – i.e. that you want to reuse.

When you refactor, you still reuse a significant amount of the code, but you’re not having to predict the future to do it. Refactoring**is the kind of reuse I believe in.

In his article, David talks about types of reuse such as business agility, adaptability and easily changeable orchestration. These look a lot more like refactoring than black box reuse to me. Unfortunately, David waves these away, saying  “Still, isn’t this just another form of reuse?”. Reconfiguration hardly qualifies as “predict the future” style reuse that he spends the rest of the article arguing against. It’s just one paragraph in an otherwise splendid article, so I’ll give him a pass this time. (I’m sure he’s relieved.)

A Question of Context

A couple of weeks ago, David Chappell posted a great article on SOA and the Reality of Reuse. When someone mentions the idea of using SOA for reuse, I cringe. David does a great job blowing the “SOA for Reuse” argument out of the water. In the future, I will just send that link rather than spending the time arguing out the point.

But something nagged at the back of my brain about that post. David starts by talking about object reuse before making the parallel to services. The problem with that comparison is that object reuse hasn’t been a failure. When was the last time you wrote a String class? A Linked List? A Button? There’s been support for Buttons in Windows since at least the 3.x days (probably longer, but that’s before my time). Whatever your OO language of choice, there’s a big collection of reusable objects to go with it.

Given his position as “famous technology author”, I’m assuming David is well aware of successes of object reuse. Furthermore, I doubt it was an accident that in his article he writes that “reuse of business objects failed” (emphasis added). While there has been success around object reuse, essentially none of those successes have been in a business scenario. In fact, there have been some high profile projects such as Microsoft’s Business Framework and IBM’s San Francisco Project that have crashed and burned been significantly less than successful.

So here’s the question: given that general object reuse has seen some success, what’s so different about business objects that causes reuse to fail utterly? Since we’re really interested in service reuse, knowing why some object reuse succeeds and other reuse fails will help us understand which services are likely to be reusable and which wont. I would say that success of object reuse hinges on context.

Wikipedia gives this definition of context: “The context of an event, word, paradigm, change or other reality includes the circumstances and conditions which surround it.” (emphasis in original) For example, the word “order” is ambiguous. If you’re using a procurement system for the military, you could conceivably be given an order to place an order. (OK, that’s silly. But you get the idea.) The word “order” has two different meanings. However, the words that surround the ambiguous term make the meaning clear. An order that you place is different that an order that you give. That’s context.

A string or a linked list or a button has very little in the way of contextual needs. That is to say you can use it the exact same way in a wide variety of environments. A business object on the other hand has significant contextual requirements, which makes reuse difficult or impossible. For example, a Purchase Order object from the above-mentioned military procurement system sounds like it might be reusable. At least until you take into account the differences between branches of the military, between ordering tanks and ordering uniforms, between active units and reserve units, etc. Once the generic Purchase Order has been specialized to the point of practical usability for a given scenario, it’s no longer reusable in the general case.

Taking this back to the service realm, likewise I figure the reusable services will be the ones with little or no contextual needs. A good example is the identity and directory services such as Active Directory and its competitors. Sure, you use LDAP not SOAP to access it, but AD is certainly both reusable and a service plus it’s in wide usage. Other candidates for reusable services my team is looking at are service directory, management and operations, business activity monitoring and provisioning.

I actually think there will be less reuse in services than there was with objects. The value of reuse of services has to exceed not only the contextual issues but also the overhead of distributed access. Calling across the network is an expensive operation – whatever’s on the other side better be worth the drive. I’m guessing for services, more often than not, reuse won’t be worth the trip (if it’s possible at all).

Update: David pointed out to me that the last paragraph of his article begins:

Object reuse has been successful in some areas. The large class libraries in J2EE and the .NET Framework are perhaps the most important examples of this.

Doh! I guess my “assumption” that David is aware of successful object reuse was correct.