10

We have a dependency to a third-party service which exposes a gigantic interface of which we only need like 3 methods. Additionally, the interface changes frequently...

I've decided to wrap the interface in a class in our project and only expose the methods that we need.

But I'm unsure how I should handle the return values... The interface returns an object of type Storage. We internally have a type StorageModel which is our internal representation of a Storage.

What would you return in the mapper: Storage or StorageModel? We have a DataService StorageService which get a dependency of the wrapper injected.

Currently I'm doing it basically like this:

public class StorageService 
{
    private readonly IExternalStorageWrapper externalStorageWrapper;

    public StorageService(IExternalStorageWrapper externalStorageWrapper)
    {
        this.externalStorageWrapper = externalStorageWrapper;
    }

    public StorageModel GetStorage(int storageId)
    {
        return this.externalStorageWrapper.GetStorage(storageId).ConvertToStorageModel();
    }
}

public class ExternalStorageWrapper : IExternalStorageWrapper
{
    public Storage GetStorage(int storageId)
    {
        using(var ext = new ExternalStorage())
        {
            return ext.GetStorage(storageId);
        }
    }
}

What would you say:

  • Is it good like above, that the wrapper returns the external Storage object and the internal StorageService returns the internal StorageModel?
  • Or would you return a StorageModel in the wrapper already?
  • 2
    Why do you name it a wrapper? Better look for the facade-, bridge- and adapter-pattern. As I understand, a wrapper would provide all methods of the third-party service - but that is not what you want. – Tobias Otto Feb 16 '18 at 12:13
  • Recommended reading: Why is asking a question on “best practice” a bad thing?. – user22815 Feb 16 '18 at 16:01
  • @TobiasOtto a wrapper doesn't need to expose all of the wrapped object's behavior, see this article in "A limited wrapper". – guillaume31 Feb 19 '18 at 10:35
11

In my optinion, the wrapper should deal with all things related to the external library. This means that the public interface of the Wrapper must not name any external types.

Mapping external types to the respective types of your application is part of the wrapper's duties. If this is not a trivial operation, then you may use the various tools available to decompose the problem, e.g. injecting a translator object. However, the translator must still be part of your wrapper module, and no other part of your application may depend on it.

This way, the rest of your application is completely immune not only to changes in the library, but also to replacements of the library for another one.

3

I've decided to wrap the interface in a class in our project and only expose the methods that we need.

That's ok. This is also known as Adapter.

You choose the Adapter pattern, so the aim here is transforming one interface (library model) into another (domain model). So, if something from the former reaches the domain model, the adapter is failing to its purpose.

According to the previous arguments, the adapter should return the StorageModel.

Ultimately, your domain "speaks" a specific language, where Storage is a stranger.

But I'm unsure how I should handle the return values...

The key here is to know for what reason you are wrapping/adapting the library.

Adapter, Decorator, Facade patterns might have similarities but they are fairly different. As different as the problems they solve.

That said, you might be also interested in:

1

You can't effectively wrap a library by duplicating it.

What you should wrap is you usage of the library and that means not exposing it objects, in this case Storage. Don't try to duplicate them either.

Use the library, but keep it contained. So in your case, assuming your using the StorageService to store things you should wrap it in repositories

MyPocoObjectRepo
    MyPocoObject GetObject(string id);

where MyPocoObject is entirely your data and business logic. Not a duplication of Storage or a DataReader or anything

0

The answer is that it depends on whether or not you ever need to access Storage directly from a class that isn't StorageModel.

If you're going to wrap the library, it makes sense to also wrap the object returned by it to let you future proof changes made by the library in the future. However if you ever need to use Storage directly, it means there may be need to return Storage according to the situation. An argument could be said for obliging Storage usage here to be StorageModel as you likely want to remain consistent throughout your program.

I would strongly recommend you wrap both the interface and the returned object if you're not already doing so, though again, that only makes sense if you're only using StorageModel throughout your program and not Storage.

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.