2017-01-12 20 views
2

私のプレーフレームワーク(2.5)アプリでは、サービスの単体テストを書く必要があります。Slick 3:トランザクションでリポジトリパターンを実装する方法は?

私はリポジトリのインターフェースを作成して、私のユニットテストでそれらを模擬したいこのため 、単独でサービス層をテストすることができへのデータアクセスロジックを分離する必要があり

class UserService { 
    def signUpNewUser(username: String, memberName: String): Future[Unit] { 
     val userId = 1 // Set to 1 for demo 
     val user = User(userId, username) 
     val member = Member(memberName, userId) 
     // ---- I NEED TO EXECUTE THIS BLOCK WITHIN TRANSACTION ---- 
     for { 
     userResult <- userRepository.save(user) 
     memberRepository.save(member) 
     } yield()  
     // ---- END OF TRANSACTION ---- 
    } 
} 

上記の例では、userRepository.save(User)memberRepository.save(member)操作はトランザクション内で実行する必要があります。

テストを複雑にするので、私は自分のサービス層で直接slickを使いたくありません。

また、私は単体テストに組込みデータベースを使いたくないのですが、他の場所では単体テストではありません。完全な分離が必要です。

私は私のリポジトリのインタフェースが全くツルツルに依存することにしたいが、このようなものは必要ありません:どのように私は滑らかなでこれを達成することができます

trait UserRepository { 
    findById(id: Long): Future[Option[User]] 
    save(user: User): Future[Unit] 
} 

を?

答えて

1

OK - 質問を3つの部分に分解しましょう。あなたが行われていDBIOFutureに変換するとすぐにHow to use transaction in slick

はどのように

は基本的にこの回答を読んで、トランザクションにブロックを実行します。単一のトランザクション内で複数の操作を構成する機会はありません。物語の終わり。テスト

Slickを使用しないようにする方法

これは、基本的な設計の問題である - 取引でこのサービス層の契約を聞かせてより - あなたはどんなRepository/DAO /の上のビジネス層を持つようにしたい場合。このレイヤーの外側にあるSlickと対話する必要はありません。

は、最も簡単な方法でSlick

に依存するリポジトリのインターフェースを回避 - あなたは、トランザクション内での操作を構成するためにスリックDBIOに依存する必要があります(とトランザクション内Repository方法を構成することで、任意ので避けることができないものです深刻なアプリケーション)。

DBIOに依存しないようにするには、おそらく自分のモナディックタイプ、たとえばTransactionBoundary[T]またはTransactionContext[T]を作成します。

TransactionContext[T]を実行するTransactionManagerのようなものがあります。努力する価値はない

私見では、私は単純にすでに輝かしい名を持つDBIOを使用すると思います(HaskellのモナドIOのように - DBIOあなたはあなたのストレージ上で実行IO操作の説明を持っていることを通知します)。しかし、あなたはまだそれを避けたいと仮定しよう。

あなたは、おそらくそのようなこと行うことができます:あなたの特性は次のようになります

package transaction { 

    object Transactions { 
    implicit class TransactionBoundary[T](private[transaction] val dbio: DBIO[T]) { 
     // ... 
    } 
    } 

    class TransactionManager { 
    def execute[T](boundary: TransactionBoundary[T]): Future[T] = db.run(boundary.dbio) 
    } 
} 

を:

transactionManager.execute(
    for { 
     userResult <- userRepository.save(user) 
     memberRepository.save(member) 
    } yield() 
) 
:あなたはこのように行うだろう

trait UserRepository { 
    findById(id: Long): TransactionBoundary[Option[User]] 
    save(user: User): TransactionBoundary[Unit] 
} 

とどこかにあなたのコード内の

暗黙の変換を使用すると、メソッドの結果はRepositoryになります。au自動的にTransactionBoundaryに変換されます。

しかしもう一度 - 上記のすべては、おそらく美容の味を除いて、DBIOの使用以上に実際の利点をもたらすものではありません。

type TransactionBoundary[T] = DBIO[T] 

、どこでもそれを使用する:あなたは、特定の層の外側Slick関連するクラスを使用しないようにしたい場合は、ちょうどこのようなタイプのエイリアスを作ります。

関連する問題