jdumont

jdumont

CQRS with Commanded - Return directly from aggregate

Afternoon all, I’m back with another ES/CQRS question that’s perhaps more philosophical (for lack of a better word) than technical.

Event-sourcing with Elixir and @slashdotdash 's Commanded actually seems a little different than it is in other languages thanks to the BEAM. Each of the aggregates in our app actually exist as an individual process, and they hang around until they are stopped.

I have an aggregate in my app that builds workouts — I’m back on this CrossFit app — and whilst they are being created they exist as a process and my projection in the db is effectively a mirror, just with some computed fields added which aren’t relevant during the workouts creation. Once the status of a workout changes from "draft" to "published" I’ll be stopping the aggregate as-per Commanded’s docs and the latest post from Bruno, and just the projection of it should suffice.

However, whilst I’m building the workout, it would be really handy to just return the current state of it from the aggregate, rather than the projection. My projection literally just copies out the aggregates state and I don’t need to rely on the projection for unique validations, so I don’t see a huge difference in just returning the aggregate rather than the projection. It should be faster as eventual-consistency wouldn’t be an issue and there should be no issues with validating commands as I’m quite literally working from the canonical source of truth.

This is why my question is more philosophical than technical. What I’ve just described goes against CQRS as I understand it, but it seems to make more logical sense?

Most Liked

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I think @slashdotdash 's explanation does a good job of noting why this is at odds with the traditional CQRS mantra of having distinct read and write models.

The approach I took with GitHub - CargoSense/fable: Your events have a story to tell. · GitHub leans slightly more towards what you’re asking for by eschewing CQRS in favor of a simpler implementation of Event Sourcing.

Basically you have database table, say “shipments”, and Fable guarantees that each event you emit for a given shipment row is handled serially by that shipment row. There may well be other database tables that are affected by that same event (containers or products in that shipment) and in that way the “shipments” table acts a lot like an aggregate. All changes to the shipments table and the containers table should happen because you cut an event, and then the event handler for that event makes changes to the various tables based on the event.

However with Fable as written today you still just read from the database normally when it comes to querying data, and it doesn’t push you to maintain a separate read vs write model. This is either a feature or a big deficiency depending on how wedded you are to CQRS.

Sadly Fable is still alpha stage and missing any useful documentation :frowning: Nonetheless I think there’s a “market” for a middle ground for a more minimalist library that supplies event sourcing guarantees without requiring buy in to the entire CQRS world view.

jdumont

jdumont

I’m going to go ahead and answer myself after half an hour playing around with this idea…I shouldn’t / can’t return the state of the aggregate directly. Commanded doesn’t seem to allow for it, which must be for good reason.

This is why I’ve enjoyed working with CQRS so far, whilst there’s a lot more procedure, it keeps me from shooting myself in the foot! :joy:

slashdotdash

slashdotdash

Commanded allows you to directly access an aggregate’s state via the undocumented Commanded.Aggregates.Aggregate.aggregate_state/2 function:

alias Commanded.Aggregates.Aggregate

%BankAccount{..} = Aggregate.aggregate_state(BankAccount, account_number)

The reason why this isn’t exposed publicly is because an aggregate’s state should be an internal implementation detail. Instead, CQRS promotes the concept of two models: one for writes (aggregates) and one for reads (projections). Commands are handled by the write model and queries are handled by the read model.

In your example you have noticed that the projection (read model) and aggregate state (write model) are very similar, so it might save you some time and effort to use the aggregate state instead of creating a separate read model. This works until the needs of the two models starts to diverge, due to differing data access patterns. At which point you either migrate to a separate read model, or extend the aggregate state to store data which isn’t necessary for command handling. Investing the effort from the outset by implementing the two separate models should payoff as your model becomes richer in behaviour.

Last Post!

slashdotdash

slashdotdash

Using a separate elements table with a jsonb column would be a hybrid approach that might work (basically using Postgres as a document DB!).

Where Next?

Popular in Questions 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
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 54006 488
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
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 54921 245
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement