Remove the business context from your services
A lot have been written and said about multiple use (or reuse depending on your definition) of services. I want to touch one aspect of this with this post.
As a general rule, the more something is generic or small the easier it is to use it in different contexts, for example Hash tables are used all over the place in a lot of programs. The Hash table is a generic container and carries very little in terms of business context so it is very easy to use it. A corollary to the above mentioned rule is that the more specific is something the harder it is to use it in different contexts. Unfortunately (from the “use” point of view) specific domain logic is exactly what we strive to have with SOA. The value of services is derived from the business value they can generate. To add insult to injury, there’s also a limitation on how small we’d want a service to be. The fact that communicating with a service requires communication over a network means that if we’ll make it too small, the overhead in getting to it (serialization, network traffic, security etc.) can out weight its utility (an anti-pattern I call nano-services)
Well, one thing you can try to do is remove the business context from the services. before you flame me about how this matches my previous statement that services’ value comes from the business value or domain know how they provide, you should note that I said “business context” and not business logic.
Let me try to clarify this with a concrete example from my current system. At xsights we provide image identification services for mobile devices. for instance when you see a movie ad, you can take a picture with your mobile, send it to us, via MMS, a specific client or video call, and we provide related information such as the trailer, where to buy tickets etc. Our initial offering supported only video calls (for business reasons irrelevant for this post). In a video call you have a constant stream of incoming video from the handset (10-15 frames per second) so we (try to) identify frames as they come. We mostly use event driven architecture over SOA so (a partial) flow looks something like the following (the events occur in the context of a saga). An extractor service listens on an RTP stream, extract and preprocess images and raises a FrameArrived event on each new frame. An Identification GW decides how to handle an incoming frame and directs it to one or more algorithmic workers (this isn’t event driven). After a successful identification the Identification GW raises a LinkFound event. And a Call Flow service takes it from there:
if we didn’t get an identification within a timeout we can ask the user to better aim the camera or whatnot (behavior controlled by the CallFlow service)
When we first added support for MMS we wanted to use the same identification logic – there’s a slight difference though: in a video call you have a constant stream of low-quality images where as in an MMS you get a single high(er) quality shot. To add support for MMS we needed to add some logic to the identifier so that it will know whether the origin of the image is an MMS message or a video call. If it is the first then the Identifier needs to raise a “failed to identify” even when it finished processing the image (the video call can use a timeout instead)
But that’s the wrong way to do it – since now we need to know which sagas are MMS sagas and which are Video call ones. Not to mention we would probably need some other “special” logic to handle clients (which indeed we needed) . If we go down this lane and add more and more business context to the identifier we make it less autonomous – even though we are using events they are no longer about the business of the service events (like “FrameArrived” from the extractor) they are system context events (“MMSIdentificationFailed”) our identifier is gaining more and more “reasons to change” and is becoming tightly couples to specific contexts. So yes, we using it over and over again but the costs for that are getting higher with each such reuse
What’s a better way? Remove the business context from the service and focus on keeping the business logic and rules. In this case that would be a NoMatchForFrame event for each failed frame. In an MMS related saga there would be a service that listens to this event, in a video call related saga no service will listen on the event*. Once the business context is removed our identification GW focuses only on its core business activity (routing images to algorithmic workers and notifying the world on success/failure. Adding support for client behavior becomes much easier in this can, in fact the identification GW doesn’t need any changes to support this scenario.
To sum this post – if you want to increase the chance to use services in different contexts you should strive to remove the context specific bits outside of the services. This will simplify the services themselves as well as increase their autonomy
* Out communications framework allows for different event wiring (or route) depending on the saga “type” so actually the event won’t even fire in a video call as our communication framework will identify there aren’t any subscribers. This is very good from the service point of view as it allows it to fire events and letting the the communications framework worry about the context. The saga initiator is the only place where the context has to be specified (I’ll expand on this in another post)