web-dev-qa-db-ja.com

クリーンなアーキテクチャのためにサービスとリポジトリの間のレイヤーを使用する必要があります-Spring

私はアーキテクチャで作業しています。Webクライアントおよびモバイルアプリ用のREST APIを提供する予定です。私はSpring(spring mvc、spring data jpaなど)を使用しています。ドメインモデルは、JPA仕様でコーディングされています。

クリーンアーキテクチャのいくつかの概念を適用しようとしています( https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html )。すべてではありません。これは、jpaドメインモデルを維持するためです。

レイヤーを通過する実際のフローは次のとおりです。

フロントエンド<->APIサービス->Service->Repository->[〜# 〜] db [〜#〜]

  • フロントエンド:Webクライアント、モバイルアプリ
  • API Service:Rest Controllers、ここではコンバーターとdtoを使用し、サービスを呼び出します
  • Service:実装とのインターフェースであり、ビジネスロジックが含まれています
  • Repository:CRUD操作とおそらくいくつかのSQLクエリを含む、自動的に実装される(Spring Data JPAによって実行される)リポジトリインターフェイス

私の疑問:サービスとリポジトリの間に追加のレイヤーを使用する必要がありますか?

私はこの新しいフローを計画しています:

フロントエンド<->APIサービス->Service->Persistence->Repository->[〜#〜] 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)
    }
}

永続化レイヤーの実装を変更するだけでよいので、サービスを変更せずに残しました。リポジトリがフレームワークに関連しているという事実と相まってください。

どう思いますか?ありがとう。

9
Alejandro

フロントエンド<-> APIサービス->サービス->リポジトリ-> DB

正しい。これは、Spring Frameworkによって提案された懸念の分離による基本設計です。だから、あなたは "Spring's right way"にいます。

RepositoriesはDAOとして頻繁に使用されますが、真実は、Spring開発者がRepositoryの概念をEric EvansのDDDから採用したことです。 CRUDメソッドと多くの開発者がリポジトリのインターフェースをジェネリックにして最終的にはEntityManager(true 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を別のdata sourceに置き換える新しい実装を取得するだけです。

    //@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つ追加する必要があるかどうかは、必要ないのでノーです。あなたの例は、さらに複雑さを加えているだけです。あなたが提案するレイヤーは、特定のロジックがservicesrepositoriesの間のプロキシとして、またはpseudo-service-repositoryレイヤーとして最終的に作成されます必要な場所に配置する必要はありません。


1:多くの開発者が考えるのとは異なり、各リポジトリは異なるドメインのニーズに対応するため、リポジトリインターフェースは互いに完全に異なる可能性があります。 Spring Data JPAでは、ロール[〜#〜] dao [〜#〜]EntityManagerによって実行されます。セッション、DataSourcemappingsなどへのアクセスを管理します。

2:同様のソリューションは、Springのリポジトリインターフェースを拡張して、それらをカスタムインターフェースと混合することです。詳細については、BaseRepositoryFactoryBeanおよび@ NoRepositoryBeanを探してください。しかし、私はこのアプローチが面倒でわかりにくいことに気づきました。

6
Laiv

設計が柔軟であることを証明する最良の方法は、それを曲げることです。

永続化を担当するが、リポジトリを使用するという考えに縛られないコード内の場所が必要です。いいよ現時点では、何も役に立っていません...ため息、結構です。

OK、このシャントレイヤーが何かうまくいったかどうかをテストしましょう。製品をファイルに保持するフラットファイルレイヤーを作成します。この新しいレイヤーはこのデザインのどこに行くのですか?

まあそれは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

サービスに触れずにすべてを機能させることができる場合は、柔軟なコードを使用できます。

しかし、私の言葉をそれに取ってはいけません。それを書いて、何が起こるか見てください。柔軟であると信頼できる唯一のコードは、フレックスコードです。

3
candied_orange