Skip to content

Instantly share code, notes, and snippets.

@gogogarrett
Created June 10, 2016 08:17
Show Gist options
  • Save gogogarrett/09cebaeeabafb4bf41e88994fc070f8f to your computer and use it in GitHub Desktop.
Save gogogarrett/09cebaeeabafb4bf41e88994fc070f8f to your computer and use it in GitHub Desktop.
Hopscotch like service and workflows in elixir.
defmodule Service.AwardAcorns do
alias ExSeeds.{Repo, Student}
def call(student, amount \\ 3) do
student
|> ExSeeds.Student.changeset(%{acorns: student.acorns + amount})
|> Repo.update
end
end
defmodule Service.EnsureEnoughAcorns do
def call(acorns, cost) when acorns >= cost do
{:ok, ""}
end
def call(_acorns, _cost) do
{:error, "not enough acorns"}
end
end
defmodule Service.CreateStudentEvent do
def call(map) do
case create_student_event(map) do
_event ->
{:ok, "event created."}
_ ->
{:error, "event could not be created."}
end
end
defp create_student_event(map) do
# ie.
# %{
# student_id: student.id,
# precinct: 'arcade',
# event_type: 'ArcadeGame'
# }
# BlakeProgress.Service.create_student_event(map)
%{id: 1}
end
end
defmodule Workflow.Arcade.PlayGame do
alias ExSeeds.{Repo, Student}
def call(student, game) do
steps(student, game)
|> Workflow.Transaction.call
end
defp steps(student, game) do
fn ->
with {:ok, _} <- Service.EnsureEnoughAcorns.call(student.acorns, game.cost),
{:ok, _} <- deduct_acorns(student, game.cost),
{:ok, _} <- Service.CreateStudentEvent.call(%{id: 1}),
do: {:ok, "workflow successful"}
end
end
defp deduct_acorns(student, cost) do
Service.AwardAcorns.call(student, -cost)
end
end
defmodule Workflow.Transaction do
def call(function) do
ExSeeds.Repo.transaction(fn() ->
case function.() do
{:ok, result} -> result
{:error, error} -> ExSeeds.Repo.rollback(error)
end
end)
end
end
@gogogarrett
Copy link
Author

gogogarrett commented Jun 10, 2016

The problem with this approach is that these service are still coupled to the ecto/phoenix model's and schemas.

I am curious if there is a more "elixir way" approach to solve this problem, perhaps with a standalone application for "PlayArcadeGame" that gets a single message and handles the entire transaction inside the application, instead of living inside a phoenix app. (the problem I see here, is you will need each of these "workflow/applications" to share the same ecto schema and talk to the same database.. and possibly run into race conditions? idk)

Or if each of these services (steps in a with workflow) delegate to stand alone otp app.. how to undo the thing if a further step fails.

Happy to hear any and all suggestions / feedback / criticism

@gogogarrett
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment