4
\$\begingroup\$

I want to know how would I go about architecting an MVC architecture using actix-web.

I have a user_service which requires user_repository, and this is how I'm currently doing it:

let config = Data::new(config.clone());
let mongo_repo = Data::new(MongoRepo::init(&config).await);
let user_repository = Data::new(UserRepository::new(&mongo_repo, &config));
let user_service = UserService::new(config_arc.clone(), user_repository.clone()).await;

then in main:

 HttpServer::new(move || {
 let logger = middleware::Logger::default();
 App::new()
 .wrap(logger)
 .wrap(middleware::NormalizePath::trim())
 .app_data(web::FormConfig::default().limit(1 << 25))
 .service(user_service::get_scope())
 .default_service(web::to(|| HttpResponse::NotFound()))
 })
 .bind((host_address.to_owned(), port.to_owned()))?
 .run()
 .await

then in the user_service:

pub struct UserService {
 config: Data<Configuration>,
 user_repository: Data<UserRepository>
}
impl UserService {
 pub async fn new(config: Data<Configuration>, user_repository: Data<UserRepository>) -> Self {
 UserService {
 config: config,
 user_repository
 }
 }
// ... other user service related functions
}

This feels a bit weird to me. Is there a better way to achieve this?

I have previous experience with spring boot and .NET, and dependency injection seems easier in those frameworks.

Is this how it's supposed to be, or is there any better way to do dependency injection?

Its working as expected but I want to know if this is the right way to do it or if there's a different but easier way?

toolic
14.6k5 gold badges29 silver badges204 bronze badges
asked Jun 18, 2024 at 0:57
\$\endgroup\$

1 Answer 1

7
\$\begingroup\$

Almost any pattern pioneered in C# or Java is going to be a bit more cumbersome in Rust. Not because of OOP necessarily, but because they are GC'd; many design patterns make heavy use of shared resources which Rust doesn't make transparent. Any time you want to share objects in Rust it pretty much needs to be with an Arc. The more cumbersome part is that you'd ultimately need to incorporate a Mutex as well for shared objects that need to keep mutable state.

Not to say you shouldn't use such patterns, their abstractions may be clear and helpful to you, just the ownership and access of objects is much more exposed. But this may be why many seem to prefer actor-based architectures instead.

That being said, using Data in a general purpose way is weird. It is essentially a wrapper for Arc<T> that implements FromRequest to use in Actix-Web handlers to get data from the root App. If you are not using it for that purpose (and nothing here suggests you are) then you should just use Arc<T> instead.

answered Jun 18, 2024 at 5:02
\$\endgroup\$
1
  • \$\begingroup\$ Makes sense, I was initially using Arc but since I'm new to Rust and actix I thought this is the standard way to do it. Thanks for your input! \$\endgroup\$ Commented Jun 19, 2024 at 21:01

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.