문제

애플리케이션이 3계층으로 나누어져 있다고 가정해 보겠습니다.GUI, 비즈니스 로직 및 데이터 액세스.비즈니스 논리 계층에서 비즈니스 개체를 설명했습니다.getter, setter, 접근자 등...당신은 아이디어를 얻습니다.비즈니스 논리 계층에 대한 인터페이스는 비즈니스 논리의 안전한 사용을 보장하므로 호출하는 모든 메서드와 접근자가 입력의 유효성을 검사합니다.

신뢰할 수 있는 인터페이스가 깔끔하게 정의되어 있기 때문에 UI 코드를 처음 작성할 때 이는 매우 좋습니다.

그러나 여기에서 까다로운 부분이 발생합니다. 데이터 액세스 계층 작성을 시작할 때 비즈니스 로직에 대한 인터페이스가 사용자의 요구를 수용하지 않는다는 것입니다.숨겨져 있거나 사용되는 필드를 설정하려면 더 많은 접근자와 게터가 필요합니다.이제 비즈니스 로직의 인터페이스를 침식해야 합니다.이제 UI 레이어에는 비즈니스 설정이 없는 UI 레이어의 필드를 설정할 수 있습니다.

데이터 액세스 계층에 필요한 변경으로 인해 비즈니스 로직에 대한 인터페이스가 유효하지 않은 데이터로 비즈니스 로직을 설정하는 것조차 가능한 지점까지 침식되었습니다.따라서 인터페이스는 더 이상 안전한 사용을 보장하지 않습니다.

문제를 충분히 명확하게 설명했으면 좋겠습니다.인터페이스 침식을 방지하고, 정보 숨김 및 캡슐화를 유지하면서, 다양한 계층 간의 다양한 인터페이스 요구 사항을 어떻게 수용할 수 있습니까?

도움이 되었습니까?

해결책

제가 질문을 올바르게 이해했다면 귀하는 도메인 모델을 만들었고 데이터베이스의 레코드와 도메인 개체 사이를 매핑하기 위해 개체 관계형 매퍼를 작성하고 싶을 것입니다.그러나 개체의 필드를 읽고 쓰는 데 필요한 '배관' 코드로 도메인 모델을 오염시키는 것에 대해 우려하고 있습니다.

한발 뒤로 물러서면 기본적으로 데이터 매핑 코드를 어디에 넣을지 두 가지 선택이 있습니다. 즉, 도메인 클래스 자체 내에서 또는 외부 매핑 클래스에서 선택할 수 있습니다.첫 번째 옵션은 Active Record 패턴이라고도 하며 각 개체가 자체적으로 지속하는 방법을 알고 내부 구조에 대한 충분한 액세스 권한을 갖고 있어 비즈니스와 관련되지 않은 필드를 노출하지 않고도 매핑을 수행할 수 있다는 장점이 있습니다.

예:

public class User
{
    private string name;
    private AccountStatus status;

    private User()
    {
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public AccountStatus Status
    {
        get { return status; }
    }

    public void Activate()
    {
        status = AccountStatus.Active;
    }

    public void Suspend()
    {
        status = AccountStatus.Suspended;
    }

    public static User GetById(int id)
    {
        User fetchedUser = new User();

        // Lots of database and error-checking code
        // omitted for clarity
        // ...

        fetchedUser.name = (string) reader["Name"];
        fetchedUser.status = (int)reader["statusCode"] == 0 ? AccountStatus.Suspended : AccountStatus.Active;

        return fetchedUser;
    }

    public static void Save(User user)
    {
        // Code to save User's internal structure to database
        // ...
    }
}

이 예에는 이름과 AccountStatus를 가진 사용자를 나타내는 개체가 있습니다.우리는 상태를 직접 설정하는 것을 허용하고 싶지 않습니다. 아마도 변경 사항이 유효한 상태 전환인지 확인하고 싶기 때문에 설정자가 없기 때문일 것입니다.다행히 GetById 및 Save 정적 메서드의 매핑 코드는 개체의 이름 및 상태 필드에 대한 전체 액세스 권한을 가집니다.

두 번째 옵션은 매핑을 담당하는 두 번째 클래스를 갖는 것입니다.이는 비즈니스 논리와 지속성의 다양한 문제를 분리하여 디자인을 보다 테스트 가능하고 유연하게 만들 수 있다는 장점이 있습니다.이 방법의 과제는 이름 및 상태 필드를 외부 클래스에 노출하는 방법입니다.일부 옵션은 다음과 같습니다:1.반사를 사용하십시오 (물체의 개인 부품에 깊이 파고 들지 않는 자격이 없습니다) 2.특별히 명명된 공개 설정자를 제공합니다(예:'비공개'라는 단어로 접두사를 접두사하고 우연히 사용하지 않기를 바랍니다.귀하의 언어가 이를 지원하는 경우 setter를 내부로 만들고 데이터 매퍼 모듈 액세스 권한을 부여하십시오.예:.NET 2.0 이상에서는 InternalsVisibleToAttribute를 사용하고 C++에서는 친구 함수를 사용하세요.

더 자세한 내용은 마틴 파울러(Martin Fowler)의 고전 '엔터프라이즈 아키텍처 패턴(Patterns of Enterprise Architecture)'을 추천합니다.

그러나 경고의 말씀으로, 자신만의 매퍼를 작성하기 전에 nHibernate 또는 Microsoft의 Entity Framework와 같은 타사 ORM(객체 관계형 매퍼) 도구를 사용해 보는 것이 좋습니다.저는 여러 가지 이유로 자체 매퍼를 작성하는 네 가지 프로젝트에 참여했는데 최종 사용자에게 가치를 제공하는 코드를 작성하는 대신 매퍼를 유지 관리하고 확장하는 데 많은 시간을 낭비하기가 매우 쉽습니다.나는 지금까지 한 프로젝트에서 nHibernate를 사용해 보았고 처음에는 꽤 가파른 학습 곡선을 보였지만 초기에 투자한 만큼 상당한 성과를 거두었습니다.

다른 팁

이는 도메인 모델을 데이터베이스 모델과 분리하는 전형적인 문제입니다.이를 공격하는 방법에는 여러 가지가 있습니다. 제 생각에는 프로젝트 규모에 따라 다릅니다.다른 사람들이 말했듯이 저장소 패턴을 사용할 수 있습니다..net 또는 Java를 사용하는 경우 다음을 사용할 수 있습니다. NH절전 모드 또는 최대 절전 모드.

내가하는 일은 사용하는 것입니다 테스트 주도 개발 그래서 먼저 UI와 모델 레이어를 작성하고 데이터 레이어를 조롱합니다. 따라서 UI와 모델은 도메인 특정 개체를 중심으로 구축된 다음 나중에 이러한 개체를 데이터 레이어를 사용하는 기술에 매핑합니다.데이터베이스가 앱 디자인을 결정하고, 앱을 먼저 작성하고 데이터에 대해서는 나중에 생각하도록 하는 것은 매우 나쁜 생각입니다.

ps. 질문 제목이 약간 오해의 소지가 있습니다.

@Ice^^더위:

데이터 계층이 비즈니스 논리 계층을 인식해서는 안 된다는 것은 무엇을 의미합니까?비즈니스 객체를 데이터로 어떻게 채우겠습니까?

UI는 비즈니스 계층의 ServiceClass에 서비스를 요청합니다. 즉, 필요한 매개변수 데이터가 있는 개체로 필터링된 개체 목록을 가져옵니다.
그런 다음 ServiceClass는 데이터 계층에 저장소 클래스 중 하나의 인스턴스를 생성하고 GetList(ParameterType 필터)를 호출합니다.
그런 다음 데이터 계층은 데이터베이스에 액세스하여 데이터를 가져와 "도메인" 어셈블리에 정의된 일반 형식에 매핑합니다.
BL은 이 데이터와 더 이상 관련이 없으므로 이를 UI에 출력합니다.

그런 다음 UI는 항목 X를 편집하려고 합니다.항목(또는 비즈니스 개체)을 비즈니스 계층의 서비스로 보냅니다.비즈니스 계층은 개체의 유효성을 검사하고, 문제가 없으면 저장을 위해 데이터 계층으로 보냅니다.

UI는 데이터 계층에 대해 다시 알고 있는 비즈니스 계층의 서비스를 알고 있습니다.

UI는 사용자 데이터 입력을 개체에 매핑하는 역할을 하며, 데이터 계층은 db의 데이터를 개체에 매핑하는 역할을 합니다.비즈니스 계층은 순전히 비즈니스로 유지됩니다.:)

인터페이스를 침식하지 않으므로 해결책이 될 수 있습니다.다음과 같은 수업을 할 수 있을 것 같습니다.

public class BusinessObjectRecord : BusinessObject
{
}

나는 항상 다음을 포함하는 별도의 어셈블리를 만듭니다.

  • 많은 작은 인터페이스(ICreateRepository, IReadRepository, IReadListRepsitory를 생각해 보세요..목록은 계속되고 있으며 대부분은 제네릭에 크게 의존합니다.)
  • IReadRepository에서 상속되는 IPersonRepository와 같은 많은 구체적인 인터페이스를 보면 요점을 알 수 있습니다.
    더 작은 인터페이스만으로는 설명할 수 없는 모든 것을 구체적인 인터페이스에 넣습니다.
    IPersonRepository를 사용하여 개체를 선언하는 한 깨끗하고 일관된 인터페이스를 얻을 수 있습니다.하지만 중요한 점은 f.x를 수강하는 수업을 만들 수도 있다는 것입니다.생성자에 ICreateRepository가 있으므로 코드를 사용하면 정말 펑키한 작업을 수행하기가 매우 쉬워집니다.여기에는 비즈니스 계층의 서비스에 대한 인터페이스도 있습니다.
  • 마침내 코드 베이스 자체를 좀 더 깔끔하고 느슨하게 결합하기 위해 모든 도메인 객체를 추가 어셈블리에 집어넣었습니다.이러한 개체에는 논리가 없으며 모든 3개 이상의 레이어에 대한 데이터를 설명하는 일반적인 방법일 뿐입니다.

그런데.데이터 계층을 수용하기 위해 비즈니스 논리 계층에서 메서드를 정의하는 이유는 무엇입니까?
데이터 계층은 비즈니스 계층이 있는지조차 알 이유가 없어야 합니다.

데이터 계층이 비즈니스 논리 계층을 인식해서는 안 된다는 것은 무엇을 의미합니까?비즈니스 객체를 데이터로 어떻게 채우겠습니까?

나는 종종 이렇게 합니다:

namespace Data
{
    public class BusinessObjectDataManager
    {
         public void SaveObject(BusinessObject object)
         {
                // Exec stored procedure
         {
    }
}

그렇다면 문제는 비즈니스 계층이 데이터 계층에 더 많은 기능을 노출해야 한다는 것인데, 이 기능을 추가한다는 것은 UI 계층에 너무 많은 것을 노출한다는 뜻인가요?제가 귀하의 문제를 올바르게 이해하고 있다면 단일 인터페이스로 너무 많은 것을 만족시키려고 하여 인터페이스가 복잡해지는 것처럼 들립니다.비즈니스 계층에 두 개의 인터페이스를 두지 않는 이유는 무엇입니까?하나는 UI 레이어를 위한 간단하고 안전한 인터페이스입니다.다른 하나는 데이터 계층을 위한 하위 수준 인터페이스입니다.

UI와 데이터 영역 모두에 전달해야 하는 모든 개체에 이 두 인터페이스 접근 방식을 적용할 수도 있습니다.

public class BusinessLayer : ISimpleBusiness
{}

public class Some3LayerObject : ISimpleSome3LayerObject
{}

인터페이스를 다음과 같은 두 가지 유형으로 나눌 수 있습니다.

  • 보기 인터페이스 - UI와의 상호 작용을 지정하는 인터페이스입니다.
  • 데이터 인터페이스 - 데이터와의 상호 작용을 지정할 수 있는 인터페이스입니다.

다음과 같이 두 인터페이스 세트를 모두 상속하고 구현할 수 있습니다.

public class BusinessObject : IView, IData

이렇게 하면 데이터 계층에서는 IData의 인터페이스 구현만 확인하면 되고, UI에서는 IView의 인터페이스 구현만 확인하면 됩니다.

사용할 수 있는 또 다른 전략은 UI 또는 데이터 레이어에서 객체가 해당 레이어에서만 소비되도록 구성하는 것입니다. 예:

public class BusinessObject : DomainObject

public class ViewManager<T> where T : DomainObject

public class DataManager<T> where T : DomainObject

그러면 비즈니스 개체가 UI/뷰 계층과 데이터 계층 모두를 무시할 수 있습니다.

나는 계속해서 틀에 어긋나는 습관을 갖고 왜 이렇게 끔찍하게 복잡한 객체 레이어를 모두 구축하는지 질문해야 한다고 말할 것입니다.

많은 개발자들이 데이터베이스를 객체에 대한 단순한 지속성 레이어로 생각하고 해당 객체에 필요한 CRUD 작업에만 관심을 갖고 있다고 생각합니다.객체 모델과 관계형 모델 사이의 "임피던스 불일치"에 너무 많은 노력이 들어가고 있습니다.아이디어는 다음과 같습니다.노력을 중단하세요.

데이터를 캡슐화하는 저장 프로시저를 작성합니다.데이터베이스와 상호 작용하려면 코드에서 필요에 따라 결과 세트, DataSet, DataTable, SqlCommand(또는 java/php/동등한 항목)를 사용하십시오.그러한 개체는 필요하지 않습니다.훌륭한 예는 SqlDataSource를 .ASPX 페이지에 포함하는 것입니다.

누구에게도 데이터를 숨기려고 해서는 안 됩니다.개발자는 실제 데이터 저장소와 상호 작용하는 방법과 시기를 정확히 이해해야 합니다.

객체 관계형 매퍼는 악마입니다.사용을 중단하세요.

엔터프라이즈 애플리케이션을 구축하는 것은 종종 복잡성을 관리하는 연습입니다.가능한 한 단순하게 유지해야 합니다. 그렇지 않으면 유지 관리가 전혀 불가능한 시스템을 갖게 됩니다.(어쨌든 모든 애플리케이션에 내재된) 일부 결합을 허용하려는 경우 비즈니스 논리 계층과 데이터 액세스 계층을 모두 제거할 수 있으며(저장 프로시저로 대체) 이들 중 어느 것도 필요하지 않습니다. 인터페이스.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top