OOP에 대해 점점 더 많이 배우고 다양한 디자인 패턴을 구현하기 시작하면서 사람들이 Active Record에서 싫어하는 사례를 계속해서 떠 올립니다 .
종종 사람들은 그것이 잘 확장되지 않는다고 말하지만 (트위터를 그들의 주된 예로서 인용)-아무도 실제로 왜 잘 확장되지 않는지 설명 하지 않습니다. 및 / 또는 단점없이 AR의 장점을 달성하는 방법 (비슷하지만 다른 패턴을 통해?)
바라건대 이것은 디자인 패턴에 대한 성스러운 전쟁으로 바뀌지 않을 것입니다. 제가 알고 싶은 것은 **** 구체적으로 **** Active Record의 문제점입니다.
확장이 잘되지 않는다면 왜 안됩니까?
다른 어떤 문제가 있습니까?
답변
거기에 액티브는 디자인 패턴 및 액티브 레일 ORM 라이브러리 및 .NET 용 노크 오프, 다른 언어의 톤도있다.
이것들은 모두 다른 것입니다. 그들은 대부분 그 디자인 패턴을 따르지만 다양한 방법으로 확장하고 수정합니다. 그래서 누군가 “ActiveRecord Sucks”라고 말하기 전에 “어떤 ActiveRecord, 힙이 있습니까?”라고 말하여 자격을 부여해야합니다.
저는 Rails의 ActiveRecord에만 익숙합니다. 사용과 관련하여 제기 된 모든 불만 사항을 해결하려고합니다.
@BlaM
내가 Active Records에서 보는 문제는 항상 하나의 테이블에 불과하다는 것입니다.
암호:
class Person
belongs_to :company
end
people = Person.find(:all, :include => :company )
이렇게하면를 사용하여 SQL이 생성 LEFT JOIN companies on companies.id = person.company_id
되고 연결된 Company 개체가 자동으로 생성되므로 people.first.company
데이터가 이미 존재하므로 데이터베이스에 접속할 필요가 없습니다.
멍멍
Active Record의 내재 된 문제는 데이터베이스 쿼리가 자동으로 생성되고 실행되어 개체를 채우고 데이터베이스 레코드를 수정한다는 것입니다.
암호:
person = Person.find_by_sql("giant complicated sql query")
추악하기 때문에 권장하지 않지만 단순하고 단순하게 원시 SQL을 작성해야하는 경우에는 쉽게 수행 할 수 있습니다.
@Tim Sullivan
… 모델의 여러 인스턴스를 선택하면 기본적으로 “select * from …”을 수행합니다.
암호:
people = Person.find(:all, :select=>'name, id')
이렇게하면 데이터베이스에서 이름과 ID 열만 선택되고 매핑 된 개체의 다른 모든 ‘속성’은 해당 개체를 수동으로 다시로드하지 않는 한 nil이됩니다.
답변
저는 항상 ActiveRecord가 모델이 상대적으로 평탄한 (클래스 계층 구조가 많지 않은) 빠른 CRUD 기반 애플리케이션에 적합하다는 것을 발견했습니다. 그러나 복잡한 OO 계층이있는 응용 프로그램의 경우 DataMapper 가 더 나은 솔루션 일 수 있습니다. ActiveRecord는 테이블과 데이터 개체 간의 1 : 1 비율을 가정하지만 이러한 종류의 관계는 더 복잡한 도메인에서는 다루기 어렵습니다. 패턴에 대한 그의 책에서 Martin Fowler는 ActiveRecord가 모델이 상당히 복잡한 조건에서 분해되는 경향이 있다고 지적 하고 대안 으로 DataMapper 를 제안합니다 .
나는 이것이 실제로 사실임을 발견했습니다. 도메인에 상속이 많은 경우 연결 또는 구성을 매핑하는 것보다 상속을 RDBMS에 매핑하는 것이 더 어렵습니다.
내가하는 방법은 컨트롤러가 이러한 DataMapper (또는 “서비스 계층”) 클래스를 통해 액세스하는 “도메인”개체를 갖는 것입니다. 이들은 데이터베이스를 직접 미러링하지 않지만 일부 실제 개체에 대한 OO 표현으로 작동합니다. 도메인에 User 클래스가 있고 해당 User 개체를 검색 할 때 이미로드 된 다른 개체에 대한 참조 또는 컬렉션이 있어야한다고 가정합니다. 데이터는 여러 다른 테이블에서 가져올 수 있으며 ActiveRecord 패턴으로 인해 매우 어려울 수 있습니다.
User 객체를 직접로드하고 ActiveRecord 스타일 API를 사용하여 데이터에 액세스하는 대신 컨트롤러 코드는 예를 들어 UserMapper.getUser () 메서드의 API를 호출하여 User 객체를 검색합니다. 각각의 테이블에서 연결된 개체를로드하고 완료된 사용자 “도메인”개체를 호출자에게 반환하는 것은 매퍼입니다.
기본적으로 코드를보다 쉽게 관리 할 수 있도록 다른 추상화 계층을 추가하는 것입니다. DataMapper 클래스에 원시 사용자 지정 SQL이 포함되어 있든, 데이터 추상화 계층 API에 대한 호출이 있든, 심지어 ActiveRecord 패턴 자체에 액세스하든 상관없이 멋지고 채워진 User 개체를 수신하는 컨트롤러 코드에는 중요하지 않습니다.
어쨌든, 그렇게 해요.
답변
사람들이 ActiveRecord를 “미워하는”이유와 “잘못된”이유 사이에는 매우 다른 이유가있을 수 있다고 생각합니다.
증오 문제에 관해서는 Rails와 관련된 모든 것에 대한 많은 독이 있습니다. 문제가있는 한 모든 기술과 같을 가능성이 높으며 좋은 선택이되는 상황과 더 나은 선택이있는 상황이 있습니다. 내 경험상 Rails ActiveRecord의 대부분의 기능을 활용하지 못하는 상황은 데이터베이스가 잘못 구조화 된 경우입니다. 데이터에 액세스하는 데 필요한 많은 저장 프로 시저가있는 첫 번째 일반 형식을 위반하는 기본 키없이 데이터에 액세스하는 경우 SQL 래퍼에 가까운 것을 사용하는 것이 좋습니다. 데이터베이스가 비교적 잘 구조화 된 경우 ActiveRecord를 사용하면이를 활용할 수 있습니다.
코드 스 니펫을 다시 결합하여 ActiveRecord에서 일이 어렵다고 말하는 댓글 작성자에게 응답하는 주제에 추가하려면
@Sam McAfee 도메인에 User 클래스가 있고 해당 User 개체를 검색 할 때 이미로드 된 다른 개체에 대한 참조 또는 컬렉션이 있어야한다고 가정합니다. 데이터는 여러 다른 테이블에서 가져올 수 있으며 ActiveRecord 패턴으로 인해 매우 어려울 수 있습니다.
user = User.find(id, :include => ["posts", "comments"])
first_post = user.posts.first
first_comment = user.comments.first
include 옵션을 사용하여 ActiveRecord를 사용하면 기본 지연로드 동작을 재정의 할 수 있습니다.
답변
내 길고 늦은 대답은 완전하지는 않지만 좋은 설명입니다. 왜 내가이 패턴, 의견 및 일부 감정을 싫어하는지 :
1) 짧은 버전 : Active Record 는 데이터베이스와 애플리케이션 코드 사이 에 ” 강력한 바인딩 ” 의 ” 박층 “을 만듭니다 . 논리적, 문제, 문제를 전혀 해결하지 않습니다. IMHO 는 프로그래머를위한 일부 구문 설탕 을 제외하고는 어떤 값도 제공하지 않습니다 (그런 다음 “객체 구문”을 사용하여 관계형 데이터베이스에 존재하는 일부 데이터에 액세스 할 수 있음). 프로그래머를위한 약간의 편안함을 만들기위한 노력은 (IMHO …) 낮은 수준의 데이터베이스 액세스 도구에 더 잘 투자되어야합니다. 예를 들어 단순하고 쉬우 며 평범 하고 유사한 방법 의 일부 변형 (물론 개념과 우아함은 사용 된 언어).hash_map get_record( string id_value, string table_name, string id_column_name="id" )
2) 긴 버전 : 내가 사물을 “개념적으로 제어”할 수있는 데이터베이스 기반 프로젝트에서 AR을 피했고 좋았습니다. 저는 보통 계층화 된 아키텍처를 구축합니다 (적어도 중대형 프로젝트에서는 조만간 소프트웨어를 계층으로 나눕니다).
A1) 데이터베이스 자체, 테이블, 관계, 심지어 DBMS가 허용하는 경우 일부 논리 (MySQL도 이제 어른이 됨)
A2) 매우 자주, 데이터 저장소 이상의 것이 있습니다. 파일 시스템 (데이터베이스의 Blob이 항상 좋은 결정은 아닙니다 …), 레거시 시스템 ( “어떻게”액세스 할 것인지 상상해보십시오. 다양한 종류가 가능합니다 .. 요점이 아니라 …)
B) 데이터베이스 액세스 계층 (이 수준에서 도구 방법, 데이터베이스의 데이터에 쉽게 액세스 할 수있는 도우미는 매우 환영하지만 AR은 일부 구문 설탕을 제외하고 여기에 어떤 가치도 제공하지 않습니다)
C) 애플리케이션 객체 레이어 : “애플리케이션 객체”는 때때로 데이터베이스에있는 테이블의 단순한 행이지만 대부분은 어쨌든 복합 객체이고 더 높은 로직이 연결되어 있으므로이 수준에서 AR 객체에 시간을 투자하는 것은 분명히 쓸모가 없습니다. , 귀중한 코더의 시간 낭비입니다. 왜냐하면 “실제 가치”, 이러한 객체의 “높은 논리”가 AR 객체 위에 구현되어야하기 때문입니다. AR의 유무에 관계없이! 예를 들어 “로그 항목 개체”를 추상화하려는 이유는 무엇입니까? 앱 로직 코드가이를 작성하지만 업데이트하거나 삭제할 수 있어야합니까? 어리석게 들리며 App::Log("I am a log message")
사용하기 더 쉬운le=new LogEntry(); le.time=now(); le.text="I am a log message"; le.Insert();
. 예를 들어, 애플리케이션의 로그보기에서 “로그 항목 개체”를 사용하면 100, 1000 또는 10000 개의 로그 행에서 작동하지만 조만간 최적화해야합니다. 대부분의 경우에는 많은 코드를 감싸고 숨기는 단단한 고정 AR 아이디어 프레임에 작은 문을 래핑하는 대신 앱 로직에서 작고 아름다운 SQL SELECT 문을 사용하십시오 (AR 아이디어를 완전히 깨뜨립니다 ..). AR 코드 작성 및 / 또는 작성에 낭비 된 시간은 로그 항목 목록을 읽기위한 훨씬 더 영리한 인터페이스에 투자되었을 수 있습니다 (다양한 방법으로 하늘이 한계입니다). 코더는 의도 한 애플리케이션에 맞는 애플리케이션 로직을 실현하기 위해 새로운 추상화를 발명 해야하며 어리석은 패턴을 어리석게 다시 구현하지 않아야합니다., 첫눈에 좋은 소리!
D) 응용 프로그램 논리-상호 작용하는 객체의 논리를 구현하고 응용 프로그램 논리 객체를 생성, 삭제 및 나열 (!)합니다 (아니요, 이러한 작업은 응용 프로그램 논리 객체 자체에 거의 고정되어서는 안됩니다. 책상 위의 종이가 말합니까? 사무실에있는 다른 모든 시트의 이름과 위치를 알고 계십니까? 객체를 나열하는 “정적”방법을 잊어 버리십시오. 인간의 사고 방식을 [일부 AR 프레임 워크와 유사하지 않은 -] AR 사고)
E) 사용자 인터페이스-글쎄요, 다음 줄에 쓸 내용은 매우 주관적이지만 제 경험상 AR을 기반으로 구축 된 프로젝트는 애플리케이션의 UI 부분을 무시하는 경우가 많았습니다. 모호한 추상화를 만드는 데 시간이 낭비되었습니다. . 결국 이러한 응용 프로그램은 많은 코더의 시간을 낭비하고 내부 및 외부 기술에 관심이있는 코더 용 코더의 응용 프로그램처럼 느껴집니다. 코더는 기분이 좋아지고 (종이의 개념에 따라 모든 것이 끝났고 정확 해졌습니다.) 고객은 “프로페셔널”이기 때문에 “그렇게해야한다는 것을 배워야합니다”.. 좋아, 미안하다, 나는 빗나 갔다 😉
음,이 모든 것은 주관적이지만 제 경험입니다 (Ruby on Rails 제외, 다를 수 있으며 해당 접근 방식에 대한 실제 경험이 없습니다).
유료 프로젝트에서 나는 종종 더 높은 수준의 애플리케이션 로직을위한 빌딩 블록으로 “활성 레코드”개체를 만드는 것으로 시작하라는 요구를 들었습니다. 내 경험상 이것은 눈에 띄게 자주고객 (대부분의 경우 소프트웨어 개발 회사)이 최종적으로 제품이되어야 할 제품에 대한 좋은 개념, 큰 견해, 개요를 가지고 있지 않은 것에 대한 일종의 변명이었습니다. 이러한 고객은 견고한 프레임 ( “10 년 전 프로젝트에서는 잘 작동했습니다 ..”)으로 생각하고 엔티티를 구체화하고 엔티티 관계를 정의하고 데이터 관계를 분해하고 기본 애플리케이션 로직을 정의 할 수 있지만 그런 다음 중지합니다. 그리고 그것을 당신에게 넘겨주고, 당신이 필요로하는 전부라고 생각합니다 … 그들은 종종 애플리케이션 로직, 사용자 인터페이스, 유용성 등의 완전한 개념이 부족합니다 … 그들은 큰 시야가 부족하고 그들은 AR 방식을 따르기를 원합니다. 왜냐하면 .. 음, 왜 몇 년 전에 그 프로젝트에서 효과가 있었는데 사람들을 바쁘고 조용하게 유지하기 때문입니까? 모르겠어요. 그러나 “세부 사항” 남자들과 남자들을 분리하거나 .. 원래의 광고 슬로건은 어땠나요? 😉
수년 (10 년의 활발한 개발 경험) 후 고객이 “활성 레코드 패턴”을 언급 할 때마다 알람 벨이 울립니다. 나는 그들을 본질적인 개념 단계로 되돌리려 고 노력하고, 두 번 생각하게하고, 그들의 개념적 약점을 보여 주도록 시도하거나, 그들이 무분별하다면 아예 피하는 법을 배웠습니다. 원하는 것이 무엇인지 알고, 알지만 모른다고 생각하거나 무료로 개념 작업을 ME에 외부화하려고 시도하면 많은 귀중한 시간, 며칠, 몇 주 및 몇 달이 소요되며 라이브가 너무 짧습니다 …).
그래서 마지막으로,이 모든 것이 내가 그 어리석은 “액티브 레코드 패턴”을 싫어하는 이유이며, 가능할 때마다이를 피할 것입니다.
편집 : 나는 이것을 No-Pattern이라고 부를 것입니다. 문제를 해결하지 못합니다 (패턴은 구문 설탕을 생성하기위한 것이 아닙니다). 그것은 많은 문제를 일으킨다. (여기에 많은 답변에서 언급 된) 모든 문제의 근원은 패턴 정의에 의해 극도로 제한된 인터페이스 뒤에 좋은 오래된 잘 개발되고 강력한 SQL을 숨기는 것입니다.
이 패턴은 유연성을 구문 적 설탕으로 대체합니다!
AR이 어떤 문제를 해결해 줄까요?
답변
일부 메시지가 혼란스러워집니다. 일부 답변은 “ORM”대 “SQL”또는 이와 유사한 것입니다.
사실 AR은 도메인 개체를 활용하여 데이터베이스 액세스 코드를 작성하는 단순화 프로그래밍 패턴 일뿐입니다.
이러한 개체에는 일반적으로 비즈니스 속성 (빈 속성)과 일부 동작 (일반적으로 이러한 속성에서 작동하는 메서드)이 있습니다.
AR은 데이터베이스 관련 작업에 “이러한 도메인 개체에 몇 가지 메서드를 추가”라고 말합니다.
그리고 저는 제 의견과 경험에서 패턴이 마음에 들지 않는다고 말해야합니다.
첫눈에 꽤 좋은 소리를 낼 수 있습니다. Spring Roo와 같은 일부 최신 Java 도구는이 패턴을 사용합니다.
저에게 진짜 문제는 OOP 문제입니다. AR 패턴은 어떤 방식 으로든 객체에서 인프라 객체로의 종속성을 추가하도록합니다. 이러한 인프라 개체를 통해 도메인 개체는 AR에서 제안한 방법을 통해 데이터베이스를 쿼리 할 수 있습니다.
저는 항상 두 개의 레이어가 프로젝트 성공의 열쇠라고 말했습니다. 서비스 계층 (비즈니스 로직이 상주하거나 웹 서비스와 같은 일종의 원격 기술을 통해 내보낼 수있는 위치) 및 도메인 계층. 제 생각에는 AR 패턴을 해결하기 위해 도메인 레이어 개체에 일부 종속성 (실제로 필요하지 않음)을 추가하면 도메인 개체를 다른 레이어 또는 (드문) 외부 응용 프로그램과 공유하기가 더 어려워집니다.
AR의 Spring Roo 구현은 객체 자체에 의존하지 않고 일부 AspectJ 파일에 의존하기 때문에 흥미 롭습니다. 그러나 나중에 Roo로 작업하지 않고 프로젝트를 리팩터링해야하는 경우 AR 메서드가 도메인 개체에서 직접 구현됩니다.
또 다른 관점. 객체를 저장하기 위해 관계형 데이터베이스를 사용하지 않는다고 상상해보십시오. 예를 들어 애플리케이션이 도메인 객체를 NoSQL 데이터베이스 또는 XML 파일에 저장한다고 상상해보십시오. 도메인 개체에서 이러한 작업을 수행하는 메서드를 구현할까요? 나는 그렇게 생각하지 않는다 (예를 들어, XM의 경우 도메인 객체에 XML 관련 종속성을 추가 할 것입니다 … 정말 슬프다 고 생각합니다). 그렇다면 Ar 패턴이 말하는 것처럼 도메인 개체에서 관계형 DB 메서드를 구현해야하는 이유는 무엇입니까?
요약하자면 AR 패턴은 작고 단순한 애플리케이션에 더 간단하고 좋게 들릴 수 있습니다. 그러나 복잡하고 큰 앱이있을 때는 고전적인 계층 구조가 더 나은 접근 방식이라고 생각합니다.
답변
문제는 액티브 레코드 디자인 패턴에 관한 것입니다. orm 도구가 아닙니다.
원래 질문은 rails로 태그가 지정되어 있으며 Ruby on Rails로 구축 된 Twitter를 참조합니다. Rails 내의 ActiveRecord 프레임 워크는 Fowler의 Active Record 디자인 패턴을 구현 한 것입니다.
답변
Active Record에 대한 불만과 관련하여 제가 본 주요 사항은 테이블 주위에 모델을 만들고 모델의 여러 인스턴스를 선택할 때 기본적으로 “select * from …”을 수행한다는 것입니다. 이것은 레코드를 편집하거나 레코드를 표시하는 데 적합하지만 데이터베이스의 모든 연락처에 대한 도시 목록을 표시하려는 경우 “…에서 도시 선택”을 수행하고 도시 만 가져올 수 있습니다. . Active Record로이 작업을 수행하려면 모든 열을 선택해야하지만 City 만 사용해야합니다.
물론 다양한 구현에서는이를 다르게 처리합니다. 그럼에도 불구하고 한 가지 문제입니다.
이제, 당신이하려는 특정 일에 대한 새로운 모델을 만들어서이 문제를 해결할 수 있지만, 어떤 사람들은 그것이 이익보다 더 많은 노력이라고 주장 할 것입니다.
나, Active Record를 파고 있습니다. 🙂
HTH