2014-04-04 2 views
11

Play를 개발 중입니다! 2.2 Slick 2.0을 사용한 스칼라에서의 애플리케이션. 이제 데이터 액세스 측면을 다루면서 Cake Pattern을 사용하려고한다. 유망한 것처럼 보이지만 실제로 뭔가 간단한 것을 성취하기 위해 엄청난 양의 클래스/특성/객체를 작성해야 할 필요가있는 것 같습니다. 그래서 나는 이것에 대해 약간의 빛을 사용할 수 있었다. User 개념은 매우 간단한 예를 촬영스칼라 슬릭 케이크 패턴 : 9000 클래스 이상?

, 나는 그것을 이해하는 방식이 우리가해야

case class User(...) //model 

class Users extends Table[User]... //Slick Table 

object users extends TableQuery[Users] { //Slick Query 
//custom queries 
} 

을 지금까지 완전히 합리적이다. 이제 우리는 "케이크 패터닝"UserRepository을 추가

다음
trait UserRepository { 
val userRepo: UserRepository 
class UserRepositoryImpl { 
    //Here I can do some stuff with slick 
    def findByName(name: String) = { 
     users.withFilter(_.name === name).list 
    } 
    } 
} 

우리가 가지고 UserService :

trait UserService { 
this: UserRepository => 
val userService: UserService 
class UserServiceImpl { // 
    def findByName(name: String) = { 
     userRepo.findByName(name) 
    } 
    } 
} 

이제 우리는 객체에이 모든 것을 섞어 :

object UserModule extends UserService with UserRepository { 
    val userRepo = new UserRepositoryImpl 
    val userService = new UserServiceImpl 
} 
  1. UserRepository은 정말로 유용합니까? 나는 Users 매끄러운 개체에 사용자 지정 쿼리로 findByName을 쓸 수 있습니다.

  2. Customer에 대해 이와 같은 다른 클래스 집합이 있다고 가정하고이 중 일부를 UserService 기능에 사용해야합니다.

내가 수행해야합니다

CustomerService { 
this: UserService => 
... 
} 

또는

CustomerService { 
val userService = UserModule.userService 
... 
} 
+0

어디에서 9000을 얻었습니까? – Blankman

+0

@Blankman http://knowyourmeme.com/memes/its-over-9000 – user1498572

답변

10

확인을, 좋은 목표와 같은 그 소리 : 데이터베이스 라이브러리 (매끄러운 이상

  • 추상 .. .당신이 할 수 CustomerRepository에 대한 다음

    trait UserRepository { 
        type User 
        def findByName(name: String): User 
    } 
    
    // Implementation using Slick 
    trait SlickUserRepository extends UserRepository { 
        case class User() 
        def findByName(name: String) = { 
         // Slick code 
        } 
    } 
    
    // Implementation using Rough 
    trait RoughUserRepository extends UserRepository { 
        case class User() 
        def findByName(name: String) = { 
         // Rough code 
        } 
    } 
    

    :)

  • 는 특성의 당신은 이런 식으로 뭔가를 할 수

검증 장치 확인

trait CustomerRepository { this: UserRepository => 
} 

trait SlickCustomerRepository extends CustomerRepository { 
} 

trait RoughCustomerRepository extends CustomerRepository { 
} 

을 그리고 당신의 백엔드를 기반으로 그들을 결합 변덕 :

object UserModuleWithSlick 
    extends SlickUserRepository 
    with SlickCustomerRepository 

object UserModuleWithRough 
    extends RoughUserRepository 
    with RoughCustomerRepository 
,210

당신과 같이 단위 테스트 할 객체를 만들 수 있습니다

object CustomerRepositoryTest extends CustomerRepository with UserRepository { 
    type User = // some mock type 
    def findByName(name: String) = { 
     // some mock code 
    } 
} 

을 당신은

trait CustomerRepository { this: UserRepository => 
} 

object Module extends UserRepository with CustomerRepository 

trait CustomerRepository { 
    val userRepository: UserRepository 
    import userRepository._ 
} 

object UserModule extends UserRepository 
object CustomerModule extends CustomerRepository { 
    val userRepository: UserModule.type = UserModule 
} 

사이에 강한 유사성이 있음을 관찰 할 올바른 이것은이다 Scala 세계에 맞게 업데이트 된 오래된 상속/집계 트레이드 오프. 각 방법에는 장단점이 있습니다. 혼합 특성을 사용하면 추적 할 수있는 구체적인 개체를 더 적게 만들 수 있습니다 (위와 같이 사용자와 고객을위한 별도의 개체가 아닌 단일 Module 개체 만 있음). 반면 개체 생성시 특성을 혼합해야하므로 예를 들어 기존 UserRepository을 가져 와서 혼합하여 CustomerRepository으로 만들 수는 없습니다. 필요하다면 집계를 사용해야합니다. 스칼라가 경로 의존형이 동일하다는 것을 받아들이려면 집계 (aggregation)가 위에 (: UserModule.type)와 같은 싱글 톤 유형을 지정해야하는 경우가 종종있다. 특성을 혼합하는 또 다른 힘은 재귀 적 종속성을 처리 할 수 ​​있다는 것입니다. UserModuleCustomerModule은 서로 무언가를 제공 할 수 있고 서로간에 무언가를 요구할 수 있습니다. 이것은 지연된 (lazy) val을 사용하는 aggregation에서도 가능하지만, 혼합 특성에보다 구문 적으로 편리합니다.

+0

이미 답변 해 주셔서 감사합니다.내가 원했던 하나의 추상화는 필요에 따라 매끈 매끈한 것에서 다른 것으로 바꿀 수있는 능력입니다. 또한이 모든 것을 쉽게 테스트 할 수 있기를 바랍니다. 클래스를 조롱하는 데 도움이되는 패턴에 대해 생각한 이유이기도합니다. – user1498572

+0

큰 설명과 예제를 제공해 주셔서 감사합니다. 지금은 훨씬 명확합니다. 싱글 톤 유형과 재귀 적 종속성 또한 내가 만난 이슈이기 때문에 도움이되었다. mixin trait 접근법에 대해 저를 괴롭히는 것은 모든 혼합 된 의존성으로 인해 모듈에 모든 리포지토리/서비스/모든 것을 빠르게 포함한다는 것입니다. 따라서 모듈을 사용할 때 모든 "부수적 인" 수업은 반드시 필요하거나 원하지는 않지만. – user1498572

4

최근에 발행 된 Slick architecture cheat sheet을 확인하십시오. 데이터베이스 드라이버를 통해 추상화하지는 않지만 그렇게하는 것은 간단합니다. 그냥 포장하십시오.

class Profile(profile: JdbcProfile){ 
    import profile.simple._ 
    lazy val db = ... 
    // <- cheat sheet code here 
} 

케이크 패턴이 필요하지 않습니다. 그냥 하나의 파일에 모두 넣으면 그 파일없이 빠져 나옵니다. 케이크 패턴을 사용하면 구문 오버 헤드를 지불하려는 경우 코드를 다른 파일로 분할 할 수 있습니다. 사람들은 케이크 패턴을 사용하여 다양한 서비스 조합을 비롯한 다양한 구성을 만들지 만, 귀하와 관련이 있다고 생각하지 않습니다.

데이터베이스 테이블 당 반복되는 구문 오버 헤드가 신경 쓰이는 경우 코드를 생성하십시오.

손으로 작성한 코드와 생성 된 코드를 혼합하려면 코드 작성기에 손으로 작성한 코드를 제공하거나 생성 된 코드가 손으로 직접 작성한 코드에서 상속 한 스키마를 사용하십시오. 그 반대로.

Slick을 다른 것으로 바꾸려면 다른 라이브러리를 사용하는 쿼리로 DAO 방법을 바꾸십시오.

+0

좋은 템플릿을 사용하면 아마도이 특별한 경우에 케이크 패턴이 과잉이라고 생각할 수 있습니다. 그러나 이것은 단순함을 위해 취한 기본 예이며 실제로 응용 프로그램은 더 크고 일부 모듈성을 사용할 수 있습니다. 코드 생성기를 사용해 보았지만 좋았지 만, 예를 들어 연극 진화와 관련하여 몇 가지 부작용 문제가 있습니다. 해결책이 있다는 것을 알고 있지만 아직 그것에 들어가고 싶지 않습니다. 또한 비즈니스 모델 클래스 (사례 클래스 사용자)를 DAO 종속성과 완전히 구분하고 별도의 파일로 가져 오는 것이 좋습니다. 하지만 매끄러운 부분에 템플릿을 사용하겠습니다. – user1498572

+0

예, 매끄러운 코드 gen과 play (또는 slick) evolutions의 통합을 제공해야합니다. 이 방향에 대한 연구가 있습니다. http://blog.papauschek.com/2013/12/play-framework-evolutions-slick-2-0-code-generator/ – cvogt

+0

예. 유망 해 보였습니다. 아마도 안정화되고 앱이 너무 커지면 결국 사용하게 될 것입니다. 그러나 현재 가지고있는 모델 클래스의 수는 그렇게 크지 않고 수동으로 관리 할 수 ​​있습니다. 또한 내가하는 일을 실제로 이해하는 데 도움이됩니다.) – user1498572