클래식 연결에 대한 대안이 실패합니다.

Everything in our knowledge... contains nothing but mere relations —Kant



이 포스트에서 나는 다른 모델 간의 관계를 위한 테이블/모델과 같은 간단한 아이디어가 어떻게 더 행복한 개발을 만드는지 보여줄 것입니다.

핵심 교리는 다음과 같습니다.

  • 연관이 필요한 두 모델이 엄격한 부모-자식 커플링이 아닌 경우 항상 연결 모델을 사용합니다. Order와 OrderItem은 상위 주문이 없는 항목이 존재하지 않기 때문에 엄격한 결합인 반면, 일부 사용자와 프로젝트는 서로 없이 존재할 수 있기 때문에 강력하게 결합되지 않습니다.
  • 모델을 관계 데이터에서 분리할 때 신중해야 합니다. 관계는 물리적 대상처럼 실제적이며 고유한 모델이어야 합니다.
  • 작은 테이블과 모델을 수용하십시오. 많은 상용구(기존 모델의 마이그레이션 파일, 모델, 사양, 연관 업데이트)처럼 보일 수 있지만 깔끔한 디자인은 유지 보수성과 성능을 보장합니다.

  • 사례1, 남자친구와 여자친구



    나는 이것을 일부 SO 토론에서 보았을 것이라고 맹세합니다. 예는 고안된 것일 수 있지만 목적에 도움이 될 것입니다.
    소셜 앱을 만들고 Boyfriend 모델을 가지고 있고 has_one :girlfriend 를 도입할 생각을 하고 있다고 가정합니다. 이렇게 하려면 가능한 여자 친구 모델에 boyfriend_id 열이 있어야 합니다.

    # bad
    
    class Boyfriend
      has_one :girlfriend  
    
      # Table name: boyfriends
      #
      #  id                  :integer(11)    not null, primary key  
    end
    
    class Girlfriend
      belongs_to :boyfriend  
    
      # Table name: boyfriends
      #
      #  id                  :integer(11)    not null, primary key  
      #  boyfriend_id        :integer(11)    not null
    end
    


    이 모델링에는 적어도 두 가지 문제가 있습니다. 왜 남자친구는 여자친구 한 명에게만 제한되어 있습니까? 그리고 왜 여자친구가 남자친구에게 속한다는 성차별적 암시가 있습니까?
    두 신조를 모두 준수하면 남자 친구와 여자 친구 모두 실제로는 자신의 사람이며 그들의 관계는 완전히 다른 것임을 알 수 있습니다.

    # good
    
    class Person
      enum gender: {female: 0, male: 1, other: 2}
      has_many :relationships, foreign_key: :of_person
    _id
    
      # bonus
      has_many(
        :boyfriends, -> { where(gender: "male") },
        through: :relationships, source: :with_person
      )
    
      has_many(
        :girlfriends, -> { where(gender: "female") },
        through: :relationships, source: :with_person
      )
    
      # Table name: people
      #
      #  id                  :integer(11)    not null, primary key  
      #  gender              :integer(11)    not null, default: 0
    end
    
    class Relationship
      belongs_to :of_person
      belongs_to :with_person
    
      # Table name: relationships
      #
      #  id                  :integer(11)    not null, primary key  
      #  of_person_id        :integer(11)    not null
      #  with_person_id      :integer(11)    not null
    end
    


    이 "지시된"또는 "유형화된"관계 모델링에는 두 개의 레코드가 필요합니다. 하나는 "남자친구"에 대한 것이고 다른 하나는 관계의 "여자친구"에 대한 것입니다. 모든 성별 페어링을 수용할 수 있으며 가족 관계에도 다양한 타이핑이 가능합니다.

    Case2, 계정의 기본 사용자



    this SO question 에서 영감을 받았습니다.
    사용자가 많을 수 있는 계정이 있으며(일종의 은행 업무, 마케팅 등 비즈니스 계약?) 이제 특별한 "기본"사용자가 있어야 하며, 이상적으로는 계정당 한 명이 필요합니다.

    # bad
    
    class Account 
      has_many :users
      has_one :primary_user, -> { where(primary: true) }, class_name: "User"
    
      # Table name: accounts
      #
      #  id                  :integer(11)    not null, primary key
    end
    
    class User
      belongs_to :account  
    
      # Table name: users
      #
      #  id                  :integer(11)    not null, primary key
      #  email               :string(200)    not null, default ""
      #  account_id          :integer(11)    not null
      #  primary             :boolean        not null, default false
      #
      # indexes
      # (account_id, primary) UNIQUE, WHERE(primary = true)
    end
    


    다시 말하지만, 두 가지 원칙을 모두 적용하면 사용자가 계정에 대한 엄격한 "자식"개체가 아닐 수 있으므로 계정과의 관계는 별도의 모델이 필요합니다. 또한, 우선 순위는 "계정-사용자"관계와 계정 간의 특별한 종류의 관계이기도 합니다.

    # good
    
    class Account 
      has_many :account_users
      has_one :primary_user
    
      # Table name: accounts
      #
      #  id                  :integer(11)    not null, primary key
    end
    
    class User
      has_one :account_user 
      has_one :account, through: :account_user  
    
      # Table name: users
      #
      #  id                  :integer(11)    not null, primary key
      #  email               :string(200)    not null, default ""  
    end
    
    class AccountUser
      # Tie model, which account a user is part of.
    
      belongs_to :account
      belongs_to :user
    
      # Table name: account_users
      #
      #  id              :integer(11)    not null, primary key
      #  account_id      :integer(11)    not null
      #  user_id         :integer(11)    not null
      #
      # indexes
      # (user_id) UNIQUE # a user can only ever be in one account
    end
    
    class PrimaryAccountUser
      # Stores which user is the primary user in an account.
      # Note that belonging to :account_user instead of :user reduces the risk of the relationship being removed, but primacy remaining.
    
      belongs_to :account_user
      belongs_to :account
    
      # Table name: account_users
      #
      #  id                :integer(11)    not null, primary key
      #  account_id        :integer(11)    not null
      #  account_user_id   :integer(11)    not null
      #
      # indexes
      # (account_id) UNIQUE # an account can only ever have one primary user
    end
    


    이 모델링을 통해 서로 의존하지 않는 계정과 사용자를 만들 수 있습니다. 사용자는 AccountUser 레코드를 만들어 계정과 연결할 수 있으며, 마지막으로 PrimaryAccountUser 레코드를 만들어 기본 사용자를 지정할 수 있습니다(사용자가 먼저 계정과 연결된 경우).

    좋은 웹페이지 즐겨찾기