Вопросы о более сложных сценариях [closed]

У меня есть проект с этими моделями и отношениями:

  1. Игрок
    1. Команда
      1. Игры
      2. Тренер
      3. Доктор
        1. Медицинская специальность
    2. Спонсор

Это, конечно, пример, но, как видите, я могу иметь на столе Player а team_id столбец как FK (внешний ключ) для Team который имеет coach_id столбец как FK для Coach стол.

В моем бэкэнде Golang я использую ORM который позволяет вам (генерировать и) использовать такой код:

type Player struct {
  id string
  name string
  Team *Team
  //...
}

type Team struct {
  id string
  Coach *Coach
  //...
}

type Coach struct {
  id string
  //...
}

func (repo *Repo) PlayerById(id string) DomainPlayer {
  player := repo.queryById(id).withTeam().withCoach()

  // player here has TEAM and TEAM.COACH using a unique SQL query

  // and I can easily convert it:

  return ConvertPlayerToDomainPlayer(player)
}

Начиная с Rust, я был очарован МореОРМ но теперь, когда я пытаюсь создать что-то более сложное, я понял, что не могу делать вещи, как в Go.

Я создание объектов с использованием sea-orm-cli из моей базы данных Postgresql, созданной с использованием Мигратор SeaORM (это совершенно необязательно, вы также можете написать следующий код самостоятельно).

Сгенерированные объекты выглядят так:

//! SeaORM Entity. Generated by sea-orm-codegen 0.9.3

use sea_orm::entity::prelude::*;

#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "player")]
pub struct Model {
    #[sea_orm(primary_key, unique)]
    pub id: String,
    pub name: String,
    pub team_id: Option<String>,
}

#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
    #[sea_orm(
        belongs_to = "super::team::Entity",
        from = "Column::TeamId",
        to = "super::team::Column::Id",
        on_update = "NoAction",
        on_delete = "NoAction"
    )]
    Team,
}

impl Related<super::team::Entity> for Entity {
    fn to() -> RelationDef {
        Relation::Team.def()
    }
}

impl ActiveModelBehavior for ActiveModel {}

И я использую такой код:

async fn player_by_id(&self, id: String) -> Result<Option<DomainPlayer>> {
    let player = match Player::find_by_id(id)
        .find_also_related(Team)
        // I would like to use here something like `with_team()` and `with_coach()` or `with_team(|team| team.with_coach())`
        .one(self.db)
        .await?
    {
        Some(o) => o,
        None => return Ok(None),
    };

    let coach = if let Some(team) = &player.1 {
        Coach::find_by_id(team.coach_id).one(self.db).await?
    } else {
        None
    };

    // Since I need to return a DomainPlayer (not a Player) here I cannot use a `Player.into()` because generated `Player` does not have `Team` field

    Ok(Some(custom_func_to_transform_player_to_domain_player(player, player.1 /*this is team*/, coach)))

    // Do you can imagine how complex is to use these functions?
}

Я хотел бы знать, что вы думаете об этом мысленном приказе.

Скорее всего, мне нужно изменить его, потому что код действительно уродлив (а также дорог для многих SQL-запросов и распределений) и неудобен для использования для масштабирования с еще большей сложностью.

Что вы порекомендуете?

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *