Abstraction in Elixir

Ritresh Girdhar
2 min readApr 22, 2022

👋 Viewer, I am sharing knowledge on how to bring abstraction between controller and downstream service in Elixir. It might help those developers who are new to Elixir and coming from the OOPS background.

Photo by kazuend on Unsplash

In OOPS, the Abstraction principle is easy to understand and implement. We have interfaces/abstract classes and concrete classes to implement those. Example:

https://www.sourcecodeexamples.net/2017/12/chain-of-responsibility.html

We could achieve a similar abstraction in Elixir via @behaviour

Let’s take an example of the Offer Management System, getting offers from downstream systems based on some business logic.

defmodule Offers do
@doc """
Get offers from various downstream systems
"""
@callback fetch(OffersRequest) :: {:ok, OffersResponse} | {:error, String.t}
end

Modules adopting the Offers behavior will have to implement all the functions defined with the @callback attribute (similar to the interface implementation in OOPS)

DownstreamOfferSystem1 & DownstreamOfferSystem2 will be invoking their respective endpoints.

defmodule DownstreamOfferSystem1 do
@doc """
Get offers from various Downstream systems1
"""
@behaviour Offers

@impl true
def fetch(%OffersRequest{}) do
# invoke downstream system
......
# parse response in OffersResponse
end
defmodule DownstreamOfferSystem2 do
@doc """
Get offers from various Downstream systems2
"""
@behaviour Offers

@impl true
def fetch(%OffersRequest{}) do
# invoke downstream system 2
......
# parse response in OffersResponse
end

Let's add the Dynamic dispatcher in `Offers.ex`

defmodule Offers do
@doc """
Get offers from various downstream systems
"""
@callback fetch(OffersRequest) :: {:ok, OffersResponse} | {:error, String.t}
def fetch!(implementation, %OffersRequest{} = or) do
case implementation.fetch(or) do
{:ok, %OffersResponse{}} = resp -> resp
{:error, _} = e -> e
end
end
end

Configure the default offer system in the ‘config.exs’

config :app, active_offer_client DownstreamOfferSystem2

Parent Function will look like this — (return offers from DownstreamOfferSystem2)

defmodule Service dorequire Logger
alias Offers
def get_offer() dowith {:ok, client} <- get_active_offer_client,
{:ok, req} <- get_offer_request,
{:ok, %OffersResponse{} } = resp <- Offers.fetch(client,req) do
respelse
{:error, err} -> Logger.error(" caught exception ... fallback handling #{inspect(err)}")
end
defp get_active_offer_client do
{:ok,Application.get_env(:app, :active_offer_client)}
end
end

Thanks for reading!

--

--

Ritresh Girdhar

Father || Coder || Engineer || Learner || Reader || Writer || Silent Observer