artem

artem

CQRS/Commanded and counting credits/usage

Hi all

I am learning CQRS and Commanded by doing a project (so main point is learning), I’ve got to the point where it is already doing something useful on my laptop: you type a news article and then it uses ChatGPT for crafting some clever posts about it + you can tune the response with supposedly cool UI.

Now I want to make it public and let people play with it for free, but within some limits so I don’t get bankrupt on a hobby project (say, some 20-50 generations for user per month or per whole user lifetime) and.. how do you do it in CQRS? :slight_smile:

Non-CQRS ways
I come from Java/SpringBoot world so a relatively natural way for me would be to have some sort of credits field in the customer or user object. Then before doing something expensive (ChatGPT generation in this case) I’d check if there are credits available and decrease them in the same transaction. If generation fails, then some fail handler may or may not reuse credit back.

Ways for CQRS and specifically commanded
As everything in CQRS is eventually consistent, does it mean that I need to “book” some credits, make it somehow possible to monitor when the “booking” is completed (via some projection with a booking request id?) and only then do a request?

Or is possible to rely on Commanded’s strong consistency (or maybe it’s enough to specify option returning: aggregate_state when dispatching) and once dispatch has happened I can already know if the credits were enough or not.

Or am I missing some other obvious options? Like is it possibly common for exactly “credit checks” kind of use cases live outside CQRS so that DB transactions could be used for consistency protections and access serialization?

Again, my main point here is learning (though I still want to open project to public), so I’ll highly appreciate any hints and ideas, especially if ever did anything like that in CQRS.

P.S.
Even though it’s mostly self-education project almost invented just so I could have a real use case to play with CQRS, there is still some reasoning for exactly CQRS. My main technical motivation is about separating read-write models so I could have as many and as convenient read models as I like. Access serialization and consistency are way less important for me in this particular case.

First Post!

byu

byu

Caveat: I’m a major beginner in EventSourcing and CQRS.

What about something like the following?

  1. Manage an Event Stream (aggregate) per User per Refresh Period (month, year, all time).
    • eg generation-account-bob-202402
    • This tracks not just the count, but the lifecycle of each generation request.
      • pending, completed, failed to complete, rejected.
    • Also can have event that indicates the number of allowed credits.
  2. Use a SubmitGenerationCommand that includes
    • request identifier
    • request payload/details about the generation TBD
  3. Handling the SubmitGenerationCommand can emit Events
    • GenerationSubmittedEvent – with request identifier
    • GenerationRequestRejectedEvent – with details such as:
      • max pending requests limit reached
      • max credits reached.
  4. An Event Handler or Process Manager can watch for GenerationSubmittedEvent and then issue commands or execute API requests?
    • Then somehow events get back into the “generation-accounts” stream that would indicate success or failure?
  5. And more Event Handlers would watch for Success/Failure events to then send message to update the “conversation”?

Basically, requests go into a “rate limiter” aggregate for that user-period. The Events of Submitted/Completed/Failed/Rejected requests allow the single aggregate to track the used credits count, pending credits such that used credits + pending <= allowed credits ? and can reject requests over the limit?

And follow-on Event Handlers will eventual consistently process (succeed or fail) the submitted requests?

The above uses patterns:

  • Closing the Books – like accounting, so we can reset the user’s credit each period
  • Many small streams, instead of big streams – A small stream of data for each user-time-period instead of a single stream for the user of all time.
  • Write Ahead Pattern – we are writing the “request submitted” event to hold the pending place, then a Completion/Failed event to confirm the credit usage

We avoid:

  • Reservation Pattern (book keeping) – because if the credit is reserved/booked, that reservation has an expiration because that ticket may never have been used/completed… And even then, the generation processor that receives the ticket and executes for a GenerationCompleted response MAY race condition with the ExpirationEvent.

[edit]: Here’s a suggested video that also talks about how to do different kinds of “business transactions” that spans multiple streams:

Where Next?

Trending in Questions Top

jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
silverdr
Using Phoenix.LiveView.TagEngine as an EEx.Engine is deprecated! To compile HEEx, use Phoenix.LiveView.TagEngine.compile/2 instead. Sta...
New
saveman71
Hello ! We want new/edit form pages to POST/PUT to their own URL rather than the resources REST defaults (post /things, put /things/:id)...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
michallepicki
I am using Oban and occasionally, shortly after a deployment, a handful of jobs can fail because of dependency on other parts of the syst...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
type1fool
I just stumbled on a newly redesigned elixir-lang.org. :tada: It looks like @Software_Mansion did the work, and I think it is generally a...
New
akoutmos
@hugobarauna and I (Alex Koutmos) have been hard at work on writing a book on Nerves that takes you from simply blinking LEDs to building...
New

We're in Beta

About us Mission Statement