Include relations of database in Struct or not?

Hello

I have the following - heavily simplified - database-structure:
Schermafbeelding 2022-11-01 om 01.01.22
These are all separate tables in my database.

The question I have now: Is it better in my Rust-code to include the relations in my structs. Or keep them separate?

Option 1: Include relations in my structs.

pub struct Restaurant {
    id: u64,
    name: String,
    description: String,
    dishes: std::vec::Vec<Dish>,                    // RELATION TO DISH IS INCLUDED IN THIS PARENT STRUCT.
}

pub struct Dish {
    id: u64,
    name: String,
    ingredients: String,
    dish_variations: std::vec::Vec<DishVariation>,  // RELATION TO DISHVARIATON IS INCLUDED IN THIS PARENT STRUCT.
    dish_extras: std::vec::Vec<DishExtra>,          // RELATION TO DISHEXTRA IS INCLUDED IN THIS PARENT STRUCT.
}

pub struct DishVariation {
    id: u64,
    name: String,
    price: f64,
}

pub struct DishExtra {
    id: u64,
    name: String,
    price: f64,
}

In this case I could in my Askama templates access all the data of a restaurant simple by using restaurant.name, restaurant.dish[0].name,...

Option 2: Separate relations in my structs.

pub struct Restaurant {
    id: u64,
    name: String,
    description: String,
}

pub struct Dish {
    id: u64,
    name: String,
    ingredients: String,
}

pub struct DishVariation {
    id: u64,
    name: String,
    price: f64,
}

pub struct DishExtra {
    id: u64,
    name: String,
    price: f64,
}

Is this option the structs are identical to the database-model. If I for example want to access the dishes a Restaurant has, I'd use a custom function like get_dishes_of_restaurant(id: u64) -> std::vec::Vec<Dish> {...}.

What is the best option?

For me it would depend on how often you need the relation data when you use the structs.

If you almost always need to fetch the relation data to do anything useful, then making it part of the struct makes a lot of sense. It saves you having to right extra code to fetch the relations (and possibly have some sort of cache).

On the other hand if you often don't need the relations, then fetching them every time is just forcing you to do more queries and use more memory.

Recently I've been mostly leaning towards making structs that represent the database row rather than doing any pre-fetching of relations. It's maximally flexible, and so far I haven't found myself writing so much extra code to fetch the relations that it feels like a bad trade off. But like I said, it will depend heavily on the kinds of operations you need to do in many places in your code.

2 Likes

Note that with representation #1, you can't represent the inverse relationship! If your restaurant contains the dishes it offers, then the dishes can't contain the reataurant they are offered in, because that would require the second restaurant instance to contain another set of the dishes, each of them containing another copy of the restaurant, and so on.

I generally recommend that for representing such indirect relationships between entities, you use IDs – this lets you fetch the data at the other end if you need it, while preserving the option of performing an inverse lookup should that ever become necessary.