Good software architecture creates good abstractions. Define good interfaces between boundaries and hide the implementations is the goal of every architecture team. And it absolutely should be. Otherwise software becomes brittle and difficult to maintain and enhance. But, as in all aspects of engineering, it doesn't come without it's own share of pitfalls. This is especially true where performance and transaction scalability is concerned.
Not too long ago I was looking at dependencies between pools and databases during coupling analysis. I stumbled onto a dependency that made little sense. One of our transactional pools depended upon a batch configuration database. Not only did it depend upon it, it was generating considerable traffic to it. After some investigation, I discovered that a specific entity type had been augmented to support one specific batch application. This new subtype of the entity was specific to batch so the decision was made to persist it on a batch host. So far, so good. The problem came when the finder for the base type of the entity was modified to include this new subtype in the result set.
Everybody involved in this process had made reasonable engineering decisions. They just hadn't been aware that the public interface, which was not violated, would be impacted in a systemic way with this implementation change. This is the peril of good abstractions.
Of course system qualities should be part of a good abstraction. The challenge is how do you express those in a way that neither constrains or exposes the implementation? In the above example, this would be quite difficult. The performance of the transactional application wasn't really impacted. It was the coupling between transactional and batch subsystems that was at issue. So how does one express what couplings are permitted without effectively specifying implementation details (i.e. what resources may be used behind the interface).
You could easily argue that the new subtype should have been treated as a separate entity with a corresponding interface. And from a coupling perspective, you are absolutely right. The challenge with this approach though is you expose your partitioning challenges through the interface. The interface will now appear to have an arbitrary bifurcation because the motivator is to avoid coupling in the implementation. This is not a particularly exciting proposition either.
How did we solve this particular problem? We added a hint to the finder to indicate whether the application was transactional or batch. Is that perfect? Absolutely not, but it was a pragmatic solution that maintained a reasonable quality abstraction.
Does SOA make this problem better or worse? I'd argue there is nothing magic about SOA. Service providers are going to need very explicit about all aspects of their contract. Not just the call semantics but systemic qualities as well. Changes to the implementation that substantively change the performance or availability downward would have to be vetted with all consumers of the contract. Of course that is completely obvious to SOA designers.
Is it equally obvious to all service implementors? That remains to be seen. Especially when certain qualities are problematic to express with quantifiable metrics.
Technorati Tags: architecture, engineering, java, performance, programming, scalability, services, soa, software, to_read, toread, web
Comments