서비스 대신 컨트롤러 호출 저장소를 사용하는 것은 나쁜 습관입니까?


43

서비스 대신 컨트롤러 호출 저장소를 사용하는 것은 나쁜 습관입니까?

더 설명하기 위해 :

좋은 디자인 컨트롤러에서는 서비스 및 서비스 사용 저장소를 호출한다는 것을 알았습니다.

그러나 때로는 컨트롤러에서 논리가 필요하지 않고 db에서 가져 와서보기 위해 전달해야합니다.

그리고 서비스를 호출 할 필요없이 저장소를 호출하면됩니다. 나쁜 습관입니까?


서비스를 어떻게 부르십니까? REST 인터페이스를 통해?
Robert Harvey

2
나는 그 디자인 접근법을 다소 일반적으로 사용합니다. 내 컨트롤러 (또는 기본 작성기 클래스)는 리포지토리에 데이터를 요청하거나 리포지토리에 데이터를 보낸 다음 처리해야하는 모든 서비스 클래스에 전달합니다. 데이터 처리 클래스를 데이터 검색 / 관리 클래스와 결합 할 이유는 없지만 일반적인 접근 방식은 그렇게하는 것이지만 다른 관심사입니다.
Jimmy Hoffa

3
Meh. 작은 응용 프로그램이고 데이터베이스에서 데이터를 가져 오려고 하는 경우 해당 서비스 계층이 REST 인터페이스와 같은 일부 공용 API의 일부가 아닌 한 서비스 계층은 시간 낭비입니다 . "우유는 당신에게 좋거나 나쁜가요?" 유당 불내증 여부에 따라 다릅니다.
Robert Harvey

4
Controller-> Service-> Repository structure on Controller-> Repository가 있어야한다는 강력하고 빠른 규칙은 없습니다. 올바른 응용 분야에 적합한 패턴을 선택하십시오. 내가 말하고자하는 것은 응용 프로그램의 일관성을 유지해야한다는 것입니다.
NikolaiDante

어쩌면 요청을 저장소로 전달한 다음 반환하는 일반 서비스를 생각해 낼 수도 있습니다. 이는 균일 한 인터페이스를 유지하는 데 유용 할 수 있으며 나중에 저장소를 호출하기 전에 무언가를 수행하기 위해 실제 서비스를 추가해야하는 경우 간단합니다.
Henrique Barcelos

답변:


31

아닙니다. 리포지토리 서비스이기도합니다.

저장소를 통해 검색 한 엔티티가 대부분의 비즈니스 로직을 처리하는 경우 다른 서비스가 필요하지 않습니다. 리포지토리 만 있으면 충분합니다.

엔티티를 조작하기 위해 통과해야하는 일부 서비스가 있더라도. 먼저 저장소에서 엔티티를 잡고 해당 서비스로 전달하십시오. 시도하기 전에 HTTP 404를 던질 수있는 것이 매우 편리합니다.

또한 읽기 시나리오의 경우 DTO / ViewModel에 프로젝트를 투영하기 위해 엔티티가 필요합니다. 그 사이에 서비스 계층이 있으면 종종 추악한 메소드를 많이 통과하게됩니다.


2
잘했다! 저장소를 호출하는 것이 선호되며 저장소가 충분하지 않은 경우 (즉, 다른 저장소를 사용하여 두 엔티티를 수정해야 함)이 작업을 담당하는 서비스를 만들고 컨트롤러에서 호출합니다.
Zygimantas

서비스 사용을 정당화하기 위해 다소 복잡한 코드를 발견했습니다. 터무니없는, 최소 ...
Gi1ber7

따라서 내 저장소는 'xml 객체'로 변환 해야하는 '비즈니스 객체'목록을 반환합니다. 이것이 서비스 계층을 가질만큼 충분한 이유입니까? 각 객체에서 메소드를 호출하여 다른 유형으로 변환하고 새 목록에 추가합니다.
bot_bot

직접 DAO 액세스는 컨트롤러에서 위험하므로 SQL 삽입에 취약하고``deleteAll ''과 같은 위험한 작업에 액세스 할 수 있습니다. 나는 그것을 피할 것입니다.
Anirudh

4

컨트롤러가 리포지토리를 직접 호출하는 것은 좋지 않습니다. "서비스"는 또 다른 도구이므로 의미가있는 곳에서 사용하십시오.

NikolaiDante는 다음과 같이 언급했습니다.

... 올바른 응용 분야에 적합한 패턴을 선택하십시오. 내가 말하고자하는 것은 응용 프로그램의 일관성을 유지해야한다는 것입니다.

일관성이 가장 중요한 측면이라고 생각하지 않습니다. "서비스"클래스는 컨트롤러가이를 구현할 필요가 없도록 상위 로직을 캡슐화하기위한 것입니다. 주어진 작업에 "높은 수준의 논리"가 필요하지 않으면 리포지토리로 직접 이동하십시오.

우려와 테스트 가능성을 분리 시키려면 리포지토리는 생성자를 통해 서비스에 주입하는 종속성이어야합니다.

IFooRepository repository = new FooRepository();
FooService service = new FooService(repository);

service.DoSomething(...);

데이터베이스에서 레코드를 검색 할 때 일종의 매개 변수화 된 쿼리가 필요한 경우 서비스 클래스는 뷰 모델을 가져 와서 리포지토리에서 실행하는 쿼리를 작성하기에 좋은 장소 일 수 있습니다.

마찬가지로, 폼에 복잡한 뷰 모델이있는 경우 서비스 클래스는 도메인 모델 / 엔터티에서 메서드를 호출 한 다음 리포지토리를 사용하여 레코드를 유지하여 레코드를 생성, 업데이트 및 삭제하는 논리를 캡슐화 할 수 있습니다.

컨트롤러가 ID로 레코드를 가져와야하는 경우 반대 방향으로 가면 서비스 오브젝트에 위임하는 것은 슬레지 해머로 압정을 치는 것과 같습니다. 필요한 것 이상입니다.

컨트롤러가 트랜잭션 또는 작업 단위 오브젝트 를 처리하기에 가장 적합한 위치에 있음을 발견했습니다 . 그런 다음 컨트롤러 또는 작업 단위 (UOW) 오브젝트는 복잡한 조작을 위해 서비스 오브젝트에 위임하거나 간단한 조작을 위해 저장소로 직접 이동합니다 (예 : ID로 레코드 찾기).

public class ShoppingCartsController : Controller
{
    [HttpPost]
    public ActionResult Edit(int id, ShoppingCartForm model)
    {
        // Controller initiates a database session and transaction
        using (IStoreContext store = new StoreContext())
        {
            // Controller goes directly to a repository to find a record by Id
            ShoppingCart cart = store.ShoppingCarts.Find(id);

            // Controller creates the service, and passes the repository and/or
            // the current transaction
            ShoppingCartService service = new ShoppingCartService(store.ShoppingCarts);

            if (cart == null)
                return HttpNotFound();

            if (ModelState.IsValid)
            {
                // Controller delegates to a service object to manipulate the
                // Domain Model (ShoppingCart)
                service.UpdateShoppingCart(model, cart);

                // Controller decides to commit changes
                store.SaveChanges();

                return RedirectToAction("Index", "Home");
            }
            else
            {
                return View(model);
            }
        }
    }
}

나는 다양한 서비스와 리포지토리를 직접 사용하는 것이 완벽하게 허용된다고 생각합니다. 필요한 경우 트랜잭션을 작업 단위 (UOW) 오브젝트에 캡슐화 할 수 있습니다.

책임의 분류는 다음과 같습니다.

  • 컨트롤러는 어플리케이션의 흐름을 제어합니다
    • 장바구니가 데이터베이스에 없으면 "404 Not Found"를 반환합니다.
    • 유효성 검사에 실패하면 유효성 검사 메시지와 함께 양식을 다시 렌더링
    • 모든 것이 체크 아웃되면 장바구니를 저장합니다
  • 컨트롤러는 서비스 클래스에 위임하여 도메인 모델 (또는 엔티티)에서 비즈니스 로직을 실행합니다. 서비스 객체는 비즈니스 로직을 구현 해서는 안됩니다 ! 그들은 실행 비즈니스 로직을.
  • 컨트롤러는 간단한 작업을 위해 리포지토리에 직접 위임 할 수 있습니다.
  • 서비스 개체는 뷰 모델에서 데이터를 가져 와서 비즈니스 논리를 실행하기 위해 도메인 모델에 위임합니다 (예 : 서비스 개체는 도메인 모델에서 메서드를 호출하기 전에 리포지토리에서 메서드를 호출하기 전에)
  • 데이터 지속성을 위해 저장소에 서비스 객체 위임
  • 컨트롤러는 다음 중 하나 여야합니다.
    1. 거래 수명 관리
    2. 트랜잭션 수명을 관리하기 위해 작업 단위 (UOW) 오브젝트 작성

1
-1 reb가 아닌 DbContext를 컨트롤러에 넣는 경우 -1입니다. 리포지토리는 데이터 공급자를 관리하기 위해 데이터 공급자가 변경되는 경우 (예 : MySQL에서 플랫 JSON 파일로 변경, 한 곳에서 변경) 아무도 할 필요가 없습니다.
Jimmy Hoffa

@JimmyHoffa : 실제로 작성한 코드를 되돌아보고 있으며, 솔직히 말하면 데이터베이스가 아닌 저장소에 대한 "컨텍스트"객체를 만듭니다. DbContext이 경우 나쁜 이름 이라고 생각 합니다. 변경하겠습니다. NHibernate를 사용하고 리포지토리 (또는 편리한 경우 컨텍스트)는 데이터베이스 끝을 관리하므로 지속성 메커니즘 변경은 컨텍스트 외부의 코드 변경이 필요하지 않습니다.
Greg Burghardt

코드 모양으로 컨트롤러와 저장소를 혼동하는 것 같습니다. "컨텍스트"가 모두 잘못되어 컨트롤러에 절대 존재해서는 안됩니다.
Jimmy Hoffa

6
나는 대답 할 필요가 없으며 이것이 처음부터 좋은 질문인지 확실하지 않지만 귀하의 접근법이 나쁜 디자인이라고 생각하기 때문에 투표를 거부합니다. 어려운 감정은 없으며 컨트롤러가 소유 한 컨텍스트를 사용하지 않는 것이 좋습니다. IMO 컨트롤러는 이와 같은 트랜잭션을 시작하고 커밋해서는 안됩니다. 그것은 다른 많은 장소의 일입니다. 컨트롤러는 단순히 HTTP 요청을 수행하지 않는 모든 것을 다른 곳으로 위임하는 것을 선호합니다.
Jimmy Hoffa

1
리포지토리 (repository)는 일반적으로 모든 데이터 컨텍스트 정보를 담당하여 도메인 자체가 알아야하는 것 이외의 데이터 문제에 대해 아무것도 알 필요가 없도록합니다.
Jimmy Hoffa

1

아키텍처에 따라 다릅니다. 나는 Spring을 사용하며 트랜잭션은 항상 서비스에 의해 관리됩니다.

쓰기 작업 (리포지토리에 단순히 위임하는 논리가없는 간단한 서비스)을 위해 리포지토리를 직접 호출하는 경우 하나의 작업으로 수행해야하는 작업에 여러 데이터베이스 트랜잭션을 사용하고있을 수 있습니다. 그러면 데이터베이스에 일관성없는 데이터가 생성됩니다. 일반적으로 데이터베이스 작업은 작동하거나 실패해야하지만 절반 작업은 두통의 원인입니다.

따라서 컨트롤러에서 직접 리포지토리를 호출하거나 간단한 위임 서비스를 사용하는 것은 나쁜 습관이라고 생각합니다. 읽기 전용으로 시작하기 시작하면 곧 또는 한 명 또는 동료가 쓰기 작업을 수행하기 시작합니다.

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