ppicom
Designing Elixir Systems With OTP - isn't the integration pattern interfering with business logic?
Hello! ![]()
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
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? ![]()
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
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
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
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.
Popular in Chat/Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









