Problem
When do you define interfaces for a distributed system?
Forces
Resulting Context
Related Patterns
Known Uses
Example
David, I left your initial comments in-tact and added a pattern stub up top. Great job on hatching this one! --PhilipEskelin
When do you define interfaces for a distributed system?
In some recent work, I put a service together as follows:
InterfacesLast seems (to me) to be an obvious way of approaching distribution, and a good candidate pattern. It's one way of trying to keep distribution interfaces amenable to change (I'm confident in the above system that I could change the interfaces - in clients and servers - almost as quickly as I could change the code). Defining interfaces first needn't lead to the sort of tangle I've described, but it's harder - you have to forget the interface for a while.
-- DavidHarvey
WHOA! Not only have we seen this to be true, but we found it in the same domain and technologies (options trading, CORBA and Java). We did things in the following order:
(1) Design a clean model (we happened to use UML and Rose with CRC and Use Cases)
(2) Prototype the model (rapid prototyping similar to XP) -- the idea was to get to a level of granularity that most things were extremely well defined.
(3) Design the interfaces (They were all instances of the FacadesAsDistributedComponents pattern) for distribution.
(4) Refactor the model so that it fits cleanly into the new services interfaces designed in Step 3.
I'd like to nominate this one for inclusion in ComponentDesignPatterns!
I concur! Interfaces always change along with requirements during the project lifecycle. Can't wait to start filling this one in...see changes above...
I find it the other way round, at least for single-address-space frameworks. That is, I define AbstractInteractions, encode them as interfaces and then define components that play roles in those interactions.
Or are we in agreement? Your steps (1) and (2) seem to be about discovering and refining the AbstractInteractions. You then encode them in step (3) and define components that use them in step (4).
Perhaps this pattern is a form of, or follows on from, or leads into, AbstractInteractions...
Also, I'd say that the problem could be generalised to "when do you define interfaces between components?"
--NatPryce.
Nat, I find that the two differ. The way you've defined it for single-address space systems is basically what KenAuer and I were talking about in BuildInterfaceImplementationPairs. That really is about defining the abstract interactions. However, the problem that DavidHarvey pointed out occurs when you try to do this in multi-address space systems. In that case, the "big" objects that you WANT for distribution become monolithic, huge "God" objects. The idea of "design once without distribution in mind, using AbstractInteractions as necessary, then refactor that first pass with distribution in mind" seems to cover both parts.
Nat and Kyle, your viewpoint contrast tells me that defining ProcessBoundary early in your project lifecycle -- determining which components are in domestic space and which ones are in foreign space -- allows you to know what interactions deal with foreign space components.
Hence, a resulting context aspect of ProcessBoundary would be utilizing AbstractInteractions with domestic component interaction, and GoingThroughCustoms and developing InterfacesLast with foreign component interaction.
If this pattern is about distribution issues then I think it needs a different name. AbstractInteractions are not limited to a single address space, and the term "interface" does not necessarily mean an IDL interface providing TransparentDistribution. One can codify AbstractInteractions in terms of Java interfaces or Java Remote interfaces, C++ abstract classes, CORBA IDL, MIDL or whatever.
This pattern seems to be about delaying the design of component interactions between address spaces until as late as possible, while ProcessBoundary seems to be advocating making those decisions as early as possible. I think that fleshing out the forces of the two patterns is necessary before we can compare the two.
--NatPryce
Nat: Fine, I'll buy your thinking that we should flesh out forces more. But ProcessBoundary is focused on the problem of "where do my components go?" and making that decision early. You're thinking about space. And in making that decision, you are not coupled to immediate or delayed definition of interfaces or explicitly addressing interactions. But that is a solid player in resulting context.
I think you're right that AbstractInteractions spans both domestic and foreign spaces. I think GoingThroughCustoms (whether it's a force or pattern) and InterfacesLast are more polar to the foreign space.
I still don't "have" this pattern...
If this pattern is to be used within the context of ComponentBasedDevelopment, then surely the components are being used within a framework (we've bashed this one out in ComponentDesignPatternsContext and ComponentDesignPatternsDiscussion). Frameworks are defined in terms of AbstractInteractions between components, so the interfaces have to be defined early on, before components are implemented. As well as logically matching what we have discussed previously, this matches my experience of developing frameworks for both single-address-space systems and distributed systems.
It seems that this pattern is being used where there is no framework for distributed components. If that is the case, then why?
Kyle said that the interfaces were facades. Is that true for David and Philip, as well? If so, then it is pretty obvious that interfaces come after the domain objects because facades usually come after the objects that they hide. Although once you have a facade, the subsystem behind it can be refactored and you can end up with new objects. Anyway, is this just a special case of FacadesAsDistributedComponents, or is it a different pattern?
I get the impression this is a ProcessPattern more than a DesignPattern. I'm trying to understand it. It seems similar in some respects to EndGame, written up in [1]. The key sentences in EndGame are:
"We created these four zones of design.
[Comment from AnonymousDonor that COM requires interfaces to be defined first, retracted by author.]
If you go back to the beginning you'll see that this is referring to something different than what you're thinking. This pseudo-pattern is about the emergence of components from objects. It shows that you can't always do top-down component design because you're not going to be sure what the components are. As the disagreement between Nat and I showed this tends to arouse strong feelings.
For instance, think about your COM and C++ example. Suppose that you started with a COM interface. It had a big honking C++ class that implemented it. Over time someone read "Refactoring" and decided to cut the big C++ class apart into multiple smaller classes. People found that that made the individual parts more easily digestible, and started using the individual classes in places where they didn't use the original COM component. Someone else came around and said "that's silly" so they defined a set of COM interfaces on the new classes.
Voila'. InterfacesLast.
I don't agree: you are working with interfaces all the time. In your example above, you start off with one interface and later refactor it into many interfaces. However, at all times you are working in terms of protocols between components defined in terms of interfaces -- AbstractInteractions. In most cases, it is uncommon to start of with a single interface because one's design effort is concentrating on the protocols between components and these can only be defined by multiple interfaces in current languages.
I think InterfacesLast is a misnomer. This is a refactoring pattern for ComponentBasedDevelopment.
--NatPryce.
COM and CORBA books tell you to define your interface first, with IDL, and then do the implementation. This can make a lot of sense for large multi-team projects with BigDesignUpFront -- you really want to keep inter-team interfaces relatively stable.
However there's a trade-off:
-- JeffGrigg
On refactoring: COM and CORBA interfaces >can< be refactored. But you have to be more careful if people outside your team are using the interface. (...unless you like it when your application crashes.) But you can gracefully provide backward compatibility in COM during the transition period -- by supporting both old and new interfaces at the same time. (I'm told there are CORBA design patterns that support this too, but I haven't been able to find them.) -- JeffGrigg
This page mirrored in ComponentDesignPatterns as of April 29, 2006