The Daily Mistake: Not to Proxy Remote Services

The Unkown

“Just gimme a min, I glue that HTTP based remote service in.” – Whether the standard file_get_contents + Json *bam* or complete SOAP, a developer should have the time to wrap each (remote) data-source inside it’s own interface that is worth the name.

We have leaky abstractions everywhere, but what sucks big time is to have some obscure webserver or API fail introduce you the maximum of possible worst outcome: maintenance downtime. In short: A good abstraction does not leak, so let’s reduce it.

The point I’d like to make is easy: If you don’t put a layer between, you’re not flexible and you need to refactor far more often than it’s good for you, and I’m talking about both: development time and production time. Let’s see.

I have come out with a very simple processing for these cases. Sometimes the service protocol (e.g. SOAP) offer already some sort of abstraction that is useful, but you always need to integrate the server, so this is pretty much a generic approach.

Consider the standard use-case:

Remote Service <----> System

Each time the Remote Service fails, the system as direct consument is affected by that “behaviour”. Also the connection between the System and the Remote Service is a specific protocol (Remote Protocol, e.g. HTTP), but the “protocol” inside the System is different, e.g. it’s program code and no protocol at all, we can just be, no need to negotiate (e.g. PHP).

It’s generally that we already know when we write some code like that: We know this will fail. It’s just that we don’t care. Either because it’s okay to fail (consider that, too) or either because we don’t want to care.

How to improve this while maintaining a quick development cycle?

Being honest here, we actually have a sort of “bare” Service integrated into our System. A Bare Service makes use of a remote protocol to talk to the Remote Service. This should be a component on it’s own, it’s more or less just an Interface to the Remote Service for the programming language. Sort of a binding. It’s realy bare, it’s just not “remote” any longer. No further optimization is done, this component is really stupid:

Remote Service <----> Bare Service <----> System

The Bare Service does not do much, but it allows already pretty much: It’s the least common denominator between our System and the Remote Service. If you take care that all data from the Remote Service will go trough the Bare Service you’re fine. This is sometimes called proxy, remote proxy or similar (See Proxy Pattern).

This type of design should be pretty easy to create. The only thing you do is that you keep the protocol out of the Bare Service to the System side and that you keep System data-structures out of the protocol.

If we have a remote service that offers a JSON API probably which has an exemplary method “fetch.IDs” to list ids of blog posts, the Bare Service could look like in the following example. I often have a private helper function that works as the remote protocol endpoint, so it’s easy (!) to deal with failure, changing configuration and all that stuff. Also this reduces code:

class BareService
{
    private $config;
    public function __construct(BareServiceConfiguration $config) {
        $this->config = $config;
    }

    ...

    public function fetchIds() {
        $json = $this->remoteCall('fetch.IDs');
        if (FALSE === $json) {
            return FALSE;
        }
        return json_decode($json);
    }

    private function remoteCall($call)
    {
        $url = $this->getUrlForCall($call);
        return file_get_contents($url);
    }
}

It’s just a class with a fetchIds function that can be called from PHP everywhere. The key point here is that it’s now possible to differ between the protocol interaction and just the service interaction that is on a higher level. This still has the leaky abstractions, like, what if the remote protocol fails? However, you can solve different things on the protocol level already. For example switching from HTTP to HTTPS or to provide a private key etc. pp.. Remote Service consumers in the System do not need to care about that part any longer.

I find it important here to keep the Bare Service really stupid. It even is somehow only a list of remote procedures you’re calling. As we continue to refactor, it will become the connector to the Remote Service, the part of the application that is doing the actual data-processing with remote. If a Remote Service is not that straight forward, e.g. it needs some sessions management or similar, I normally add another layer, something like a high-level Interface which’s meaning is to provide a simplified interface to the features the Remote Service offers. This allows for more fine-grained management of changes in both behaviour and features of the Remote Service over time. This part can even encapsulate logic, like in the Remote Facade pattern. It’s actually a Facade which often is useful for services so that they keep their characteristics and we don’t build too much around them.

Remote Service <----> Bare Service <----> Service Facade <----> System

You then can easily extend your system with a caching backend, fallbacks and even a local mirror of the remote service or whatever you like. You should be able to maintain your code for quite a long time and you should be able as well to test your code w/o the remote impact as you can easily mock the Bare Service part. And this kind of testing also includes to test the remote-service as well easily.

I can’t say if this is always the best solution, however I’ve used this proceeding more than once in the past with good success especially when projects were involved you’re not always available for maintenance. And you can start to introduce this layering on the go as needed. Probably when starting to integrate a webservice, you still don’t know how the interface to your system will look like, but as things evolve you know that you should put that into the Service Facade while other stuff belongs into the Bare Service.

The wording in the images is more or less my own, I’ve linked some more common wording where I was aware of, however “Bare Service” and “Service Facade” might not be common terms or patterns, at least they are not for me. Ok, quick googling for Service Facade shows that this term is already in use in the Java world. Anyway, my post is not about any java pattern but just a description in my own words of what I’ve done. The important part here is the idea behind, not the name.

Have fun coding.

This entry was posted in Code Smells, PHP Code Smells, PHP Development, Pressed, Surviving the Internet and tagged , , , , , , , , , , , . Bookmark the permalink.

3 Responses to The Daily Mistake: Not to Proxy Remote Services

  1. Nice — I think people often forget that decoupling doesn’t stop at the persistence layer. It’s important to decouple your code from the implementation details of remote services as well.

    • hakre says:

      Thx. Well normally you only forget that the first time, because you then pretty quickly learn (the hard way) that remote services are often hard to handle and you don’t want to bind yourself tightly to them then for the future. What I wanted to highlight with this post is, that it’s possible to re-factor from the quick Remote Service integration to something more flexible in an easy way by starting to write the component I named “Bare Service” here. It’s not that invasive and you already gain a sound basis for further changes.

      That actually is the importance you named: Decouple from the implementation details of the remote service, and one detail is the protocol. Like a MySQL client library, you don’t want to care about the protocol, you only want to query some data. You normally even don’t want to care about connection handling, but that’s another story 😉

  2. Daniel Ly says:

    That’s nice. I am developing a MVC application and have a separate model directory for model classes. Usually I don’t care for façade classes (because of YAGNI) but I care for robust and encapsulated code.

    When the remote service fails, the application definitively should cope with it in a user-friendly way, and not spewing ugly error messages or showing white pages. For an optional external service there are null data containers like this: “currently not available”, and for required services like OAuth identification, I redirect to a generic user-friendly error page. Generic because the end user would not be able to fix the problem with the provided information.

    This handling is encapsulated in model classes, because the model knows the best what’s required for the application. Your post made me rethink this architecture, and I like that. Thank you.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.