Использование Scala, Play Framework, Slick 3, Specs2.
У меня есть слой репозиторий и слой службы. Репозитории довольно тупые, и я использую specs2
, чтобы убедиться, что сервисный уровень выполняет свою работу.
Мои репозитории возвращали фьючерсы, например:
def findById(id: Long): Future[Option[Foo]] =
db.run(fooQuery(id))
Затем он будет использоваться в сервисе:
def foonicate(id: Long): Future[Foo] = {
fooRepository.findById(id).flatMap { optFoo =>
val foo: Foo = optFoo match {
case Some(foo) => [business logic returning Foo]
case None => [business logic returning Foo]
}
fooRepository.save(foo)
}
}
Услуги было легко определить. В спецификации сервиса над FooRepository
будет издеваться так:
fooRepository.findById(3).returns(Future(Foo(3)))
Недавно я обнаружил потребность в транзакциях базы данных. Несколько запросов должны быть объединены в одну транзакцию. преобладающее мнение, по-видимому, заключается в том, что вполне нормально обрабатывать логику транзакций на уровне службы.
Имея это в виду, я изменил свои репозитории, чтобы они возвращали slick.dbio.DBIO
, и добавил вспомогательный метод для транзакционного выполнения запроса:
def findById(id: Long): DBIO[Option[Foo]] =
fooQuery(id)
def run[T](query: DBIO[T]): Future[T] =
db.run(query.transactionally)
Сервисы составляют DBIO
и, наконец, вызывают репозиторий для выполнения запроса:
def foonicate(id: Long): Future[Foo] = {
val query = fooRepository.findById(id).flatMap { optFoo =>
val foo: Foo = optFoo match {
case Some(foo) => [business logic finally returning Foo]
case None => [business logic finally returning Foo]
}
fooRepository.save(foo)
}
fooRepository.run(query)
}
Кажется, это работает, но теперь я могу указать это только так:
val findFooDbio = DBIO.successful(None))
val saveFooDbio = DBIO.successful(Foo(3))
fooRepository.findById(3) returns findFooDbio
fooRepository.save(Foo(3)) returns saveFooDbio
fooRepository.run(any[DBIO[Foo]]) returns Future(Foo(3))
Этот any
в макете run
отстой! Теперь я не проверяю фактическую логику, а вместо этого принимаю любые DBIO[Foo]
! Я пытался использовать следующее:
fooRepository.run(findFooDbio.flatMap(_ => saveFooDbio)) returns Future(Foo(3))
Но это прерывается с помощью java.lang.NullPointerException: null
, который является способом specs2
сказать "извините, приятель, метод с этим параметром не найден". Я пробовал разные варианты, но ни один из них не работал.
Я подозреваю, что это может быть потому, что функции нельзя сравнивать:
scala> val a: Int => String = x => "hi"
a: Int => String = <function1>
scala> val b: Int => String = x => "hi"
b: Int => String = <function1>
scala> a == b
res1: Boolean = false
Есть идеи, как определить состав DBIO без обмана?