Successfully reported this slideshow.

Scaling business app development with Play and Scala

2

Share

1 of 68
1 of 68

More Related Content

Related Books

Free with a 14 day trial from Scribd

See all

Related Audiobooks

Free with a 14 day trial from Scribd

See all

Scaling business app development with Play and Scala

  1. 1. Scaling business app development with Play & Scala @PeterHilton http://hilton.org.uk/
  2. 2. M A N N I N G Covers Play 2 Peter Hilton Erik Bakker Francisco Canedo FOREWORD BY James Ward Play for Scala (Manning) Peter Hilton Erik Bakker Francisco Canedo http://bit.ly/playscala2p
  3. 3. Business web app development
  4. 4. Business applications Business applications support things like data management, process visibility and process automation. A special-purpose intranet application may only have 10-100 users. @PeterHilton • 4
  5. 5. Business app development projects Development cost is the toughest issue. @PeterHilton • 5 The following is a true story of how Scala made us awesome. Scaling is usually for runtime performance This is not that talk.
  6. 6. With simplicity in the right places, building a web application with the Typesafe platform is* easier and faster than with PHP * probably @PeterHilton • 6
  7. 7. Case study: Happy Melly
  8. 8. robin-berjon / CC BY-SA 2.0
  9. 9. Happy Melly ‘Happy Melly is a network of businesses that self-organize around a purpose: creating happy workers.’ http://www.happymelly.com/about/ Several member organisations No head office or other central location @PeterHilton • 9
  10. 10. @PeterHilton • 10
  11. 11. @PeterHilton • 11
  12. 12. Working with an experienced remote product owner @PeterHilton • 13 Release early: no ‘sprint 0’ - first release on day one Public Internet test server: Play/Scala web app hosted on Cloudbees Continuous delivery - release per feature Push to master → test server deployment
  13. 13. Technical approach Play Framework 2.1 (later upgraded to 2.2) Scala 2.10 on JDK 1.7 Slick 1.0 MySQL 5.6 Twitter Bootstrap 2 (later upgraded to 3) and some helpful libraries… @PeterHilton • 14
  14. 14. @PeterHilton • 15
  15. 15. @PeterHilton • 15
  16. 16. @PeterHilton • 15
  17. 17. /** * HTML form mapping for creating and editing. */ def organisationForm(implicit request: SecuredRequest[_]) = Form(mapping( "id" -­‐> ignored(Option.empty[Long]), "name" -­‐> nonEmptyText, "street1" -­‐> optional(text), "street2" -­‐> optional(text), "city" -­‐> optional(text), "province" -­‐> optional(text), "postCode" -­‐> optional(text), "country" -­‐> nonEmptyText, "vatNumber" -­‐> optional(text), "registrationNumber" -­‐> optional(text), "category" -­‐> optional(categoryMapping), "webSite" -­‐> optional(webUrl), "blog" -­‐> optional(webUrl), "active" -­‐> ignored(true),
  18. 18. "name" -­‐> nonEmptyText, "street1" -­‐> optional(text), "street2" -­‐> optional(text), "city" -­‐> optional(text), "province" -­‐> optional(text), "postCode" -­‐> optional(text), "country" -­‐> nonEmptyText, "vatNumber" -­‐> optional(text), "registrationNumber" -­‐> optional(text), "category" -­‐> optional(categoryMapping), "webSite" -­‐> optional(webUrl), "blog" -­‐> optional(webUrl), "active" -­‐> ignored(true), "created" -­‐> ignored(DateTime.now()), "createdBy" -­‐> ignored(request.user.fullName), "updated" -­‐> ignored(DateTime.now()), "updatedBy" -­‐> ignored(request.user.fullName) )(Organisation.apply)(Organisation.unapply))
  19. 19. "name" -­‐> nonEmptyText, "street1" -­‐> optional(text), "street2" -­‐> optional(text), "city" -­‐> optional(text), "province" -­‐> optional(text), "postCode" -­‐> optional(text), "country" -­‐> nonEmptyText, "vatNumber" -­‐> optional(text), "registrationNumber" -­‐> optional(text), "category" -­‐> optional(categoryMapping), "webSite" -­‐> optional(webUrl), "blog" -­‐> optional(webUrl), "active" -­‐> ignored(true), "created" -­‐> ignored(DateTime.now()), "createdBy" -­‐> ignored(request.user.fullName), "updated" -­‐> ignored(DateTime.now()), "updatedBy" -­‐> ignored(request.user.fullName) )(Organisation.apply)(Organisation.unapply))
  20. 20. private def validateWebUrl(url: String): Boolean = { try { val uri = new java.net.URI(url) val validScheme = ValidURLSchemes.contains( Option(uri.getScheme).getOrElse("").toLowerCase) val host = Option(uri.getHost).getOrElse("") val validDomain = DomainNameRegex.findFirstIn( host.toLowerCase).isDefined validScheme && validDomain } catch { case _: Throwable ⇒ false } } // Web site URL form mapping. val webUrl = text(maxLength = 1024) verifying ("error.url.web", validateWebUrl(_))
  21. 21. @PeterHilton • 18
  22. 22. private val FacebookDomain = "facebook.com" private val LinkedInDomain = "linkedin.com" private val GoogleDomain = "google.com" private def validateDomain(url: String, domain: String): Boolean = { try { val host = Option(new java.net.URI(url). getHost).getOrElse("").toLowerCase host == domain || host.endsWith("." + domain) } catch { case _: Throwable ⇒ false } } val facebookProfileUrl = webUrl verifying (error = "error.url.profile", validateDomain(_, FacebookDomain))
  23. 23. Simplifying front-end development Twitter Bootstrap No custom CSS or JavaScript* Master-detail pages (HTML tables, straightforward layout) Edit pages (mostly standard Bootstrap form layout) @PeterHilton • 20 * hardly any
  24. 24. http://cloc.sourceforge.net v 1.58 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Language files blank comment code -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Scala 81 1063 2747 4494 HTML 40 321 0 2855 SQL 49 248 14 655 Javascript 5 30 150 284 CoffeeScript 1 15 3 51 XML 1 4 24 14 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ SUM: 177 1681 2938 8353 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
  25. 25. How to cheat at front-end dev We only supported the latest version of Google Chrome … and the usual pain just went away Reminder: intranet application, few users, and an experienced product owner. @PeterHilton • 22
  26. 26. // Code example: Scala Slick database access // Nice: select * from LICENSE where id=? val query = Query(Licenses).filter(_.id === id)
  27. 27. // Code example: Scala Slick database access // Nice: select * from LICENSE where id=? val query = Query(Licenses).filter(_.id === id) // Nasty: select b.NUMBER, b.DATE, p.NAME, o.NAME from BOOKING b // inner join ACCOUNT a on (a.ID=b.FROM_ID) // left outer join PERSON p on (p.ID=a.PERSON_ID) // left outer join ORGANISATION o on (o.ID=a.ORGANISATION_ID) val query = for { entry ← BookingEntries ((fromAccount, fromPerson), fromOrganisation) ← Accounts leftJoin People on (_.personId === _.id) leftJoin Organisations on (_._1.organisationId === _.id) if fromAccount.id === entry.fromId } yield ( entry.bookingNumber, entry.bookingDate, fromPerson.name.?, fromOrganisation.name.?)
  28. 28. Scalariform Source code formatter, integrated with sbt We liked it so much we set it up to reformat code on every compilation and replace ASCII art arrows with ⇒ and ← @PeterHilton • 25 https://github.com/mdr/scalariform
  29. 29. // project/Build.sbt … val main = play.Project(appName, appVersion, appDependencies resolvers += Resolver.url("sbt-­‐plugin-­‐releases", url(" resolvers += Resolver.url("Objectify Play Snapshot Repository resolvers += Resolver.url("Objectify Play Repository", routesImport += "binders._" ).settings( // Reformat code before every compilation :) ScalariformKeys.preferences := FormattingPreferences(). setPreference(SpacesWithinPatternBinders, false). setPreference(PreserveSpaceBeforeArguments, true). setPreference(RewriteArrowSymbols, true) )
  30. 30. SecureSocial Social network authentication: Twitter, Facebook, Google, LinkedIn Less effort and better UX than the usual sign-up, log-in, reset password features @PeterHilton • 28 http://securesocial.ws
  31. 31. DataTables HTML tables with client-side filter and sort, in this case from server-side HTML tables. http://datatables.net DataTables-Bootstrap integrates styling. http://datatables.net/manual/styling/bootstrap @PeterHilton • 30
  32. 32. pegdown & JSoup Markdown processing - an easy way to use standard HTML forms to edit HTML https://github.com/sirthias/pegdown JSoup sanitises the resulting HTML using an HTML whitelist http://jsoup.org @PeterHilton • 32
  33. 33. Joda Money Currency arithmetic and conversion API. Money type for an amount with a currency. Arithmetic and currency conversion, with an explicit rounding policy. @PeterHilton • 34 http://www.joda.org/joda-money/
  34. 34. Lessons learned You can save a lot of time on front-end development if you cheat. Development is very fast with two experienced developers. Slick had a steep learning curve* and some scary queries, but we still liked it. * writing/publishing Slick tutorials helped @PeterHilton • 35
  35. 35. One more thing… Halfway through the project, the customer decided to open source the application https://github.com/happymelly/teller @PeterHilton • 37
  36. 36. Case study: NIIOS
  37. 37. Netherlands Institute for Innovative Ocular Surgery (NIIOS) Independent eye surgery clinic in Rotterdam, the Netherlands. ISO accreditation requires quality management and detailed reporting. Status quo: lots of spreadsheets. @PeterHilton • 39
  38. 38. Technical approach Play Framework 2.2 Scala 2.10 on JDK 1.7 Slick 2.0 PostgreSQL 9.3 Twitter Bootstrap 2 … and jXLS, webjars, play-plugins-mailer @PeterHilton • 44
  39. 39. http://cloc.sourceforge.net v 1.58 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Language files blank comment code -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ Scala 55 548 572 2497 HTML 20 179 0 1456 SQL 13 157 30 668 CoffeeScript 6 43 39 133 XML 2 8 6 39 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐ SUM: 96 935 647 4793 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
  40. 40. jXLS Parse and generate Excel spreadsheets More useful than CSV because it supports workbooks with multiple sheets @PeterHilton • 46 Simplifying data maintenance with spreadsheet integration
  41. 41. // build.sbt libraryDependencies ++= Seq( jdbc, "com.github.tototoshi" %% "slick-­‐joda-­‐mapper" % "1.1.0", "com.typesafe.slick" %% "slick" % "2.0.2", "com.typesafe.play" %% "play-­‐slick" % "0.6.0.1" , "net.sf.jxls" % "jxls-­‐core" % "1.0.5", "net.sf.jxls" % "jxls-­‐reader" % "1.0.5", "org.postgresql" % "postgresql" % "9.3-­‐1101-­‐jdbc41", "org.webjars" %% "webjars-­‐play" % "2.2.1-­‐2", "org.webjars" % "bootstrap" % "3.1.0", "org.webjars" % "datatables" % "1.10.0", "org.webjars" % "datatables-­‐bootstrap" % "2-­‐20120202-­‐2", "com.typesafe" %% "play-­‐plugins-­‐mailer" % "2.2.0" )
  42. 42. WebJars JavaScript/front-end library management Specify dependencies in sbt Use Play reverse routing to resolve URLs: @routes.WebJarAssets.at( WebJarAssets.locate("jquery.min.js")) @PeterHilton • 48
  43. 43. class EmailActor extends Actor { override def receive = { case m@EmailMessage(to, subject, body) => { import com.typesafe.plugin val mailer: MailerAPI = plugin.use[MailerPlugin].email mailer.setRecipient(to: _*) mailer.setSubject(subject) mailer.setFrom(from) mailer.send(body.trim) } } }
  44. 44. play-plugins-mailer Sending e-mail. Send asynchronously from an Akka actor. @PeterHilton • 50 It Just Works.
  45. 45. Lessons learned Development is fast and predictable if you’ve used the same architecture before. ‘We only support Chrome’ is possible twice. One thing didn’t work: authentication via NTLM challenge-response on Microsoft IIS @PeterHilton • 51
  46. 46. Scaling app dev: lessons learned
  47. 47. Scale down @PeterHilton • 53 High-performance technology can scale down, as well as up. Who knew? It turns out that Play and Scala make simple applications easier to build. Bonus: maintainability and performance
  48. 48. Get Play framework benefits Template system allows simple HTML and using existing front-end frameworks. HTML form validation API results in clear, understandable code. No XML. @PeterHilton • 54
  49. 49. Get Scala benefits Strong types capture the domain model more explicitly and clearly (DDD FTW!) Less verbose code, with immutable types, is easier to debug and maintain. Third-party Java libraries remain essential. @PeterHilton • 55
  50. 50. Scale down the architecture No web front-end development (no custom JavaScript or CSS) Standard action-based MVC (server-side form validation only) Database most familiar to the team (avoid surprises and getting stuck) No reactive programming (would be premature optimisation here) @PeterHilton • 56
  51. 51. Scaling projects: lessons learned
  52. 52. Scaling down the scope The first version of a business application has a lot in common with a start-up’s MVP (although a start-ups usually include front-end dev and branding in ‘minimum viable’) @PeterHilton • 59
  53. 53. Scaling up productivity Throughput. Cycle time. One developer on the team needs to know enough about agile software development to be able to get people using the software before the project gets cancelled. @PeterHilton • 60
  54. 54. Scaling down the team Scale down the architecture first. The team size trade-off is: communication overhead (big team) vs skills gaps (small team) @PeterHilton • 61
  55. 55. Vertical and horizontal scaling In this context, vertical scaling is about making each developer more productive. Horizontal scaling means more developers … at the cost of exponentially increasing overhead. @PeterHilton • 62
  56. 56. @PeterHilton http://hilton.org.uk/

×