Scaling business app 
development with 
Play & Scala 
@PeterHilton 
http://hilton.org.uk/
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
Business web app 
development
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
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.
With simplicity in the right places, 
building a web application with the 
Typesafe platform is* easier and 
faster than with PHP 
* probably 
@PeterHilton • 6
Case study: 
Happy Melly
robin-berjon / CC BY-SA 2.0
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
@PeterHilton • 10
@PeterHilton • 11
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
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
@PeterHilton • 15
@PeterHilton • 15
@PeterHilton • 15
/** 
* 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),
"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))
"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))
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(_))
@PeterHilton • 18
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))
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
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 
-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
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
// Code example: Scala Slick database access 
// Nice: select * from LICENSE where id=? 
val query = Query(Licenses).filter(_.id === id)
// 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.?)
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
// 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) 
)
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
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
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
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/
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
One more thing… 
Halfway through the project, the customer 
decided to open source the application 
https://github.com/happymelly/teller 
@PeterHilton • 37
Case study: 
NIIOS
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
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
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 
-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
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
// 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" 
)
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
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) 
} 
} 
}
play-plugins-mailer 
Sending e-mail. 
Send asynchronously from an Akka actor. 
@PeterHilton • 50 
It Just Works.
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
Scaling app dev: 
lessons learned
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
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
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
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
Scaling projects: 
lessons learned
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
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
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
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
@PeterHilton 
http://hilton.org.uk/

Scaling business app development with Play and Scala

  • 1.
    Scaling business app development with Play & Scala @PeterHilton http://hilton.org.uk/
  • 2.
    M A NN 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.
    Business web app development
  • 4.
    Business applications Businessapplications support things like data management, process visibility and process automation. A special-purpose intranet application may only have 10-100 users. @PeterHilton • 4
  • 5.
    Business app developmentprojects 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.
    With simplicity inthe right places, building a web application with the Typesafe platform is* easier and faster than with PHP * probably @PeterHilton • 6
  • 7.
  • 8.
  • 9.
    Happy Melly ‘HappyMelly 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.
  • 11.
  • 13.
    Working with anexperienced 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
  • 14.
    Technical approach PlayFramework 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
  • 15.
  • 16.
  • 17.
  • 18.
    /** * HTMLform 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),
  • 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.
    "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))
  • 21.
    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(_))
  • 22.
  • 23.
    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))
  • 24.
    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
  • 25.
    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 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
  • 26.
    How to cheatat 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
  • 28.
    // Code example:Scala Slick database access // Nice: select * from LICENSE where id=? val query = Query(Licenses).filter(_.id === id)
  • 29.
    // 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.?)
  • 30.
    Scalariform Source codeformatter, 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
  • 31.
    // 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) )
  • 33.
    SecureSocial Social networkauthentication: Twitter, Facebook, Google, LinkedIn Less effort and better UX than the usual sign-up, log-in, reset password features @PeterHilton • 28 http://securesocial.ws
  • 35.
    DataTables HTML tableswith 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
  • 37.
    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
  • 39.
    Joda Money Currencyarithmetic 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/
  • 40.
    Lessons learned Youcan 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
  • 42.
    One more thing… Halfway through the project, the customer decided to open source the application https://github.com/happymelly/teller @PeterHilton • 37
  • 43.
  • 44.
    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
  • 49.
    Technical approach PlayFramework 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
  • 50.
    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 -­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐-­‐
  • 51.
    jXLS Parse andgenerate Excel spreadsheets More useful than CSV because it supports workbooks with multiple sheets @PeterHilton • 46 Simplifying data maintenance with spreadsheet integration
  • 52.
    // 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" )
  • 53.
    WebJars JavaScript/front-end librarymanagement Specify dependencies in sbt Use Play reverse routing to resolve URLs: @routes.WebJarAssets.at( WebJarAssets.locate("jquery.min.js")) @PeterHilton • 48
  • 54.
    class EmailActor extendsActor { 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) } } }
  • 55.
    play-plugins-mailer Sending e-mail. Send asynchronously from an Akka actor. @PeterHilton • 50 It Just Works.
  • 56.
    Lessons learned Developmentis 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
  • 57.
    Scaling app dev: lessons learned
  • 58.
    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
  • 59.
    Get Play frameworkbenefits Template system allows simple HTML and using existing front-end frameworks. HTML form validation API results in clear, understandable code. No XML. @PeterHilton • 54
  • 60.
    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
  • 61.
    Scale down thearchitecture 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
  • 63.
  • 64.
    Scaling down thescope 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
  • 65.
    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
  • 66.
    Scaling down theteam Scale down the architecture first. The team size trade-off is: communication overhead (big team) vs skills gaps (small team) @PeterHilton • 61
  • 67.
    Vertical and horizontalscaling 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
  • 68.