Play framework: lessons learned

4,562 views

Published on

Published in: Software
0 Comments
33 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,562
On SlideShare
0
From Embeds
0
Number of Embeds
328
Actions
Shares
0
Downloads
0
Comments
0
Likes
33
Embeds 0
No embeds

No notes for slide

Play framework: lessons learned

  1. 1. Play Framework Lessons Learned @PeterHilton http://hilton.org.uk/
  2. 2. Lessons learned 1. Coolness 2. Architecture 3. Writing code 4. Dev/prod environments 5. Reducing project risk @PeterHilton • 2
  3. 3. Lesson 1: Play is cool
  4. 4. ‘Anyone who says “and using this tool I can remove the programmers” just became the programmer’ ! Allan Kelly - @allankellynet @PeterHilton • 4
  5. 5. Play Framework Action-based MVC web framework for applications in Java or Scala. ! Full-stack framework with a simple, flexible and powerful HTTP API and a high-performance scalable architecture. @PeterHilton • 5
  6. 6. package controllers ! import play.api.mvc.{Action, Controller} import models.Product ! // Play web application controller. object Products extends Controller { ! // Action method -­‐ show a list of products. def list = Action { val products = Product.findAll Ok(views.html.products.list(products)) } }
  7. 7. /** Formats a Product instance as JSON. */ implicit object ProductWrites extends Writes[Product] { def writes(product: Product) = Json.obj( "name" -­‐> Json.toJson(product.name), "description" -­‐> Json.toJson(product.description)) } ! /** Render a list of products in JSON format. */ def listJson = Action { // Automatically sets Content-­‐type: application/json Ok(Json.toJson(Product.findAll)) }
  8. 8. # HTTP routes configuration. ! GET /products controllers.Products.list GET /products.json controllers.Products.listJson HTTP method URL path controller action mapping
  9. 9. Play Framework Designed by web developers for web developers ! High-productivity web development ! Play is fun @PeterHilton • 9
  10. 10. Template syntax starts with @ Template parameter declaration @(products: List[Product]) ! ! @if(products.isEmpty) { ! <h1>No products</h1> ! } No delimiter for the end of a template syntax section
  11. 11. @(products: List[Product]) ! ! @if(products.isEmpty) { ! <h1>No products</h1> ! } else { ! <h1>@products.size products</h1> ! } Output the value of an expression
  12. 12. @(products: List[Product]) ! @if(!products.isEmpty) { ! <table> @for((product, index) <-­‐ products.zipWithIndex) { <tr class='@(if (index % 2 == 0) "even" else "odd" )'> <td>@product.name</td> <td>@product.price</td> </tr> } </table> }
  13. 13. @(! products: List[Product]) ! ! @if(!products.isEmpty) { ! ! <table> ! @for((product, index) <-­‐ products.zipWithIndex) { <tr class='@(if (index % 2 == 0) "even" else "odd" )'> <td>@product.name</td> <td>@product.price</td> </tr> } </table> } XML inside an HTML attribute would be a mess
  14. 14. Code reloading 1. Change code (Java, Scala, HTML, etc) 2. ⌘R ! Code reloading is essential for: immediate feedback fast development cycle maintaining developer focus… @PeterHilton • 13
  15. 15. Parse error: syntax error, unexpected T_VARIABLE in /usr/local/www/htdocs/index.php on line 3
  16. 16. Error messages Error messages must be immediate, in the right place and include context @PeterHilton • 17
  17. 17. Developer Experience (DX) Developers are people too DX is UX for developers Good DX = happy + productive developers ! Code reloading and good error messages are the most important factors for good DX @PeterHilton • 18
  18. 18. More cool front-end features Manage dependencies with web jars LESS compilation CoffeeScript compilation JSHint JavaScript code checking RequireJS integration Built-in ETag, GZip and Cache-Control @PeterHilton • 19
  19. 19. Summary Code reloading during development Useful error pages Clean API for direct HTTP programming (doesn’t break the web browser) Modern front-end development (plays with HTML5 instead of fighting it) High-performance HTTP server @PeterHilton • 20
  20. 20. Lesson 2: Architecture
  21. 21. ‘All problems in computer science can be solved by another level of indirection, except of course for the problem of too many indirections.’ ! David Wheeler @PeterHilton • 22
  22. 22. Direct HTTP programming HTTP and the web matter We don’t need to hide HTTP ! We don’t need another abstraction layer (or any other kind of indirection) ! Play has a well-designed flatter HTTP API (Play doesn’t use the Servlet API) @PeterHilton • 23
  23. 23. Stateless architecture No state in the application’s web tier e.g. Java Servlet API’s ‘HTTP session’ State belongs in other tiers: HTTP client, server cache or database Web app behaviour defined by URLs (except for identifying the logged-in user) @PeterHilton • 25
  24. 24. Stateless architecture benefits Simplified development and testing (a URL is all you need for reproducibility) Matches stateless web architecture (HTTP is stateless by design) Avoids synchronising state between layers (‘sync’ should ring tech design alarm bells) Cloud deployment & horizontal scalability e.g. on Heroku or Cloudbees @PeterHilton • 26
  25. 25. The web browser is your friend ! Friends don’t let friends break standard HTTP features Don’t break the Back button Keep your URLs clean @PeterHilton • 27
  26. 26. h t t p : / / a p p . e x a m p l e . c o m / W a r R o o t W e b D i r e c t o r y 1 / S e r v l e t s O n A P l a n e ? s e s s i o n I d = x 8 1 n j 3 8 a v n g j L O L a n 4 w q & a c t i o n = N e x t P a g e & H o n e y B a d g e r C a r e s = f a l s e & e n t i t y I d = 1 2 9 9 1 2 7 4 3 & p r o c e s s I d = U n l a d e n S w a l l o w C a l c & r o l e = p e o n & d a t e = 7 % 2 F 1 0 % 2 F 2 0 1 4 & f l a g S e t t i n g s = 0 1 0 1 0 1 1 1 0 0 1 0 1 1 & r e d i r e c t = % 2 F v i d e o s % 2 F r i c k r o l l . a v i
  27. 27. URL-centric design Clean URLs are stable URLs - your application’s public API (and part of the UI) http://example.com/products http://example.com/product/42 Design the URL scheme before coding. Configure application URLs in one file. Read it, bookmark it, mail it, tweet it. @PeterHilton • 29
  28. 28. # HTTP routes configuration file ! GET / controllers.Application.index() ! GET /products controllers.Products.list() POST /products controllers.Products.add(p: Product) GET /product/:id controllers.Products.details(id: Long) DELETE /product/:id controllers.Products.delete(id: Long) ! GET /products.json controllers.Products.listJSON() GET /product/:id.json controllers.Products.detailsJSON(id:Long)
  29. 29. User-interface architecture Think carefully about UI layer architecture Embrace HTML5 technologies Consider modern approaches: CoffeeScript instead of JavaScript LESS instead of CSS Twitter Bootstrap for consistent HTML. @PeterHilton • 31
  30. 30. What’s worse than spaghetti code?
  31. 31. Lasagne architecture! UI, web tier, services, business logic, model, database, etc plasticrevolver / CC BY-SA 2.0
  32. 32. view URL routing controllers templates model database mapping HTTP queries renders presents calls
  33. 33. Architecture summary A stateful web tier is not a cool thing Embrace HTTP-centric web architecture Beware of lasagne architecture Unlearn complexity and n-tier habits Combine multiple design perspectives UI-centric vs model-centric vs URL-centric Use cloud-ready architecture Leverage cloud tooling for running locally @PeterHilton • 37
  34. 34. Lesson 3: Writing code
  35. 35. Optimising for time may cost you… CPU time vs programmer vs maintainer efficiency
  36. 36. Writing Scala code Don’t worry about Scala style to start with (your code style will evolve anyway) Start with the syntax It takes a year to discover idiomatic style @PeterHilton • 40
  37. 37. Writing code Spend time on code review or pair programming Don’t to be clever Aim for consistency Optimise code for maintainability @PeterHilton • 41
  38. 38. Scala coding rules of thumb Immutability is your friend Learn to use functional style in methods … but don’t go overboard Humans need more info than the compiler … so add more variable names and types Use the REPL to try things out @PeterHilton • 42
  39. 39. Scala coding recommendations Study the (immutable) collections API (ignore mutable collections) ! Put logic in pure functions and test those def calculateRiskScore (factors: List[Factor]): Int @PeterHilton • 43
  40. 40. Scala coding recommendations Split large classes into traits ! Sometimes a Customer is just a NamedOrganisation ! Using more specific traits improves maintainability and compilation speed @PeterHilton • 44
  41. 41. Play application code Keep controllers and templates lightweight Single-use view case classes: @PeterHilton • 45 ! case class ProductListView( id: Int, name: String, available: Boolean)
  42. 42. Play application code Learn how to write custom components that work as if they were built-in: template formatters database column types body parsers - custom request body types writeables - custom HTTP response types @PeterHilton • 46
  43. 43. // Code example: custom Play framework template formatters ! package templates ! object Formatters { ! // e.g. @updated.format in a template outputs ‘2013-­‐10-­‐25’ implicit class RichLocalDate(date: org.joda.time.LocalDate) { def format(): String = format("yyyy-­‐MM-­‐dd") def format(pattern: String) = date.toString(pattern) } ! // e.g. @price.format in a tempate outputs ‘EUR 2.99’ implicit class RichMoney(m: org.joda.money.Money) { import play.api.templates.Html def format = Html("${m.getCurrencyUnit.getCode}&nbsp;${m.getAmount}") }
  44. 44. Third-party libraries There is no Father Christmas Hibernate. Seriously. ORM isn’t a Scala thing Learn the alternatives Slick is good but not particularly easy NoSQL persistence may be easier to use MongoDB, Neo4J, ElasticSearch, etc @PeterHilton • 48
  45. 45. // Code example: Scala Slick database access ! // Nice: select * from LICENSE where id=? val query = Query(Licenses).filter(_.id === id)
  46. 46. // 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.?)
  47. 47. Lesson 4: Dev/prod environments
  48. 48. Deployment infrastructure Move on from application servers Deploy to production before coding Cloud deployment for test/prod is easy Cloudbees, Heroku Databases are commodity cloud resources especially MySQL - Amazon RDS @PeterHilton • 51
  49. 49. Development environment set-up example 1. Install dependencies: Java JDK 1.7, git, Play 2.1.3, MySQL 5.6 2. Clone GitHub repository https://github.com/HappyMelly/teller 3. Create MySQL database and user ! 4. Start application Play Evolutions SQL script initialises DB @PeterHilton • 52
  50. 50. CloudBees deployment process example 1. Source code in GitHub repository 2. Develop code in feature branches 3. Merge commits to master after review 4. Automatic test deploy on push to GitHub 5. Smoke test on test server with prod data 6. Use Jenkins to promote the build to prod @PeterHilton • 53
  51. 51. Deployment - dev environment Set-up your development environment as if you were doing an open-source project: Minimal steps to get started on a fresh machine, Idiot-proof instructions @PeterHilton • 54
  52. 52. Deployment - prod environment Set-up your production environment as if you were deploying to the cloud: Match the development environment, Support continuous deployment, Automate deployment from the version control system @PeterHilton • 55
  53. 53. http://12factor.net
  54. 54. 12 factors for cloud deployment @PeterHilton • 57 1. Codebase 2. Dependencies 3. Configuration 4. Backing services 5. Build/release/run 6. Processes 7. Port binding 8. Concurrency 9. Disposability 10. Dev/prod parity 11. Logs 12. Admin processes http://12factor.net
  55. 55. Lesson 5: Project risk
  56. 56. Doing it the easy way Become Scala experts: Type system, compiler errors, OO vs FP ! Become Play experts: Try out most of Play’s features Learn how Play works internally Build ten experimental applications @PeterHilton • 59
  57. 57. Doing it the hard way The same as the easy way, but without a year before the first real project ! Many new things at the same time is hard: uncertainty - especially unknown duration, risk - rework and operational issues @PeterHilton • 60
  58. 58. Starting a project Enable high-productivity: Have very fast laptops - SSD, lots of RAM Match the team to the architecture: Keep both simple - at least to start with, Decide how to work with front-end devs, Have a Scala expert available Do experiments, or get help, to start faster @PeterHilton • 61
  59. 59. 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
  60. 60. Reducing project risk - summary Community events - talking to other teams e.g. London Scala Users’ Group Read the book - don’t learn the hard way ! Attend the training course to learn faster Hire an expert to reduce project risk @PeterHilton • 64
  61. 61. @PeterHilton http://hilton.org.uk/

×