수다스러운 인터페이스를 피하는 방법


10

배경 : 서버 응용 프로그램을 설계하고 다른 하위 시스템에 대해 별도의 dll을 만듭니다. 일을 단순화하기 위해 두 개의 하위 시스템이 있다고 가정 해 봅시다. 1) Users2)Projects

사용자의 공용 인터페이스에는 다음과 같은 방법이 있습니다.

IEnumerable<User> GetUser(int id);

그리고 프로젝트의 공용 인터페이스에는 다음과 같은 방법이 있습니다.

IEnumerable<User> GetProjectUsers(int projectId);

예를 들어 특정 프로젝트의 사용자를 표시해야 할 경우 전화를 걸면 데이터 GetProjectUsers그리드 또는 이와 유사한 정보를 표시 할 수있는 충분한 정보가있는 객체를 반환 할 수 있습니다 .

문제 : 이상적에서, Projects서브 시스템은 또한 사용자 정보를 저장 안하고 그냥 프로젝트에 참여하고있는 사용자의 ID를 저장해야합니다. 서비스를 제공하기 위해 GetProjectUsers, 그것은 호출해야 GetUserUsers자체 데이터베이스에 저장된 각 사용자 ID에 대한 시스템. 그러나 여기에는 많은 별도의 GetUser호출 이 필요하므로 User서브 시스템 내부에 많은 별도의 SQL 쿼리가 발생합니다 . 나는 이것을 실제로 테스트하지는 않았지만이 수다스러운 디자인은 시스템의 확장성에 영향을 미칩니다.

내가 옆 서브 시스템의 분리를 넣어, 난 할 수 두 시스템에 의해 하나의 스키마 accessable 한 모든 정보를 저장하고 Projects간단하게 할 수있는 JOIN하나의 쿼리에서 모든 프로젝트 사용자를 얻을 수 있습니다. 쿼리 결과에서 개체 Projects를 생성하는 방법도 알아야합니다 User. 그러나 이것은 많은 장점을 가진 분리를 깨뜨립니다.

질문 : 누구나 개별 GetUser통화 를 피하면서 분리를 유지할 수있는 방법을 제안 할 수 있습니까 GetProjectUsers?


예를 들어, 내가 생각한 것 중 하나는 사용자가 외부 시스템에 레이블-값 쌍으로 사용자에게 "태그"태그를 지정하고 특정 값을 가진 사용자를 요청하는 기능을 제공하는 것입니다.

void AddUserTag(int userId, string tag, string value);
IEnumerable<User> GetUsersByTag(string tag, string value);

그런 다음 프로젝트 시스템은 각 사용자가 프로젝트에 추가 될 때 태그를 지정할 수 있습니다.

AddUserTag(userId,"project id", myProjectId.ToString());

GetProjectUsers 중에는 단일 프로젝트에서 모든 프로젝트 사용자를 요청할 수 있습니다.

var projectUsers = usersService.GetUsersByTag("project id", myProjectId.ToString());

확실하지 않은 부분은 다음과 같습니다. 예, 사용자는 프로젝트를 무시하지만 실제로 프로젝트 멤버십에 대한 정보는 프로젝트가 아닌 사용자 시스템에 저장됩니다. 나는 자연스럽지 않아서 여기에 내가 놓친 큰 단점이 있는지 확인하려고합니다.

답변:


10

시스템에서 누락 된 것은 캐시입니다.

당신은 말합니다 :

그러나 여기에는 많은 별도의 GetUser호출 이 필요하므로 User서브 시스템 내부에 많은 별도의 SQL 쿼리가 발생합니다 .

메소드 호출 수는 SQL 쿼리 수와 같을 필요는 없습니다. 사용자에 대한 정보를 한 번 가져옵니다. 왜 변경되지 않은 경우 동일한 정보를 다시 쿼리 합니까? 아마도 모든 사용자를 메모리에 캐시 할 수도 있습니다 (사용자가 변경하지 않는 한).

반면에 Projects서브 시스템이 프로젝트와 사용자 모두에게 쿼리를 수행 INNER JOIN하면 추가 문제가 발생합니다. 코드에서 서로 다른 두 위치에서 동일한 정보를 쿼리하므로 캐시 무효화가 매우 어려워집니다. 결과로서:

  • 나중에 언제든지 캐시를 소개하지 않을 것입니다.

  • 또는 정보가 변경 될 때 무효화해야 할 사항을 연구하는 데 몇 주 또는 몇 달을 소비 할 것입니다.

  • 또는 간단한 위치에 캐시 무효화를 추가하여 다른 위치를 잊어 버리고 버그를 찾기가 어려울 수 있습니다.


귀하의 질문을 다시 읽은 결과, 처음 놓친 키워드 : 확장 성을 발견했습니다 . 경험상 다음 패턴을 따를 수 있습니다.

  1. 시스템이 느리게 작동하는지 (즉, 작동하지 않는 성능 요구 사항을 위반하거나 사용하기에 악몽 임) 자신에게 물어보십시오.

    시스템이 경우 하지 느린, 성능에 대한 귀찮게하지 않습니다. 깔끔한 코드, 가독성, 유지 보수성, 테스트, 지사 범위, 깔끔한 디자인, 상세하고 이해하기 쉬운 문서화, 좋은 코드 주석에 대해 귀찮게하십시오.

  2. 그렇다면 병목 현상을 검색하십시오. 추측하지 않고 프로파일 링 하여이를 수행합니다 . 프로파일 링을 통해 병목 현상의 정확한 위치를 결정하고 ( 추측 할 때 거의 항상 잘못 될 수 있으므로) 이제 코드의 해당 부분에 집중할 수 있습니다.

  3. 병목 현상이 발견되면 솔루션을 검색하십시오. 추측, 벤치마킹, 프로파일 링, 대안 작성, 컴파일러 최적화 이해, 자신에게 맞는 최적화 이해, 스택 오버플로에 대한 질문 및 저수준 언어 (필요한 경우 어셈블러 포함)로 이동하여이를 수행합니다.

Projects서브 시스템에 정보를 요구 하는 서브 시스템 의 실제 문제는 무엇입니까 Users?

궁극적 인 미래 확장 성 문제? 이것은 문제가되지 않습니다. 모든 것이 하나의 단일 솔루션으로 병합되거나 여러 위치에서 동일한 데이터를 쿼리하기 시작하면 (캐시를 도입하기가 어렵 기 때문에 아래 설명 된 것처럼) 확장 성이 악몽이 될 수 있습니다.

이미 눈에 띄는 성능 문제 가있는 경우 2 단계에서 병목 현상을 검색하십시오.

실제로 병목 현상이 존재하고 서브 시스템을 Projects통한 사용자 요청 Users(데이터베이스 조회 레벨에 있음) 으로 인해 병목 현상이 발생한 경우 에만 대안을 검색해야합니다.

가장 일반적인 대안은 캐싱을 구현하여 쿼리 수를 크게 줄이는 것입니다. 캐싱이 도움이되지 않는 상황에서는 추가 프로파일 링을 통해 쿼리 수를 줄이거 나 데이터베이스 인덱스를 추가 (또는 제거)하거나 더 많은 하드웨어를 던지거나 전체 시스템을 완전히 재 설계해야한다는 사실을 알 수 있습니다 .


내가 당신을 오해하지 않는 한, 당신은 "개별 GetUser 호출을 유지하지만 캐싱을 사용하여 db 왕복을 피하십시오"라고 말합니다.
Eren Ersönmez

@ ErenErsönmez : GetUser는 데이터베이스를 쿼리하는 대신 캐시를 찾습니다. 이것은 GetUser(캐시가 무효화되지 않은 한) 데이터베이스 대신 메모리에서 데이터를로드하기 때문에 실제로 몇 번 호출하는지는 중요하지 않습니다 .
Arseni Mourzenko

"시스템을 단일 시스템으로 병합하지 않고 번거 로움을 없애는 것"과 같은 주요 문제를 강조하는 데 도움이되지 않은 좋은 제안입니다. 사용자 및 프로젝트에 대한 나의 예는 자연스럽게 비교적 적은 수의 사용자가 거의 변하지 않는다고 믿게 만들 것입니다. 아마도 더 좋은 예는 문서와 프로젝트 일 것입니다. 매일 2 백만 개가 넘는 문서가 매일 추가되고 프로젝트 시스템이 문서 시스템을 사용하여 문서를 저장한다고 가정 해보십시오. 그래도 캐싱을 권장합니까? 아마 아니지?
Eren Ersönmez

@ ErenErsönmez : 데이터가 많을수록 더 중요한 캐싱이 나타납니다. 경험상, 읽기 수와 쓰기 수를 비교하십시오. 하루에 수천 개의 문서가 추가되고 하루에 수백만 개의 select쿼리가있는 경우 캐싱을 사용하는 것이 좋습니다. 반면에 수십억 개의 엔터티를 데이터베이스에 추가 select하고 매우 선택적인 수만으로 만 수천 개를 얻는 경우 where캐싱은 그다지 유용하지 않을 수 있습니다.
Arseni Mourzenko

당신은 아마 옳을 것입니다-나는 아직 가지고 있지 않은 문제를 해결하려고 노력하고 있습니다. 아마 그대로 구현하고 필요할 경우 나중에 개선하려고 노력할 것입니다. 예를 들어 엔터티를 추가 한 후 1-2 회만 읽을 수 있기 때문에 캐싱이 적절하지 않은 경우 질문에 추가 한 가능한 해결책이 효과가 있다고 생각합니까? 그것에 큰 문제가 있습니까?
Eren Ersönmez
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.