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


9

私はアーキテクチャで作業しています。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サービス -> サービス -> リポジトリ -> DB

  • フロントエンド:Webクライアント、モバイルアプリ
  • APIサービス:Rest Controllers、ここではコンバーターとdtoと呼び出しサービスを使用しています
  • サービス:実装とインターフェースし、ビジネスロジックを含みます
  • リポジトリ:CRUD操作とおそらくいくつかのSQLクエリを含む自動実装(Spring Data 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
リポジトリは抽象化レイヤーです。別のdoesntのヘルプの追加
ユアン・

ああ、でも、Springが提案したインターフェース、つまりリポジトリメソッド名を使用する必要があります。リポジトリを変更したい場合は、呼び出し名を保持する必要があります。
アレハンドロ

SpringリポジトリはSpringオブジェクトを公開することを強制しないように私には見えます。不可知論的なインターフェースを実装するためにそれを使用するだけ
Ewan

回答:


6

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

正しい。これは、Spring Frameworkによって提案された懸念の分離による基本設計です。あなたは「春の正しい道」にいます。

かかわらず、リポジトリが頻繁のDAOとして使用されている、真実は春の開発者が概念のかかったということであるリポジトリをエリック・エヴァンスDDDから。CRUDメソッドと、多くの開発者がリポジトリのインターフェースをジェネリックにして、結局、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つ追加する必要があるかどうかにかかわらず、私は「いいえ」と言います。あなたの例は、さらに複雑さを加えているだけです。あなたが提案するレイヤーは、特定のロジックが必要でそれをどこに配置する必要がない場合、サービスリポジトリの間のプロキシとして、または疑似サービスリポジトリレイヤーとして終了します


1:多くの開発者が考えるのとは異なり、各リポジトリは異なるドメインのニーズに対応するため、リポジトリインターフェースは互いに完全に異なる可能性があります。Spring Data JPAでは、DAOの役割はEntityManagerが担っています。セッション、DataSourceへのアクセス、マッピングなどを管理します。

2:同様のソリューションは、Springのリポジトリインターフェースを拡張して、それらをカスタムインターフェースと混合することです。詳細については、BaseRepositoryFactoryBeanおよび@NoRepositoryBean探しください。ただし、この方法は扱いにくく、混乱を招きます。


3

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

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.