Play + scala + reactive mongo

Post on 10-May-2015

6.490 views 12 download

Tags:

description

Meetup presentation. Covers building a non-blocking asynchronous data-access layer using Scala, Play framework, MongoDB and the Reactive Mongo Driver

Transcript of Play + scala + reactive mongo

Play + Scala + Reactive Mongo

Building a “reactive” data-access layer to mongoDB

About Us

Max Kremer

Trialfire - co-founderfounded in June 2013

Autodesk - cloud solutions architect

Datastay - co-founderAcquired by Autodesk 2011

Marconi Lanna

Trialfire - lead developerfounded in June 2013

Too many startups since 1996 to list here

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

What is “reactive”

Fully non-blocking and asynchronous I/O operations

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

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))}}}   

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

Data Access Layer

• Active Record Design Pattern• Based on Futures• Model = Case Class + Companion Object• Stackable Traits

Persistence using Traitsimport 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]}

The Data Access Trait

trait DataAccess[M] {    def collectionName: Stringimplicit val format: Format[M]

private def db = ReactiveMongoPlugin.dbprivate def collection = db[JSONCollection](collectionName)

    def insert(instance: M): (BSONObjectID, Future[LastError]) = {     val id = BSONObjectID.generate       (id, collection.insert(Json.toJson(instance) ++ id))   }

The Data Access Trait (cont’d)

def update(instance: M): Future[LastError] = {instance.id map { id =>collection.update(id, Json.toJson(instance))} getOrElse Future.successful(LastError(err = Some("Invalid ID"))}

def byId(id: BSONObjectID): Future[Option[M]] = {    collection.find(id).cursor.headOption map { _ flatMap { doc: JsObject =>        doc.asOpt[M] } }}

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

Cons

• Futures all the way down…• Futures all the way up, too...• No joins

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 =>...}

Links

reactive mongo driver: http://reactivemongo.org/

Play ReactiveMongo plugin: https://github.com/ReactiveMongo/Play-eactiveMongo

The End

Thanks for listening

Future[Option[Applause]]

max@trialfire.com marconi@trialfire.com

We’re hiring!