I'll start by saying I'm new to Rust, but not to programming. I've done a lot of Java development, and that almost certainly colours how I think about things, so bear with me
I'm working on an application, in Rust, with a layered architecture. Specifically I want to have something like:
Get User Handler -> Users Service -> Users Repository -> Database
Update User Handler -> Users Service -> ....
This is all relatively straightforward to make work. But I want to do it in a way that is easy to unit test. That means I want to be able to unit test the "Users Service" without needing a real "Users Repository", since a real "Users Repository" would then need a "Database".
So, in the Java world that would mean to use Interfaces, so I've assumed that in the Rust world I should use Traits. I can't see a better choice right now. And that means that I've got:
trait UserRepository {}
struct PostgresUserRepository {}
impl UserRepository for PostgresUserRepository {}
trait UserService {}
struct UserServiceImpl {}
impl UserService for UserServiceImpl {}
Except that, in Rust, traits need to be boxed in some form to be stored in other structs. And then, because this is a webapp and thus multi-threaded, they need to be Sync+Send
and so I've ended up using Arc
everywhere:
trait UserRepository {}
struct PostgresUserRepository {
database: Arc<Database>,
}
impl UserRepository for PostgresUserRepository {}
trait UserService {}
struct UserServiceImpl {
repository: Arc<UserRepository>,
}
impl UserService for UserServiceImpl {}
#[get("/users/<user_id>")]
pub fn get_user_by_id(user_id: String, user_service: State<Arc<UserService>>) -> Json<User> {}
This then means that to get from my handler to my database I'm going through three layers of Arc
. And that just feels wrong.
Is this the correct/best way to architect this? Or is there some better alternative that I've not yet found?
Cheers