dorgan

dorgan

Trying to understand how to pick a Behaviour implementation

I’m trying to figure out which is the “best” way to pick a behaviour implementation. I’ve found a couple of examples in other libraries but I haven’t found the rationale behind the design choices. These are the ideas I’ve found so far, and I would like to make sure I understood everything right before continuing to write any code.

Say I have a behaviour FileStorage.Store, and I have multiple implementations for that behaviour: Store.Local, Store.S3, Store.GCS, etc. The module FileStorage is the public API of the feature, and it somehow has to select an implementation.

Reading a bunch of libraries’s source code I’ve found two or three general approaches to this:

The first one is using a private function to get the implementation from the application env:

def action(args) do
  impl = get_impl_from_application_env()
  impl.callback(args)
end

This means that there is a global configuration for the module, something in the lines of:

config :my_app, FileStorage, store: FileStorage.Store.Local

An example of this approach is the Arc library.
This works, but has the limitation that if I need to add a secondary storage, I have to reimplement the public API module with a different config.

The second approach seems to mitigate the limitations of the previous one:

def action(impl, args) do
  impl.callback(args)
end

Now that the implementation is passed to the API function, it’s easy to use different implementations in different places. It enables you to do something like this:

defmodule ModuleA do
  alias FileStorage

  @store FileStorage.Store.Local
  def action(args) do
    FileStorage.save(@store, args)
  end
end

defmodule ModuleB do
  alias FileStorage

  @store FileStorage.Store.S3
  def action(args) do
    FileStorage.save(@store, args)
  end
end

Now the problem is that there’s a lot of repeated code since each module that wants to use FileStorage functions has to pass it the implementation it wants it to use.

I’ve seen libraries use a bit of metaprogramming mixed with a new behaviour to avoid this. The most notable example is Ecto’s Repo. So you’d have something like this:

defmodule FileStorage do
  @callback action(args :: any()) :: :ok | {:error, any()}

  defmacro __using__(opts) do
    quote do
      @behaviour unquote(__MODULE__)

      store = get_store(unquote(opts))
      @store store

      def action(args) do
        FileStorage.action(@store, args)
      end
    end
  end

  def action(impl, args) do
    impl.some_function(args)
  end
end

defmodule ModuleA do
  use FileStorage, store: FileStorage.Store.Local
end

defmodule ModuleB do
  use FileStorage, store: FileStorage.Store.S3
end

defmodule ModuleC do
  def some_function(args) do
    FileStorage.action(FileStorage.Store.GCS, args)
  end
end

Now I can have multiple modules that can configure the way FileStorage is used, both by useing it and by calling it’s functions directly with some specific implementation.

So in short, one can pick an implementation by:

  • Having a private function fetch it from the application’s configuration
  • Having many modules call the api module passing it the implementation to use
  • Use metaprogramming to let other modules configure the way the api is used

Is this how it’s usually done, or I am misinterpreting something?
Thanks!

Most Liked

fuelen

fuelen

I think store configuration for each module should be moved to config too, to have an ability to change it in tests to some mock. Actually, Ecto works this way.

fuelen

fuelen

No, dialyzer won’t help you here.

Where Next?

Popular in Chat/Questions Top

ariandanim
Hello all, I am still learning Elixir, then go into Phoenix, i am try search in google but find the programming phoenix 1.4, another for...
New
sadcad
I love the Phoenix and Elixir docs, but I always tend to learn faster when I watch a video of someone explaining things and then I implem...
New
RKC07
I’m new to elixir. I did some coding in python and C. I want to learn elixir for starting my career in web development. I need suggestion...
New
dopomecana
The title of this topic may sound silly at the first glance. But why I trying to ask you guys is how to go to the next level? I am curre...
New
nur
https://e.planaria.network Build a NoSQL DB, Build a Relational SQL Database, Build a Graph Database, Build a File System, Build a Bitc...
New
markdev
What are the best beginner resources for learning Elixir and OTP (not Phoenix) in 2018?
New
ca1989
Hi all, is there any up to date resource out there (blog, talk, video, book…) about deploying elixir applications using releases? In pa...
New
meraj_enigma
Hey, What’s a good resource to learn Microservices in Elixir/Phoenix? Is there any book/docs/Github repos that I can refer to? Thanks, -M
New
Chawki
Hi,i’m new to elixir. i’m searching elixir small programs to try it out my self,Is any good resources out there? Thank you.
New
svetarosemond
I’m planning on purchasing Elixir in action second edition, and I was wondering if anyone could tell me based off the first edition, does...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36128 110
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement