Entity Framework 엔터티-웹 서비스의 일부 데이터-최고의 아키텍처?


10

우리는 현재 몇몇 웹 응용 프로그램에서 Entity Framework를 ORM으로 사용하고 있으며 지금까지 모든 데이터가 단일 데이터베이스에 저장되어 있기 때문에 우리에게 적합했습니다. 리포지토리 패턴을 사용하고 있으며이를 사용하는 서비스 (도메인 계층)가 있으며 EF 엔터티를 ASP.NET MVC 컨트롤러로 직접 반환합니다.

그러나 웹 서비스를 통해 타사 API를 활용해야하는 요구 사항이 생겨 데이터베이스의 사용자와 관련된 추가 정보가 제공됩니다. 로컬 사용자 데이터베이스에 추가 정보를 얻기 위해 to API를 제공 할 수있는 외부 ID를 저장합니다. 사용 가능한 정보가 상당히 많지만 간결성을 위해 그 중 하나는 사용자 회사 (이름, 관리자, 회의실, 직책, 위치 등)와 관련이 있습니다. 이 정보는 한 곳에서 사용되는 것이 아니라 웹 응용 프로그램의 여러 곳에서 사용됩니다.

제 질문은이 정보를 채우고 액세스하기에 가장 좋은 곳은 어디입니까? 다양한 곳에서 사용되므로 웹 응용 프로그램에서 사용하는 곳마다 임시로 가져 오는 것이 합리적이지 않으므로 도메인 레이어 에서이 추가 데이터를 반환하는 것이 좋습니다.

저의 초기 생각은 EF 엔터티 (EFUser)와 새로운 정보를 포함하는 새로운 'ApiUser'클래스를 포함하는 래퍼 모델 클래스를 만드는 것입니다. 사용자를 얻으면 EFUser를 얻은 다음 API에서 정보를 얻고 ApiUser 객체를 채 웁니다. 그러나 이것이 단일 사용자를 얻는 데는 좋지만 여러 사용자를 얻을 때 넘어집니다. 사용자 목록을 가져올 때 API를 사용할 수 없습니다.

두 번째 생각은 AFUser를 반환하는 EFUser 엔터티에 단일 메서드를 추가하고 필요할 때 채우는 것입니다. 이렇게하면 필요할 때만 액세스 할 수 있으므로 위의 문제가 해결됩니다.

또는 최종 생각은 데이터베이스에 데이터의 로컬 사본을 유지하고 사용자가 로그인 할 때이를 API와 동기화하는 것이 었습니다. 동기화 프로세스 일 뿐이므로 최소한의 작업 만 수행 할 수 있습니다. 사용자 정보를 얻을 때마다 DB 및 API 그러나 이는 두 장소에 데이터를 저장함을 의미하며, 한동안 로그인하지 않은 사용자의 경우 데이터가 오래되었음을 의미합니다.

이런 종류의 시나리오를 가장 잘 처리하는 방법에 대한 조언이나 제안이 있습니까?


it's not really sensible to fetch it on an ad-hoc basis-- 왜? 성능상의 이유로?
Robert Harvey

나는 임시 기반으로 API를 치는 것을 의미하지는 않습니다. 기존 엔티티 구조를 그대로 유지 한 다음 필요할 때 웹 응용 프로그램에서 API 임시를 호출한다는 의미입니다. 많은 장소에서 수행해야하므로 현명합니다.
stevehayter

답변:


3

너의 경우

귀하의 경우 세 가지 옵션이 모두 가능합니다. 가장 좋은 옵션은 아마도 asp.net 응용 프로그램이 알지 못하는 곳에서 데이터 소스를 동기화하는 것입니다. 즉, 매번 포 그라운드에서 두 페치를 피하고 API를 db와 자동으로 동기화하십시오. 그래서 그것이 귀하의 경우에 유효한 옵션이라면-그렇게 말합니다.

다른 답변과 같이 가져 오기를 '한 번'으로 만드는 솔루션은 응답을 어디에도 유지하지 않으므로 ASP.NET MVC는 모든 요청에 ​​대해 페치를 계속해서 가져옵니다.

나는 싱글 톤을 피할 것이며, 일반적인 이유로 많은 것이 좋은 생각이라고 생각하지 않습니다.

세 번째 옵션을 사용할 수 없는 경우 한 가지 옵션은 지연로드하는 것입니다. 즉, 클래스가 엔터티를 확장하고 필요 에 따라 API에 도달 하도록 합니다. 그것은 훨씬 더 마술적이고 명백한 상태이기 때문에 매우 위험한 추상화입니다.

실제로 몇 가지 질문으로 요약됩니다.

  • API 호출 데이터는 얼마나 자주 변경됩니까? 자주하지 않습니까? 세 번째 옵션. 자주? 갑자기 세 번째 옵션은 너무 실행 가능하지 않습니다. 나는 당신과 같은 임시 전화에 대해 확신하지 않습니다.
  • API 호출은 얼마나 비쌉니까? 통화 당 지불합니까? 그들은 빠릅니까? 비어 있는? 속도가 빠르면 매번 전화를 걸면 효과가있을 수 있습니다. 속도가 느리면 예측을해야하고 전화를해야합니다. 비용이 많이 든다면 캐싱에 큰 동기가됩니다.
  • 응답 시간은 얼마나 빠릅니까? 분명히 더 빠를수록 좋지만, 특히 사용자를 직접 대면하지 않는 경우 단순성을 위해 속도를 희생하는 것이 가치가있을 수 있습니다.
  • API 데이터와 데이터가 어떻게 다릅니 까? 그것들은 개념적으로 다른 두 가지입니까? 그렇다면 API 결과를 결과와 함께 직접 반환하는 대신 API를 외부에 노출시키는 것이 더 좋으며 상대방이 두 번째 호출을하여 관리하는 것을 허용하는 것이 좋습니다.

우려의 분리에 관한 한두 단어

Bobson이 우려의 분리에 대해 말하는 것에 대해 논쟁하도록하겠습니다. 하루가 끝날 때-그런 논리를 엔티티에 두는 것은 우려의 분리를 나쁘게 위반하는 것입니다.

이러한 리포지토리가 있으면 비즈니스 중심 계층에 프레젠테이션 중심 논리를 배치함으로써 문제 분리를 위반하는 것입니다 . 저장소는 이제 asp.net mvc 컨트롤러에서 사용자를 표시하는 방법과 같은 프리젠 테이션 관련 사항을 갑자기 인식합니다.

이 관련 질문 나는 컨트롤러에서 직접 개체를 액세스에 대해 질문했습니다. 거기에 대한 답변 중 하나를 인용하겠습니다.

"맞춤 피자 전문점 BigPizza에 오신 것을 환영합니다. 주문 하시겠습니까?" "글쎄, 나는 올리브 피자를 갖고 싶지만, 토마토 소스는 바닥에 치즈는 바닥에 놓고 오븐에서 90 분 동안 구워서 평평한 화강암 바위처럼 딱딱해질 때까지 기다렸다." "좋아요, 맞춤 피자는 우리의 직업입니다, 우리는 그것을 만들 것입니다."

출납원은 부엌에 간다. "카운터에 사이코가 있고, 피자를 갖고 싶어요. 화강암으로 된 바위입니다. 잠깐만 요 ... 먼저 이름이 필요합니다."그는 요리사에게 말합니다.

"아니오!"요리사가 소리를 지르며 "다시! ​​우리가 이미 시도한 것을 압니다." 그는 400 페이지로 된 종이를 쌓아 올렸습니다. "여기에는 2005 년부터 화강암 바위가 있었지만 ... 올리브는 없었지만 파프리카는 ... 대신에 토마토는 최고 였지만 고객은 원했습니다. 반 분만 구 웠어요. " "어쩌면 TopTomatoGraniteRockSpecial이라고해야합니까?" "그러나 그것은 바닥에 치즈를 고려하지 않습니다 ..."계산원 : "그게 스페셜이 표현 해야하는 것입니다." "하지만 피자 바위가 피라미드처럼 형성되는 것도 특별 할 것"이라고 요리사는 대답했다. "흠 ... 어려워요 ..."라고 절망적 인 계산원은 말합니다.

"나의 피자는 이미 오븐에 있습니까?", 갑자기 부엌 문을 통해 소리 쳤다. "이 토론을 중단하고이 피자를 만드는 방법을 알려주십시오. 우리는 두 번째로 그런 피자를 갖지 않을 것"이라고 요리사가 결정합니다. "좋아요. 올리브가 들어간 피자이지만 맨 위에는 토마토 소스와 바닥에 치즈는 검은 색과 딱딱한 화강암 바위처럼 딱딱해질 때까지 오븐에서 90 분간 굽습니다."

(답의 나머지 부분을 읽으십시오. 정말 좋은 imo입니다).

데이터베이스가 있다는 사실을 무시하는 것이 순진합니다. 데이터베이스 가 있습니다. 아무리 추상화하고 싶더라도 아무데도 가지 않습니다. 응용 프로그램 데이터 소스를 인식합니다. '핫 스왑'을 할 수 없습니다. ORM은 유용 하지만 해결하는 문제가 얼마나 복잡하고 많은 성능상의 이유로 인해 유출됩니다 (예 : Select n + 1과 같이).


@Benjamin의 철저한 답변에 감사드립니다. 처음에는 Bobson의 솔루션을 사용하여 프로토 타입을 만들기 시작했지만 (답을 게시하기 전에도) 몇 가지 중요한 사항을 제기합니다. 귀하의 질문에 대답하기 위해 :-API 호출은 비용이 많이 들지 않습니다 (무료이며 빠릅니다). -데이터의 일부는 상당히 정기적으로 (일부 두 시간마다) 변경됩니다. -속도는 상당히 중요하지만 응용 프로그램의 대상은 가벼운 빠른 로딩이 반드시 필요한 것은 아닙니다.
stevehayter

@stevehayter이 경우 클라이언트 쪽에서 API 호출을 수행 할 가능성이 높습니다. 더 싸고 빠르며 세밀한 제어가 가능합니다.
Benjamin Gruenbaum

1
이 답변을 바탕으로 데이터의 로컬 사본을 유지하는 데 덜 기울고 있습니다. 실제로 API를 별도로 노출하고 추가 데이터를 처리하는 데 기울고 있습니다. 나는 이것이 @Bobson 솔루션의 단순성 사이에서 좋은 타협이 될 수 있다고 생각하지만 조금 더 편한 분리 정도를 추가합니다. 프로토 타입에서이 전략을 살펴보고 결과를 다시보고하고 현상금을 수여합니다!
stevehayter

@ BenjaminGruenbaum-나는 당신의 주장을 따르지 않을 것입니다. 내 제안에 따라 저장소에 프레젠테이션을 알리는 방법은 무엇입니까? 물론 API 지원 필드에 액세스했음을 알 수 있지만 뷰가 해당 정보로 수행하는 작업과는 아무런 관련이 없습니다.
Bobson

1
나는 모든 것을 클라이언트 측으로 옮기기로 결정했지만 EFUser의 확장 방법 (프레 젠 테이션 레이어에 별도의 어셈블리에 있음)으로 선택했습니다. 이 메소드는 단순히 API에서 데이터를 리턴하고 반복적으로 적중되지 않도록 싱글 톤을 설정합니다. 객체의 수명이 너무 짧아 단일 톤을 사용하는 데 문제가 없습니다. 이런 식으로 어느 정도의 분리가 있지만, 여전히 EFUser 엔티티로 작업하는 것이 편리합니다. 도움을 주신 모든 응답자에게 감사합니다. 확실히 흥미로운 토론 :).
stevehayter

2

우려 사항을 적절히 분리 하면 Entity Framework / API 수준 이상으로 데이터의 출처를 인식 할 수 없습니다. API 호출이 비싸지 않은 한 (시간 또는 처리 측면에서) API를 사용하는 데이터에 액세스하는 것은 데이터베이스에서 데이터에 액세스하는 것만 큼 투명해야합니다.

이를 구현하는 가장 좋은 방법 EFUser은 필요에 따라 API 데이터를 지연로드하는 추가 속성을 객체 에 추가 하는 것입니다. 이 같은:

partial class EFUser
{
    private APIUser _apiUser;
    private APIUser ApiUser
    {
       get { 
          if (_apiUser == null) _apiUser = API.LoadUser(this.ExternalID);
          return _apiUser;
       }
    }
    public string CompanyName { get { return ApiUser.UserCompanyName; } }
    public string JobTitle{ get { return ApiUser.UserJobTitle; } }
}

외부 적으로, CompanyName또는 처음으로 JobTitle사용되는 경우 단일 API 호출 (따라서 약간의 지연)이 발생하지만 객체가 파괴 될 때까지의 모든 후속 호출은 데이터베이스 액세스만큼 빠르고 쉽습니다.


감사합니다 @Bobson ... 이것은 실제로 내려 가기 시작한 초기 경로였습니다 (예 : 사용자 목록의 회사 이름 표시와 같이 사용자 목록의 세부 정보를 대량로드하기 위해 일부 확장 방법이 추가되었습니다). 지금까지는 내 요구에 잘 맞는 것 같습니다. 그러나 아래 벤자민은 몇 가지 중요한 점을 제기하므로 이번 주에도 계속 평가하겠습니다.
stevehayter

0

한 가지 아이디어는 항상 추가 정보를 가지지 않도록 ApiUser를 수정하는 것입니다. 대신 ApiUser에 메소드를 가져 와서 가져옵니다.

ApiUser apiUser = backend.load($id);
//Locally available data directly on the ApiUser like so:
String name = apiUser.getName();
//Expensive extra data available after extra call:
UserExtraData extraData = apiUser.fetchExtraData();
String managerName = extraData.getManagerName();

추가 데이터의 지연로드를 사용하도록 ApiUser 객체에서 UserExtraData를 추출 할 필요가 없도록이를 약간 수정할 수도 있습니다.

//Extra data fetched on first get:
String managerName = apiUser.lazyGetExtraData().getManagerName();

이렇게하면 목록이있을 때 기본적으로 추가 데이터를 가져 오지 않습니다. 그러나 목록을 탐색하는 동안 여전히 액세스 할 수 있습니다!


backend.load ()에서 우리가 이미 무엇을 의미하는지 잘 모르겠습니다. 우리는 이미로드를 수행하고 있습니다. 그래서 API 데이터를로드 할 것입니까?
stevehayter

나는 명시 적으로 요청 될 때까지 여분의로드를 기다려야한다는 것을 의미합니다-API 데이터를 게으르게로드하십시오.
Alexander Torstling
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.