matiso

matiso

Idiomatic way to convert from one struct to another

My use case is the following. I have an umbrella application that clearly separates the DB access layer from the business logic. By doing this, I don’t want to directly expose the DB resources to the other applications.

Instead, what I want is to have an edge interface that will expose the DB resource, and a receiver that will do the conversion from the “DB resource” struct, to an internal POEXS (plain old Elixir struct).

I know it might be overcomplicating things, but I want to see how far I can get with this approach.

What bothers me is that I don’t see a way to directly transform StructA into StructB.

So if I have:

%DB.FooResource{name: "bar"}

And I’d like to convert it into:

%MyApp.Foo{name: "bar"}

What I’d need to do is to convert the first one into a map, and create a struct from that map.

data = db_resource |> Map.from_struct()
struct(MyApp.Foo, data)

A bit cumbersome in my opinion, especially if you get an array of those.

Is there a better way of doing this, or is it cumbersome, because it’s a wrong approach?

Most Liked

axelson

axelson

Scenic Core Team

I’d think that you’d probably want to create a MyApp.Foo.from_foo_resource/1 to encapsulate the knowledge of how to transform a %DB.FooResource{} into a %MyApp.Foo{} rather than potentially hard-coding that knowledge in multiple places. It might look something like:

defmodule MyApp.Foo do
  defstruct [:name]
  
  def from_foo_resource(%DB.FooResource{} = foo_resource) do
    struct(MyApp.Foo, Map.from_struct(foo_resource))
  end
end

Then if you have a list of %DB.FooResource{} you can do

resource_list = [%DB.FooResource{name: "a"}, %DB.FooResource{name: "b"}]
foo_list = Enum.map(resource_list, &MyApp.Foo.from_foo_resource/1)

Although if all you have is a one-to-one mapping this entire approach sounds like it is overkill (but it is an interesting experiment). I’d imagine if you’re combining multiple DB resources into one representation then something like this definitely makes sense. Or if you’re doing some type of CQRS system.

13
Post #4
LostKobrakai

LostKobrakai

Another way would be using struct(MyApp.Foo, Map.from_struct(db_struct))

anthonator

anthonator

@matiso we’re doing something similar with an umbrella app.

Here’s what we came up with.

defmodule Context.Primary.Helpers.Entity do
  def new(nil, _entity) do
    nil
  end

  def new(%_{} = struct, entity) do
    struct
    |> Map.from_struct()
    |> new(entity)
  end

  def new(data, entity) do
    struct(entity, data)
  end
end

Pretty straightforward. This does break down when you need to compose something from multiple sources.

Last Post!

rogerdff

rogerdff

I was searching for the same decoupling, defining in my umbrella project an ADT Interface (Abstract Data Type).

And using direct Phoenix PubSub and RPC for data exchange through the ADTs types.

Works perfectly, just dont know yet how that will perform when in Prod Env.

Where Next?

Popular in Questions Top

rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
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
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New

We're in Beta

About us Mission Statement