ppicom

ppicom

Designing Elixir Systems With OTP - isn't the integration pattern interfering with business logic?

Hello! :wave:

I recently completed the book and highly recommend it to anyone looking to deepen their understanding of OTP (especially if you’re relatively new to Elixir and Erlang, as I am).

Now, onto my question :grin: The authors’ approach to integrating the persistence layer using a poncho project—where a persistence_fn function is passed to the core project—raised an interesting thought for me.

The original implementation of the :answer_question handler creates a new Response, uses it to advance the Quiz (by calling answer_question and select_question), and concludes the quiz if no questions remain. Here’s the code:

def handle_call({:answer_question, answer, fun}, _from, {quiz, email}) do
  response = Response.new(quiz, email, answer)

  quiz
    |> Quiz.answer_question(response)
    |> Quiz.select_question()
    |> maybe_finish(email)
end

defp maybe_finish(nil, _email), do: {:stop, :normal, :finished, nil}

defp maybe_finish(quiz, email) do
  {
    :reply,
    {quiz.current_question.asked, quiz.last_response.correct},
    {quiz, email}
  }
end

After incorporating the poncho project and modifying the handler to expect an external function, the implementation changes as follows:

def handle_call({:answer_question, answer, fun}, _from, {quiz, email}) do
  fun = fun || fn r, f -> f.(r) end
  response = Response.new(quiz, email, answer)

  fun.(response, fn r ->
    quiz
    |> Quiz.answer_question(r)
    |> Quiz.select_question()
  end)
  |> maybe_finish(email)
end

defp maybe_finish(nil, _email), do: {:stop, :normal, :finished, nil}

defp maybe_finish(quiz, email) do
  {
    :reply,
    {quiz.current_question.asked, quiz.last_response.correct},
    {quiz, email}
  }
end

By default, fun retains the original behavior, but now the user can pass a custom fun. If the passed fun does nothing, Quiz.answer_question and Quiz.select_question will no longer execute, preventing the quiz from progressing.

Am I correct in understanding that this integration effectively allows a “third party” (the poncho project) to interfere with the core business logic? Or am I overlooking something? :thinking:

PS: I haven’t contacted the authors to paste that code in here. If it’s against the rules, I am happy to take it down.

Most Liked

JEG2

JEG2

Author of Designing Elixir Systems with OTP

Thanks for the support and high praise.

You have it right. As an author, you are usually wrestling with the balance of making the example meaty enough to be meaningful versus needing so much code that the examples go on and on for pages. It’s a difficult tightrope to walk.

Do I think this code is the best it could be? No. The function should probably be executed in some error isolating construct and the return value be ignored if it doesn’t conform to some strict protocol. Sadly, that would just require more code. As it is, that book is on the high side of what a lot of folks tolerate as far as lengthy examples go.

It’s worth noting that this design of persistence is not at all common in our community. I don’t expect you to run into a lot of examples like this. It’s far more common to see database constructs driving the initial design of the core business code, in my experience. You can read more about those tradeoffs in this thread.

I will say this, though. I stand by the value of the approach in the book. I wish more people would consider it. I acknowledge all of the tradeoffs discussed in the thread above. However, I often find that the state I want in the application differs significantly from how data is stored in long term persistence. For example, if I am managing a chess game, the running application cares about the current state of the board and where all the pieces are. But as a rich history of the game of chess has taught us, what we want to persist is the list of moves that can be used to recreate and study the game. These are two very different needs and I believe there’s a lot of value in separating those concerns.

That’s just one person’s opinion. Hopefully it will give you some fun new ideas and help you discover better designs for your own projects.

Thanks again for the kind words about the book.

lessless

lessless

Amen to that! I really wish people would use data models preserving context. The SQL model gives a lot of convenience but the costs are not always a part of the initial discussion.

And as you said the app/db impedance mismatch leading to anaemic domain models is a story that happens more often than not.

LostKobrakai

LostKobrakai

I feel like a big reason for people to go with database driven designs is doing „web“. Http is a stateless protocol and we‘re expected to always be able to „cold start“ serving requests through http. Adding stateful handling will always feel like more work in such an environment (it is) and more work needs justification. Justification however can only be provided having seen the potential benefits of an stateful approach.

LiveView is imo a great technology to explore constraints and benefits even in a web space, where you have static stateless renders as well as stateful connected processes.

I‘d phrase this differently. The provider of the implementation doesn‘t „infer“ with the business logic. It „provides“ the business logic. If it provides one where progress is impossible it just is this way. It‘s no other code‘s job to prevent this.

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
Yoga
Or at least, in the works? All I can find are bits and pieces but not a single project from start to finish. Including things like i18n,...
New
phykos
In Ruby, which is very similar to Elixir I do this: def test yield end test do puts("sup there") end Here, the yield keyword will be...
New
LegitStack
I’m not a hugely experienced programmer, just a few years. So I’m looking for resources to learn about a topic but I can’t seem to find m...
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
eliottramirez
Hello, I’m trying to learn Phoenix but I constantly find difficult understanding how the framework works, and I think part of this is th...
New
jace
I wanted to write a library for interacting with a Web API as a practical way of learning Elixir. However, there seem to be a lot of diff...
New
New
younes-alouani
I’m studying Phoenix Framework but I want to understand basics first. Which book/mooc explains better the Design of Network-based Softwar...
New
Twfo326
As a novice dev I’m trying to keep the curriculum as lean as possible. My requirements are modest: build simple CRUD apps with Phoenix...
New

Other popular topics Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 43757 214
New
Lily
In templates/appointment/index.html.eex: <%= for appointment <- @appointments do %> <tr> <td><%= appoi...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
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
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" => #BSON.ObjectId<58eb1a7a9ad169198c3dXXXX>, "email" => ...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement