ActiveRecord 패턴의 단점은 무엇입니까?


30

데이터 액세스 / 비즈니스 개체에 ActiveRecord 패턴을 사용하는 데 어떤 단점이 있는지 궁금합니다. 제가 머리 꼭대기에서 생각할 수있는 유일한 것은 그것이 단일 책임 원칙을 위반한다는 것입니다. 그러나 AR 패턴은이 이유만으로는 사용하지 않는 것을 정당화하기에 "충분히 좋은"것처럼 보이지 않을 정도로 일반적입니다. 보기는 다음과 코드 I 작업 종종 아무도 때문에 왜곡 될 수 있는 ) 고체 원칙을.

개인적으로 저는 ActiveRecord의 팬이 아닙니다 (AR이 "자연적"이라고 느끼는 Ruby on Rails 응용 프로그램을 제외하고는 클래스가 너무 많은 것처럼 느껴지고 데이터 액세스가 클래스 자체에 달려서는 안되기 때문에) 다루다. 비즈니스 오브젝트를 리턴하는 저장소를 사용하는 것을 선호합니다. 내가 사용하는 대부분의 코드는 다음과 같은 형태로 ActiveRecord 변형을 사용하는 경향이 있습니다 (이 방법이 부울 인 이유는 모르겠습니다).

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

또는 때때로 public static Foo FindFooByID(int fooID)파인더 를위한 방법과 public void Save()저장 / 업데이트 를 위한 방법을 갖는보다 "전통적인"방법 .

ActiveRecord는 일반적으로 구현하고 사용하기가 훨씬 간단하지만 복잡한 응용 프로그램 에는 너무 단순 해 보이지만 데이터 액세스 논리를 리포지토리에 캡슐화하여 더 강력한 아키텍처를 가질 수 있습니다 (스왑 아웃을 쉽게하는 것은 말할 것도 없습니다) 데이터 액세스 전략 (예 : Stored Procs + DataSets를 사용하고 LINQ 등으로 전환하려는 경우)

그렇다면 ActiveRecord가 작업에 가장 적합한 후보인지 결정할 때 고려해야 할 다른 패턴의 단점은 무엇입니까?

답변:


28

주요 결점은 "엔티티"가 자체 지속성을 인식하여 다른 많은 나쁜 디자인 결정을 초래한다는 것입니다.

다른 문제는 대부분의 활성 레코드 툴킷이 기본적으로 1에서 1의 간접 레이어가없는 테이블 필드에 1 대 1을 매핑한다는 것입니다. 이것은 작은 규모로 작동하지만 해결하기 까다로운 문제가 있으면 분리됩니다.


객체에 지속성에 대해 알리면 다음과 같은 작업을 수행해야합니다.

  • 어디서나 데이터베이스 연결을 쉽게 사용할 수 있습니다. 이것은 일반적으로 불쾌한 하드 코딩 또는 어느 곳에서나 발생하는 일종의 정적 연결로 이어집니다.
  • 객체는 객체보다 SQL처럼 보입니다.
  • 데이터베이스가 너무 심해 앱 연결이 끊어졌습니다.

이것 이외에도 다른 나쁜 결정들이 많이 있습니다.


2
"다른 나쁜 디자인 결정"에 대해 자세히 설명해 주시겠습니까?
케빈 클라인

2
감사. Ruby on Rails 개발에서 이러한 문제가 문제라는 것을 발견하지 못했습니다. 동작과 지속성을 개별적으로 테스트하는 것은 여전히 ​​가능합니다. 행동과 지속성을 분리하는 IMO는 실질적인 가치가 거의 없습니다.
kevin cline

@ kevin : 믹스 인과 오리 타이핑과 같은 루비 기능의 단점은 적습니다. 그의 질문에 OP가 사용한 C #과 같은 정적 언어를 사용하면 두 언어를 분리하는 것이 조금 더 어렵습니다.
Wyatt Barnett

@ 웨인 : 나에게 클래스는 단지 메서드를 넣을 상자입니다. 비즈니스 클래스를 별도의 클래스에 배치하여 비즈니스 로직을 지속성에서 분리하거나 비즈니스 메소드가 I / I를 수행하지 않도록 개념적으로 분리 할 수 ​​있습니다 영형. 위임 지원이 열악한 언어 (예 : Java)에서는 많은 양의 코드가 저장됩니다. OTOH, 나는 단지 최대 절전 모드로 들어가서 완전히 틀릴 수 있습니다.
케빈 클라인

4
두 가지를 추가하겠습니다. 1. 지속 메커니즘과의 결합은 불가능하지는 않더라도 코드를 올바르게 단위 테스트하기 어렵게 만듭니다. 2. Active Record는 중앙 집중식 지속 메커니즘의 관계에 코드를 연결하여 결정을 내릴 때 모놀리스를 분리하기가 매우 어렵습니다.
istepaniuk

15

활성 레코드의 가장 큰 단점은 일반적으로 도메인이 특정 지속성 메커니즘과 밀접하게 연결되어 있다는 것입니다. 이 메커니즘이 파일 기반에서 DB 기반 지속성 또는 데이터 액세스 프레임 워크간에 전체적인 변경이 필요한 경우이 패턴을 구현하는 모든 클래스가 변경 될 수 있습니다. 언어, 프레임 워크 및 디자인에 따라 DB의 위치 나 "소유자"위치를 변경하는 것만 큼 간단한 방법으로도 데이터 액세스 방법을 업데이트하기 위해 모든 객체를 거쳐야 할 수도 있습니다 (액세스가 쉬운 대부분의 언어에서는 드문 경우입니다) 연결 문자열로 파일을 구성하려면).

또한 일반적으로 반복해야합니다. 대부분의 지속성 메커니즘에는 DB에 연결하고 트랜잭션을 시작하기위한 많은 공통 코드가 있습니다. DRY (Do n't Repeat Yourself)는 이러한 논리를 중앙 집중화하는 코더로 알려줍니다.

또한 원자 연산이 까다로워집니다. 개체 그룹을 전혀 또는 전혀없는 방식으로 저장해야하는 경우 (예 : 송장 및 해당 송장 라인 및 / 또는 고객 및 / 또는 GL 항목), 한 객체는 다른 모든 객체에 대해 알고 지속성을 제어해야합니다 ( 상호 연결된 큰 레코드는 종속성에 대한 모든 것을 알고있는 "신 (神) 객체"가 될 수 있습니다.) 또는 전체 트랜잭션에 대한 제어는 도메인 외부에서 처리해야합니다 (이 경우 사용하는 이유) AR?)

또한 객체 지향 관점에서 "잘못된"것입니다. 실제로는 송장 자체를 제출하는 방법을 모르므로 송장 코드 객체가 DB에 자신을 저장하는 방법을 알고있는 이유는 무엇입니까? 물론,“실제로 대응할 수있는 것만 모델링해야하는 객체”에 대한 지나치게 종교적인 준수는 빈혈 도메인 모델로 이어질 것입니다. 다른 대상은 일반적으로 나쁜 생각으로 간주됩니다).


매우 간단한 키워드 나 명령 뒤에 지속성을 정의하거나 추상화 할 수있는 Ruby에서는 문제가 거의 없습니다. 다양한 지속성 메커니즘을 설정하기 위해 더 많은 LoC가 필요한 .NET에서, 특히 여러 오브젝트를 하나의 원자 트랜잭션에 저장해야하는 경우 각 오브젝트에 대해 SQL 연결을 갖는 것이 일반적으로 중복됩니다. 인보이스를 저장하든 고객을 저장하든 DB에 연결하는 방법은 동일합니다. 공통 항목을 코드베이스의 모든 ActiveRecord 객체에 공통 인 기본 클래스로 추상화하거나 자체 클래스 (리포지토리)로 추출합니다.
KeithS

요점을 설명하는 Ruby ActiveRecord 사용법의 예를 제공 할 수 있습니까? 이러한 문제는 발생하지 않았지만 응용 프로그램은 상당히 작습니다. Ruby로 마이그레이션을 작성하여 다른 DB 구현에 배포 할 수 있습니다. Ruby의 ActiveRecord가 반복을 제거하는 데 매우 유용하다는 것을 알았으므로 ActiveRecord를 사용하면 반대 효과가 있다고 주장하는 이유를 알 수 없습니다. 저장에 문제가 없었습니다. 방금 객체 모델을 수정하고 AR이 객체 모델을 반영하도록 DB를 업데이트하도록했습니다.
케빈 클라인

2

기본적인 단점은 비즈니스 로직뿐만 아니라 지속성 정보를 보유하기 때문에 도메인 모델을 복잡 하게 만드는 것 입니다.

따라서 솔루션은 ORM 의 Data Mapper 구현을 사용하는 것입니다 . 이는 지속성 계층을 분리하고 이제 엔티티 비즈니스 로직에 더 중점을 둡니다. 교리데이터 매퍼 ORM입니다.

그러나이 방법에는 약간의 복잡성도 있습니다. 이제 쿼리의 경우 데이터 매퍼에 너무 의존하여 쿼리 지향 환경을 만듭니다. 이를 단순화하기 위해 도메인 모델데이터 매퍼 사이에 다른 계층이 도입되었으며 이를 Repository 라고 합니다.

저장소 는 지속성 계층을 추상화합니다. 그것은 객체 지향 프로그래밍을 의미합니다. 데이터베이스 테이블에 저장된 모든 엔티티와 같은 모든 유형의 객체를 수집하며 수집 작업, 추가 , 제거 로 객체에 대한 작업을 수행 할 수 있습니다 . 등을 포함

예를 들어 User Entity의 경우 작업 을 수행 할 수있는 동일한 유형의 사용자 개체 (users 테이블에 저장 됨)의 컬렉션을 나타내는 UserRepository 가 있습니다 . users 테이블에 쿼리하기 위해 User Data Mapper를 사용 하지만 도메인 모델 User로 추상화했습니다 .

리포지토리 패턴데이터 액세스 계층의 유형이고 , 다른 하나는 데이터 액세스 개체 만의 차이점입니다. 리포지토리 에는 집계 루트 기능이 있습니다


리포지토리DAO 패턴이 다르면 DAO는 일반 데이터 액세스이며 리포지토리는 모든 동일한 유형의 개체를 지속적으로 수집합니다. 즉, 모든 리포지토리에는 동일한 인터페이스 (예 : 배열 또는 목록)가 있어야합니다. 방법의 다른 왕은 저장소에 속하지 않습니다 . DAO가 낮은 수준 인 경우 리포지토리에서 DAO를 사용할 수 있습니다. 프로그래머 (특히 PHP)는 리포지토리를 DAO로 사용하지만 종종 올바르지 않습니다.
xmedeko
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.