리포지토리와 서비스 계층의 차이점은 무엇입니까?


191

OOP 디자인 패턴에서 리포지토리 패턴과 서비스 계층의 차이점은 무엇입니까?

ASP.NET MVC 3 앱을 개발 중이며 이러한 디자인 패턴을 이해하려고 노력하고 있지만 제 뇌는 그것을 얻지 못하고 있습니다.

답변:


330

리포지토리 레이어는 데이터 액세스에 대한 추가적인 추상화 수준을 제공합니다. 쓰는 대신

var context = new DatabaseContext();
return CreateObjectQuery<Type>().Where(t => t.ID == param).First();

데이터베이스에서 단일 항목을 가져 오려면 저장소 인터페이스를 사용하십시오.

public interface IRepository<T>
{
    IQueryable<T> List();
    bool Create(T item);
    bool Delete(int id);
    T Get(int id);
    bool SaveChanges();
}

전화하십시오 Get(id). 리포지토리 레이어는 기본 CRUD를 노출합니다 작업을 합니다.

서비스 계층은 리포지토리를 사용하는 비즈니스 로직을 노출합니다. 서비스 예는 다음과 같습니다.

public interface IUserService
{
    User GetByUserName(string userName);
    string GetUserNameByEmail(string email);
    bool EditBasicUserData(User user);
    User GetUserByID(int id);
    bool DeleteUser(int id);
    IQueryable<User> ListUsers();
    bool ChangePassword(string userName, string newPassword);
    bool SendPasswordReminder(string userName);
    bool RegisterNewUser(RegisterNewUserModel model);
}

List()저장소의 메소드는 모든 사용자를 리턴 하지만 ListUsers()IUserService의 사용자는 하나만 리턴 할 수 있으며 사용자는 액세스 할 수 있습니다.

ASP.NET MVC + EF + SQL SERVER에는 다음과 같은 통신 흐름이 있습니다.

보기 <-컨트롤러-> 서비스 계층-> 리포지토리 계층-> EF-> SQL Server

서비스 계층-> 리포지토리 계층-> EF 이 부분은 모델에서 작동합니다.

뷰 <-컨트롤러-> 서비스 계층 이 부분은 뷰 모델에서 작동합니다.

편집하다:

/ Orders / ByClient / 5의 흐름 예 (특정 클라이언트의 순서를보고 싶습니다) :

public class OrderController
{
    private IOrderService _orderService;

    public OrderController(IOrderService orderService)
    {
        _orderService = orderService; // injected by IOC container
    }

    public ActionResult ByClient(int id)
    {
        var model = _orderService.GetByClient(id);
        return View(model); 
    }
}

주문 서비스를위한 인터페이스입니다.

public interface IOrderService
{
    OrdersByClientViewModel GetByClient(int id);
}

이 인터페이스는 뷰 모델을 반환합니다.

public class OrdersByClientViewModel
{
     CientViewModel Client { get; set; } //instead of ClientView, in simple project EF Client class could be used
     IEnumerable<OrderViewModel> Orders { get; set; }
}

인터페이스 구현입니다. 모델 클래스와 저장소를 사용하여 뷰 모델을 작성합니다.

public class OrderService : IOrderService
{
     IRepository<Client> _clientRepository;
     public OrderService(IRepository<Client> clientRepository)
     {
         _clientRepository = clientRepository; //injected
     }

     public OrdersByClientViewModel GetByClient(int id)
     {
         return _clientRepository.Get(id).Select(c => 
             new OrdersByClientViewModel 
             {
                 Cient = new ClientViewModel { ...init with values from c...}
                 Orders = c.Orders.Select(o => new OrderViewModel { ...init with values from o...}     
             }
         );
     }
}

2
@ Sam Striano : 위에서 볼 수 있듯이 내 IRepository는 IQueryable을 반환합니다. 이를 통해 서비스 계층에서 추가 위치 조건과 지연된 실행을 추가 할 수 있습니다. 예, 하나의 어셈블리를 사용하지만 이러한 모든 클래스는 다른 네임 스페이스에 배치됩니다. 소규모 프로젝트에서 많은 어셈블리를 생성 할 이유가 없습니다. 네임 스페이스와 폴더 분리가 잘 작동합니다.
LukLed

81
서비스에서 뷰 모델을 반환하는 이유는 무엇입니까? 여러 클라이언트 (모바일 / 웹)가있는 경우 서비스가 에뮬레이션한다고 가정하지 않습니까? 그렇다면 뷰 모델은 다른 플랫폼과 다를 수 있습니다
Ryan

12
@Ryan과 동의 한 서비스 계층은 엔티티 오브젝트 또는 엔티티 오브젝트 콜렉션을 리턴해야합니다 (IQueryable 아님). 그런 다음 ui 엔티티에서 예를 들어 Automapper로 SomeViewModel에 매핑합니다.
Eldar

5
@Duffp : 모든 엔터티에 대한 리포지토리를 만들 필요는 없습니다. 당신은 일반적인 구현 및 바인딩을 사용할 수 있습니다 IRepository<>GenericRepository<>당신의 IOC 라이브러리에. 이 답변은 매우 오래되었습니다. 가장 좋은 해결책은 모든 리포지토리를 하나의 클래스로 결합하는 것 UnitOfWork입니다. 모든 유형의 저장소와라는 하나의 메소드를 포함해야합니다 SaveChanges. 모든 리포지토리는 하나의 EF 컨텍스트를 공유해야합니다.
LukLed

2
서비스 계층에서 viewmodel을 반환하는 대신 automapper를 사용하여 DTO를 반환하고이를 viewModels로 변환해야합니다. 때로는 동일하지 않습니다. YGTNI를 구현 한 것에 대해 감사 할 것입니다. "You 're Going To 필요 "
hanzolo

41

Carnotaurus는 저장소가 데이터를 스토리지 형식에서 비즈니스 오브젝트로 맵핑 할 책임이 있다고 말했다. 스토리지에서 데이터를 읽고 쓰는 방법 (삭제, 업데이트도 모두)을 처리해야합니다.

반면에 서비스 계층의 목적은 비즈니스 재사용을 단일 장소로 캡슐화하여 코드 재사용 및 문제 분리를 촉진하는 것입니다. Asp.net MVC 사이트를 구축 할 때 이것이 실제로 실제로 의미하는 것은이 구조를 가지고 있다는 것입니다

[제어기]는 [서비스]를 호출합니다.

내가 찾은 유용한 한 가지 원칙은 컨트롤러 및 리포지토리에서 로직을 최소로 유지하는 것입니다.

컨트롤러에서는 DRY를 유지하는 데 도움이되기 때문입니다. 다른 곳에서 동일한 필터링 또는 논리를 사용해야하는 것이 매우 일반적이며 컨트롤러에 배치하면 재사용 할 수 없습니다.

리포지토리에서 더 나은 것이 나올 때 스토리지 (또는 ORM)를 교체 할 수 있기를 원하기 때문입니다. 그리고 저장소에 논리가 있으면 저장소를 변경할 때이 논리를 다시 작성해야합니다. 내 저장소가 IQueryable 만 반환하고 서비스가 필터링을 수행하는 경우 매핑 만 교체하면됩니다.

예를 들어 최근 몇 개의 Linq-To-Sql 리포지토리를 EF4로 교체했으며이 원칙을 지키지 않은 분은 몇 분 안에 교체 할 수있었습니다. 내가 몇 가지 논리를 가지고있는 곳은 대신 몇 시간의 문제였습니다.


나는 당신에게 미카엘에 동의합니다. 사실, 나는 기술 블로그 freecodebase.com 에서 동일한 시나리오를 적용 했으며이 구현에서 코드 우선 접근 방식을 사용했습니다. 소스 코드도 여기에서 다운로드 할 수 있습니다.
Toffee

기존 MVC 앱에서 저장소 패턴을 적용하는 일반적인 주제를 연구했습니다. Active Record와 유사한 ORM 및 기타 Rails / Laravel 규칙이있는 맞춤형 프레임 워크이며 현재 수행중인 작업에 일부 아키텍처 문제가 있습니다. 내가 보았던 한 가지는 저장소가 "ViewModels, DTO 또는 쿼리 객체를 반환해서는 안되며"저장소 객체를 반환해야한다는 것입니다. 서비스가 저장소 메소드와 같은 메소드를 통해 상호 작용 onBeforeBuildBrowseQuery하고 쿼리 작성기를 사용하여 쿼리를 변경할 수 있는 위치를 생각 하고 있습니다.
calligraphic-io

@Toffee, 귀하의 링크가 손상되었습니다. 업데이트 할 수 있습니까?이 구현을 위해서는 소스 코드가 필요합니다.
Hamza Khanzada

24

받아 들여진 대답 (그리고 수백 번의 찬성)에는 큰 결함이 있습니다. 나는 이것을 주석에서 지적하고 싶었지만 30 개의 주석으로 거기에 묻힐 것입니다.

그런 식으로 구축 된 엔터프라이즈 응용 프로그램을 인수했으며 초기 반응은 WTH 입니까? 서비스 계층의 ViewModel? 수년간의 개발로 인해 협약을 변경하고 싶지 않았으므로 ViewModels를 계속 반환했습니다. 우리가 WPF를 사용하기 시작했을 때 그것은 악몽으로 변했습니다. 우리 (개발팀)는 항상 말하고 있습니다 : 어떤 ViewModel? 실제 하나 (WPF를 위해 작성한 것) 또는 서비스 하나? 웹 응용 프로그램 용으로 작성되었으며 심지어 UI에서 편집을 비활성화하기 위해 IsReadOnly 플래그가 있었습니다 . 한 단어로 인한 주요 결함 및 주요 결함 : ViewModel !!

같은 실수를 저지르기 전에, 위의 이야기 외에도 몇 가지 이유가 있습니다.

서비스 계층에서 ViewModel을 반환하는 것은 엄청난 일이 아닙니다. 그것은 다음과 같습니다.

  1. 이러한 서비스를 사용하려면 MVVM을 사용하는 것이 좋으며 여기에 사용해야하는 ViewModel이 있습니다. 아야!

  2. 서비스는 UI 어딘가에 표시 될 것이라고 가정합니다. 웹 서비스 나 Windows 서비스와 같은 비 UI 응용 프로그램에서 사용하는 경우 어떻게됩니까?

  3. 그것은 심지어 실제 ViewModel이 아닙니다. 실제 ViewModel에는 관찰 성, 명령 등이 있습니다. 이름이 잘못된 POCO 일뿐입니다. (이름이 중요한 이유는 위의 내 이야기를 참조하십시오.)

  4. 소비 애플리케이션은 프리젠 테이션 레이어 (ViewModel이이 레이어에서 사용됨)이고 C #을 더 잘 이해합니다. 또 다른 아야!

제발 하지마!


3
나는 그것이 토론에 추가되지 않는다는 것을 알고 있지만 이것에 대해서만 언급해야했다. <<-그것은 티셔츠에 멋지다! :) :)
Mephisztoe

8

일반적으로 리포지토리는 엔티티를 채우는 스캐 폴딩으로 사용됩니다. 서비스 계층은 나가서 요청을 소싱합니다. 서비스 계층 아래에 ​​리포지토리를 배치 할 수 있습니다.


따라서 EF4를 사용하는 ASP.NET MVC 앱에서는 다음과 같은 것이 있습니다. SQL Server-> EF4-> 리포지토리-> 서비스 계층-> 모델-> 컨트롤러?
Sam

1
예, EF4에서 경량 엔티티를 가져 오는 데 저장소를 사용할 수 있습니다. 서비스 계층을 사용하여이를 특수 모델 관리자 (시나리오의 모델)로 다시 보낼 수 있습니다. 컨트롤러는이 작업을 수행하기 위해 전문 모델 관리자에게 문의 할 것입니다 ... Mvc 2 / 3에 대한 내 블로그를 간단히 살펴보십시오. 다이어그램이 있습니다.
CarneyCode

설명을 위해 : 시나리오의 EF4는 모델이 내 다이어그램에 있고 시나리오의 모델은 내 다이어그램의 전문 모델 관리자입니다.
CarneyCode

5

리포지토리 계층은 데이터베이스에 액세스하기 위해 구현되며 데이터베이스에서 CRUD 작업을 확장하는 데 도움이됩니다. 서비스 계층은 응용 프로그램의 비즈니스 논리로 구성되며 저장소 계층을 사용하여 데이터베이스와 관련된 특정 논리를 구현할 수 있습니다. 응용 프로그램에서는 별도의 리포지토리 계층과 서비스 계층을 갖는 것이 좋습니다. 별도의 리포지토리와 서비스 계층이 있으면 코드가 더 모듈화되고 데이터베이스가 비즈니스 로직에서 분리됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.