jayy-lmao / clean-architecture-buckpal-rust

The example from "Get Your Hands Dirty With Clean Architecture" but in Rust because honestly why not
28 stars 5 forks source link

What about DB transactions? #2

Open frederikhors opened 6 months ago

frederikhors commented 6 months ago

How are you dealing with DB transactions?

jayy-lmao commented 6 months ago

Hi @frederikhors! Forgive me as it's been quite a while since I touched this repo.

There are ways with sqlx to deal with transactions I regularly dip into now in my professional work.

Here's one of their official examples: https://github.com/launchbadge/sqlx/blob/main/examples/postgres/transaction/src/main.rs

Can't quite remember what the source book did with transactions, but if you need to store / pass them around you might be able to do so with a generic that implements the Transaction trait. Maybe an Arc/Mutex to wrap it if that becomes necessary. What's your use case?

frederikhors commented 6 months ago

Hi @jayy-lmao I'm talking about transactions across business usecases (the services layer): imagine "wallet" along with your existing "account".

Example:

jayy-lmao commented 6 months ago

So you would definitely need a Arc, Mutex, and an optional. You would need to be careful that you have not created a singleton, but rather a per-request.

Or you may be able to do this by making it an argument at either the service or repository layer, then passing around the tx object.

e.g.

impl AbstractWalletService for WalletService {
  fn check_wallet_balance(&self, tx: &mut Transaction<'_, Postgres>) -> anyhow::Result<bool> {
    todo!()
  }
}

Then in your handler or whichever you would do

let mut tx = db.pool.begin().await?;

let result = check_wallet_balance(&mut tx)?;

// further down once you've done all your operations
tx.commit()

Of course you may want to do this at a Service level instead, in which case it would just be the same thing with your repository functions.

Its worth being aware that you can also alternatively use the Executable trait as a trait bound if you wanted your functions to accept either a transaction or a regular pool reference.