8

As part of the dependencies that the project I'm working on has, we use several core services. These services, to which we can't make big changes, are a big mess. Depending on the method we invoke, we need to convert our parameters (and return values) to different encodings, locales and time zones.

Since we generate these parameters in multiple places in our own code, we perform these conversions in multiple places. Some times where we generate them, before passing them around in our side; some times right before invoking the method in the core service. So the mess is spreading throughout our code, and I want to introduce a layer to isolate it.

My question is what's the best approach for that. Initially I thought of just creating a service/method corresponding to each service/method that we need to use. These methods would simply perform the conversion, delegate to the core services and perform conversion of the return value. But this seems somehow unwieldy.

Then I thought of using annotations, but I'm not completely sure of how to use them. And as I understand it, ideally I would need to annotate the invoked method. For instance, I could annotate the parameters with @converToUtc, and do the conversion in the annotation implementation. Is this correct? Of course, this is hard because it's not our code and it'll break code currently using those methods in projects other than our.

What's the best approach for this situation?

  • 6
    A common term for this is anti corruption layer if you want to look it up. – Esben Skov Pedersen Sep 17 '15 at 10:56
6

What you're trying to do is implement the facade pattern. Basically you want to put a simpler face on the core code. You'll probably want to create a set of classes that provide a 1:1 mapping for each core class, then use the facade classes in place of the core classes. If the core services are just a bunch of methods in some big monolithic class, then you might consider breaking them down by functional domain (e.g., authentication, data access, business function, etc.) and having a different facade for each domain. Your facade should present an interface that makes sense to your code, and handle all the mapping and data conversion required to communicate with the core services.

6

The problem you are facing is an instance of a general problem that we quite often face in Software Engineering: Molding the tools to bring them to our specific problem domain by means of abstraction/conversion layers.

You have an application which solves a problem. The entities that it contains, manages, and interacts with are entities that belong to the domain of the problem. They are all expressed in terms that are useful for solving the problem at hand, and they are "friendly" in that they allow you to focus on solving the problem rather than wasting time and polluting your code with conversions that have nothing to do with the problem at hand.

For as long as you own all of the code, that's all fine and dandy; but when you bring third party tools (libraries) into the picture, these tools have most probably not been written to work within your problem domain, so they require conversions which are often distracting, cumbersome, and error-prone.

What usually happens is that the inconveniences are minor, and we just cope with the tools that we are given. But when the inconveniences are major, so much that they make our daily life considerably more difficult, or that the end result is more fragile and more error prone, then we do sometimes introduce abstraction/conversion layers between our code and the tools that we use.

These abstraction/conversion layers offer services to our code which are expressed in terms of our problem domain, and they delegate to the third-party tools, performing all the necessary conversions along the way. When doing so, these layers tend to also abstract as much as possible of the peculiarities of the tools that we use, so that we can, in theory, replace a tool with a different tool by modifying only the abstraction/conversion layers, with no need to modify our core logic.

Regarding annotations, I do not see how they can help here. First of all, if you do not own the source code of the target interfaces, then you cannot add annotations to them. But even if you could somehow add annotations to the target interfaces, for the annotations to work you would have to embed some intermediate layer between your code and the target interfaces, which intercepts the calls, examines the annotations of the target methods, and performs the necessary conversions. You could, perhaps, use the Spring Framework or something like the Castle Proxy's Interceptor mechanism to magically embed this intermediate layer between your code and the libraries, but it seems to me that you could just as easily write the intermediate layer in a traditional way, having intimate knowledge of the target interfaces, performing the conversions in a hard-coded, straightforward fashion, and also abstracting the interfaces of the libraries to suit your needs.

0

First of all, you (or your team) need to agree on a "standard" format that you will use in your own code. For example:

  • Encoding: UTF-8
  • Locale: en_UK
  • Timezone: UTC

Only after, you could write a layer that will adapt the values to the formats requested from your dependancies (I don't think it's unwieldy).

As Mike Nakis said, I also don't see any benefit to use annotations to solve this problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.