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_service is 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.
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:
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:
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:
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.