How to handle file imports in a sane way

I have a project where I store my domain logic in various sub folders. The holds all the imports and starts a manager which starts the various threads that make up the program.

My now looks like this... is there a more "sane" way to do this? There are about 10 more models and persistence structs being developed so this list will grow. I have the gut feeling that I am not doing it the right way. (even though it works perfectly)

use std::env;

use dotenv::dotenv;
use tokio::runtime::Runtime;

use crate::domain::managers::manager::Manager;

mod lib {
    pub mod utils {
        pub mod amqp;
        pub mod helpers;

pub mod domain {
    pub mod constants;
    pub mod env_vars;

    pub mod consumers {
        pub mod consume_broadcast_queue;
        pub mod consume_private_queue;

        pub mod core {
            pub mod models {
                pub mod contract;
                pub mod order;
                pub mod user;
                pub mod exe_rprt;

            pub mod persistence {
                pub mod persist_trait;
                pub mod persist_client;
                pub mod persist_contract;
                pub mod persist_err_resp;
                pub mod persist_order;
                pub mod persist_user;
                pub mod persist_exe_rprt;

    pub mod managers {
        pub mod manager;
        pub mod monitor;
        pub mod core {
            pub mod structs;

    pub mod publisher {
        pub mod publisher;

        mod core {
            pub mod enums;
            pub mod requests;
            pub mod structs;

const LOG_LEVEL: &str = "info";

fn main() {

    let manager: Manager = Manager::default();
    let rt = Runtime::new().unwrap();

    rt.block_on(async {

fn set_log_level() {
    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", LOG_LEVEL);

Imports in Manager look like this

use std::sync::{Arc, Mutex};
use std::thread;
use std::thread::JoinHandle;

use crate::domain::consumers::consume_broadcast_queue::ConsumeBroadcastQueue;
use crate::domain::consumers::consume_private_queue::ConsumePrivateQueue;
use crate::domain::managers::core::structs::State;
use crate::domain::managers::monitor::Monitor;
use crate::domain::publisher::publisher::Publisher;
use crate::lib::utils::amqp::create_connection;

pub struct Manager {
    state: Arc<Mutex<State>>,


What I would expect is the top of to look like this:

// uses here

mod lib;
pub mod domain;

// rest of the module

With the following directory structure:

  • lib/
    • utils/
  • domain/
    • consumers/


Each module then contains just the mod X; items for its sub-modules. So lib/ would contain pub mod utils;. Then, domain/ would contain: pub mod constants; pub mod env_vars; pub mod consumers; etc.

Aside: you can use either src/lib/ + src/lib/ or src/lib/ + src/


You could do:

mod prelude {
    pub use crate::nested::stuff;
    pub use crate::etc;

and then:

use crate::prelude::*;

everywhere else. That's a standard pattern.

"Flattening" the API by pub use in the root of the library (src/ is also a good way to simplify things.


This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.