Build a Reactive Data Layer for MongoDB with Play + Scala + ReactiveMongo
1. Play + Scala + Reactive Mongo
Building a “reactive” data-access layer to
mongoDB
2. About Us
Max Kremer
Trialfire - co-founder
founded in June 2013
Autodesk - cloud solutions architect
Datastay - co-founder
Acquired by Autodesk 2011
Marconi Lanna
Trialfire - lead developer
founded in June 2013
Too many startups since
1996 to list here
3. Why “reactive”
Classic Synchronous Model:
With a traditional synchronous database driver, each
operation blocks the current thread until a response is
received.
More requests = more threads waiting = poor
scalability
5. Play and async I/O
• Java NIO
• Non-blocking, asynchronous IO
• Process multiple HTTP requests with a single thread
• Large number of concurrent requests can be handled
with a few threads
6. Example
•A Play controller using a Future result:
package controllers
import play.api.mvc.{Action, Controller}
import concurrent.{ExecutionContext, Future}
import ExecutionContext.Implicits.global
object StuffController extends Controller {
def doStuff( ) = Action {
val someStuff = scala.concurrent.future {
models.Stuff.fetch( )
}
Async {
someStuff.map(value => Ok(value))
}
}
}
7. A word about Futures
•Represents a value that will be available later
•Execution contexts - think “thread pools”
•Futures are Monads
•Layer of abstraction over multi-threading
8. Data Access Layer
•Active Record Design Pattern
•Based on Futures
•Model = Case Class + Companion Object
•Stackable Traits
9. Persistence using Traits
import play.api.libs.json.Json
case class User
( id : Option[BSONObjectID]
, firstName: String
, lastName : String
, email : String
, password : String)
object User extends DataAccess[User] {
def collectionName = “user”
implicit val format = Json.format[User]
}
10. The Data Access Trait
trait DataAccess[M] {
def collectionName: String
implicit val format: Format[M]
private def db = ReactiveMongoPlugin.db
private def collection = db[JSONCollection](collectionName)
def insert(instance: M): (BSONObjectID, Future[LastError]) = {
val id = BSONObjectID.generate
(id, collection.insert(Json.toJson(instance) ++ id))
}
12. Pros
•easy compared to sql
•no schemas
•no queries
•no migration
•it just works
conversion from/to json automatically
handled by play json api macros
14. Cons (cont’d)
case class Book
( name : String
, author: Author)
case class author(name: String) {
lazy val books: Seq[Book] = Book.byAuthor(this)
}
lazy val books: Future[Seq[Book]] = Book.byAuthor(this)
val books = author.books
author.books map { books =>
...
}