깨끗한 아키텍처를 위해 서비스와 저장소 사이에 계층을 사용해야합니까-Spring


9

나는 건축에서 일하고 있는데, 웹 클라이언트와 모바일 앱을위한 나머지 API를 제공 할 것입니다. Spring (spring mvc, spring data jpa, ... etc)을 사용하고 있습니다. 도메인 모델은 JPA 사양으로 코딩됩니다.

클린 아키텍처의 개념을 적용하려고합니다 ( https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html ). jpa 도메인 모델을 유지하기 때문에 전부는 아닙니다.

레이어를 통한 실제 흐름은 다음과 같습니다.

프런트 엔드 <-> API 서비스 -> 서비스 -> 리포지토리 -> DB

  • 프론트 엔드 : 웹 클라이언트, 모바일 앱
  • API 서비스 : Rest Controllers, 여기서는 변환기와 dto를 사용하고 서비스를 호출합니다.
  • 서비스 : 구현과의 인터페이스 및 비즈니스 로직을 포함
  • 리포지토리 : CRUD 작업과 일부 SQL 쿼리를 오염시키는 자동 구현 (스프링 데이터 JPA로 수행)과 리포지토리 인터페이스

내 의심 : 서비스와 저장소 사이에 추가 계층을 사용해야합니까?

이 새로운 흐름을 계획하고 있습니다.

프론트 엔드 <-> API 서비스 -> 서비스 -> 지속성 -> 저장소 -> DB

이 지속성 계층을 사용해야하는 이유 클린 아키텍처 기사에서 말했듯이 불가지론 지속 레이어에 액세스하는 서비스 구현 (비즈니스 로직 또는 유스 케이스)을 원합니다. 다른 "데이터 액세스"패턴을 사용하기로 결정한 경우 (예 : 리포지토리 사용을 중단하기로 결정한 경우) 변경이 필요하지 않습니다.

class ProductServiceImpl implements ProductService {
    ProductRepository productRepository;
    void save(Product product) {
        // do business logic
        productRepository.save(product)
    }
}

따라서 다음과 같은 지속성 레이어를 사용하고 있다고 생각합니다.

class ProductServiceImpl implements ProductService {
    ProductPersistence productPersistence;
    void save(Product product) {
        // do business logic
        productPersistence.save(product)
    }
}

다음과 같이 지속성 계층의 구현 :

class ProductPersistenceImpl implements ProductPersistence {
    ProductRepository productRepository;
    void save(Product product) {
        productRepository.save(product)
    }
}

따라서 지속성 계층의 구현을 변경하고 서비스를 변경하지 않고 리포지토리가 프레임 워크와 관련되어 있다는 사실과 관련이 있습니다.

어떻게 생각해? 감사.


3
저장소는 추상화 계층입니다. 다른 하나를 추가해도 도움이되지
Ewan

그러나 Spring에서 제안한 인터페이스를 사용해야합니다. 즉, 저장소 메소드 이름입니다. 리포지토리를 변경하려면 호출 이름을 유지해야합니다. 아니요?
Alejandro

스프링 저장소가 스프링 객체를 노출시키지 않는 것처럼 보입니다. 이 인터페이스를 사용하여 불가지론적인 인터페이스를 구현하십시오
Ewan

답변:


6

프런트 엔드 <-> API 서비스-> 서비스-> 리포지토리-> DB

권리. 이것은 Spring Framework가 제안한 우려를 분리하여 기본 디자인입니다. 그래서 당신은 " 봄의 올바른 길 "에 있습니다.

에도 불구하고 저장소가 자주 DAO를 사용하는, 진실은 봄 개발자의 개념했다 있다는 것입니다 저장소 에릭 에반스 'DDD에서. 리포지토리 인터페이스는 CRUD 방법으로 인해 DAO와 매우 유사하게 보이며 많은 개발자가 리포지토리의 인터페이스를 너무 일반화하여 결국 EntityManager (여기서는 실제 DAO) 1와 다르지만 쿼리 및 기준.

Spring 컴포넌트로 번역하면 디자인이

@RestController > @Service > @Repository >  EntityManager

리포지토리는 이미 서비스와 데이터 저장소 간의 추상화입니다. Spring Data JPA 리포지토리 인터페이스를 확장 할 때이 디자인을 암시 적으로 구현합니다. 이 작업을 수행 할 때 세금을 지불해야합니다. Spring의 구성 요소와의 밀접한 결합. 또한 필요하지 않거나 원하지 않는 여러 가지 방법을 상속하여 LoD 및 YAGNI를 중단합니다. 물론 그러한 인터페이스는 그들이 제공하는 도메인 요구에 대한 귀중한 통찰력을 제공하지 않습니다.

즉, Spring Data JPA 리포지토리를 반드시 확장 할 필요는 없으며보다 평범하고 사용자 정의 된 클래스 계층을 구현하여 이러한 절충을 피할 수 있습니다.

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private EntityManager em;

        @Autowire
        public MyRepository (EntityManager em){    
             this.em = em;
        }

        //Interface implentation
        //...
    }

데이터 소스를 변경하면 EntityManager 를 다른 데이터 소스로 대체하는 새로운 구현이 필요합니다 .

    //@RestController > @Service > @Repository >  RestTemplate

    @Repository
    public class MyRepositoryImpl implements MyRepository{
        private RestTemplate rt;

        @Autowire 
        public MyRepository (RestTemplate rt){    
             this.rt = rt;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  File

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private File file; 
        public MyRepository (File file){    
            this.file = file;
        }

        //Interface implentation
        //...
    }
    //@RestController > @Service > @Repository >  SoapWSClient

    @Repository
    public class MyRepositoryImpl implements MyRepository{

        private MyWebServiceClient wsClient; 

        @Autowire
        public MyRepository (MyWebServiceClient  wsClient){    
               this.wsClient = wsClient;
        }

        //Interface implentation
        //...
    }

등등. 2

질문으로 돌아가서 추상화 레이어를 하나 더 추가해야하는지 여부는 필요하지 않기 때문에 아니요라고 대답합니다. 귀하의 예는 더 많은 복잡성을 추가하고 있습니다. 제안하는 계층은 서비스리포지토리 간의 프록시 또는 특정 논리가 필요할 때 의사 서비스 리포지토리 계층 으로 끝나고 배치 할 위치는 없습니다.


1 : 많은 개발자들이 생각하는 것과 달리, 각 리포지토리는 서로 다른 도메인 요구를 제공하기 때문에 리포지토리 인터페이스는 완전히 다를 수 있습니다. Spring Data JPA에서 DAO 역할 은 EntityManager에 의해 수행됩니다 . 세션, DataSource에 대한 액세스 , 매핑 등 을 관리합니다 .

2 : 비슷한 솔루션이 Spring의 저장소 인터페이스를 사용자 정의 인터페이스와 혼합하여 향상시킵니다. 자세한 정보는 BaseRepositoryFactoryBean@NoRepositoryBean을 찾으십시오 . 그러나이 접근법은 번거롭고 혼란 스럽습니다.


3

디자인이 유연하다는 것을 증명하는 가장 좋은 방법은 디자인을 유연하게하는 것입니다.

코드에 지속성을 책임지지 만 저장소 사용 아이디어와 관련이없는 장소를 원합니다. 좋아. 지금은 유용한 일이 없습니다 ... 한숨.

이 분로 레이어가 제대로 작동했는지 테스트 해 봅시다. 제품을 파일로 유지할 플랫 파일 계층을 만듭니다. 이 새로운 레이어는이 디자인에서 어디로 가나 요?

DB가있는 곳으로 갈 수 있어야합니다. 결국 플랫 파일을 사용하기 때문에 더 이상 DB가 필요하지 않습니다. 그러나 이것은 또한 저장소가 필요하지 않아야했습니다.

아마도 리포지토리는 구현 세부 사항 일 것입니다. 결국 리포지토리 패턴을 사용하지 않고 DB와 대화 할 수 있습니다.

Front end <--> API Service -> Service -> Repository -> DB

Front end <--> API Service -> Service -> Repository -> Files

Front end <--> API Service -> Service -> Persistence -> DB

Front end <--> API Service -> Service -> Persistence -> Files

서비스를 건드리지 않고 모든 작업을 수행 할 수 있다면 유연한 코드가 있습니다.

그러나 내 말을 받아들이지 마십시오. 그것을 작성하고 어떻게되는지보십시오. 내가 유연하다고 믿는 유일한 코드는 코드입니다.

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