Need some thoughts on designing an adapter based library

I am building an http client library (see here for source) that wraps some of the well known libraries (such as HTTPoison) to provide the ability to build composable http requests. I originally built this with a hard dependency on HTTPoison and I have now split this out into an adapter based architecture, however I am unsure on my approach so far.

Currently my adapter doesn’t have to depend on the generic library as it only has to implement one public function that takes a single parameter. I’m unsure if this is my OO ways kicking in, but I feel as though I should have a strongly defined interface that adapters are forced to implement, maybe a protocol or a simple base module that the adapter has to use.

Anybody have any thoughts?

I don’t have experience doing this, but my take would be to use behaviours to enforce a specific interface. If you have the time, you could always take a look at Ecto (adapters). @michalmuskala once wrote how to implement a new Ecto adapter, it sounded quite good:

1 Like

Take a look at a package that I put together called Hora. It’s a simple adapter based password hashing library.

You can see how I defined the interface here:

And one of the implementations here:

Like @Tuxified said you probably want to look at Elixir’s behaviours for defining the interface.

1 Like

Many thanks to you both, a behaviour is exactly what I need.

Hmm… so it’s going to be a library, that calls an adapter, that wraps httpoison, that wraps hackney. I’ve had trouble with setups like this before because it’s difficult to debug, and there’s a lag between new features appearing in the base library and getting support for them in the layers of wrappers. YMMV though.

A similar example is bamboo, which has a bamboo_smtp adapter, which wraps gen_smtp. Sadly a lot of gen_smtp features are not exposed through the adapter. It’s a good example though if you’re interested in behaviors and adapters, its purpose is also very similar (make email construction composable).

Yes, sadly a wrapper of a wrapper, etc…

But this is what prompted me to move to an adapter setup as I intend on implementing a hackney adapter which will at least remove one level of wrapping.

I have also felt this pain more than once, usually when trying to use a newer library in a larger framework such as phoenix for example. I guess there is a line to draw before each library just re-implements the functionality of the others it would otherwise depend upon.