byu

byu

Ash Resource Consistency Boundary vs Transactional Boundary

Given a very very simple twist on the canonical Ash Example, I just have a Ticket resource with only an id and name attributes backed by a postgres DB.

In the iex -S mix console, while playing around some more, and exploring a bit…

I execute the following code to create tickets; I see a series of INSERTs where each insert is in its own database transaction.

for i <- 0..5 do
  Helpdesk.Support.Ticket
    |> Ash.Changeset.for_create(:create, %{name: "Issue #{i}"})
    |> Helpdesk.Support.create!()
end

So, in a very loose analogy to DDD, the Ash Resource is akin to an Aggregate Root, where the Ash action also has the job to enforce the bounded context’s business rules of the Aggregate (or Resource).

And not to get sucked into the DDD arguments of whether or not the transactional boundaries must match consistency boundaries (the aggregate root), say I still want a set of action/changes to all succeed or not…

How is that achieved in Ash? How can I get a series of resources acted upon within the same DB transaction?

Like if I wanted something similar to a “DDD Service” that takes two resources and updates them conforming to a business rule, then persisting then in a “Unit of Work” ? – Loose analogies still.

For example, say I have two chess players (each a Resource/Aggregate instance) and I want to update their ranking after a game… I want to increase the winner’s points and decrease the loser’s points, while staying consistent?

Or am I thinking about this all wrong?

Thanks

Most Liked

zachdaniel

zachdaniel

Creator of Ash

:wave: so you have a few options. Transactionality is built into Ash action. So using your chess game example, you might have something like this:

# on ChessGame

update :finish_game do
  change set_attribute(:complete)
  change fn changeset, _ -> 
    Ash.Changeset.after_action(changeset, fn changeset, game -> 
      # fetch players and modify their score
      # whatever happens here is transactional
    end)
  end
end

By default things that happen in hooks will be in the same transaction. You can also start your own transactions using Ash.DataLayer.transaction(resource, fn -> end)

If your resources are in the same data layer then this works exactly as you’d expect transactions to work. If you have resources across multiple data layers, a transaction for both will be opened. This makes it technically possible to achieve inconsistency if one transactions loses and the other one doesn’t due to a network error (for example). However, it makes a good effort at cross-data-layer consistency. There are usually some things to keep in mind if you are using cross data layer actions but I won’t go into that here because I don’t think that is the main thing in question :slight_smile:

byu

byu

Ok. Looks like I just need to get my hands dirty and try things to find what’s going to work out best for my situation.

Thanks for showing these options.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a &gt; b) do {:ok, "a"} end if (a &lt; b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
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
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
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
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

Other popular topics Top

malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
TunkShif
This post is an instruction guide to help you setup your Neovim for Elixir development from scratch. It includes general information on h...
274 42158 114
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
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
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New

We're in Beta

About us Mission Statement