-
Notifications
You must be signed in to change notification settings - Fork 21
Description
So in the last few days, I tried to change one of my app codebases to the one you are suggesting (the latest example of it right now is the branch 3).
I found a bit of issues which I hope we'll find a way to fix in the subsequent branches:
-
How do I write traits implementation on multiple files?
This is important to me. I have tons of methods and I cannot have everything in one file only.
-
What if a repository method must be called within the same transaction?
This is another big issue currently not handled.
-
I lose the ergonomics of writing this in the service method:
impl Service { pub async fn buy_product(&self, customer_id: &str, id: &str) -> Result<()> { // This is just a fake code and a fake example let transaction = self.repo.begin(); // note begin() is custom and not leaks DB details to service layer let product = transaction.get_product(id).await?; // repo method let balance = transaction.check_money_balance(customer_id).await?; // repo method // This is just a simple example, but there can be many business rules here if balance < product.price() { return Err("not enough money left"); } transaction.buy_product(id, customer_id).await?; // repo method transaction.commit(); // again, DB details not leaked to the service layer here Ok(()) } }
and instead, I have to call a specific repo method passing all the details and above all I'm forced to move some logic functions in the repo method:
impl Service { pub async fn buy_product(&self, customer_id: &str, id: &str) -> Result<()> { // Since I cannot start transactions in the service anymore // I need to call a repo method to do everything in it instead for the DB transaction: let result = self.repo.buy_product(customer_id, id).await?; // repo method Ok(()) } } impl Db { pub async fn buy_product(&self, customer_id: &str, id: &str) -> Result<()> { // This is the same code used to be in the service method let transaction = self.repo.begin(); let product = self.get_product(transaction, id).await?; // repo method let balance = self.check_money_balance(transaction, customer_id).await?; // repo method // This kind of rule needs to be in the service layer, not in the repo one if balance < product.price() { return Err("not enough money left"); } self.buy_product(transaction, id, customer_id).await?; // repo method transaction.commit(); Ok(()) } }
-
To be able to fix the previous point I need to create a repo method for each service method (and for the Trait), which is a mess to maintain: many files to write and update, many Trait declarations to write/update: a mess.
-
I can't call methods of other services in the same transaction (but I have to say that this point doesn't interest me much, in fact, it's wrong as you explain very well in your excellent guide).
The simple example is this:
impl Service { pub async fn buy_product(&self, customer_id: &str, id: &str) -> Result<()> { // This is just a fake code and a fake example let transaction = self.repo.begin(); // note begin() is custom and not leaks DB details to service layer self.settings_service.get_customer_sell_settings(transaction, customer_id).await?; // do something else transaction.commit(); // again, DB details not leaked to the service layer here Ok(()) } }
This is useful and usable only if
settings_serviceis within the same server, maybe in the same codebase, you know: if it's very fast to respond.
It's all for now. I'll update this issue if I find something else to suggest to you. Thanks for everything.