Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.

Slideshare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.

Composable and streamable Play apps

Web pages can get very complex and slow. In this talk, I share how we solve some of these problems at LinkedIn by leveraging composition and streaming in the Play Framework. This was my keynote for Ping Conference 2014 ( http://www.ping-conf.com/ ): the video is on ustream ( http://www.ustream.tv/recorded/42801129 ) and the sample code is on github ( https://github.com/brikis98/ping-play ).

Very cool stuff. With this setup it should also be possible to easily switch of javascript rendering for SEO purposes (by just rendering the content in the template directly instead of via javascript).

object ServiceClient { def makeServiceCall(serviceName:
String): Future[String] = { WS.url(s"http://localhost:9000/mock/$serviceName").get().map(_.body) } } app/data/ServiceClient.scala A simple client to make a remote call to the mock endpoint and return a Future with the body of the HTTP response

@(wvypCount: Int) <p class="wvyp-count"> <span
class="large-number">@wvypCount</span> <span>Your profile has been viewed by <b>@wvypCount</b> people in the past 3 days</span> </p> app/views/wvyp/wvypCount.scala.html @(searchCount: Int) <p class="search-count"> <span class="large-number">@searchCount</span> <span>Your have shown up in search results <b>@searchCount</b> times in the past 3 days</span> </p> app/views/wvyp/searchCount.scala.html The markup for the two partials that show the counts

Wins 1. Abstraction: we can
focus on just one small part of the page while safely ignoring all the rest. 2. Composition: we can build complicated pages by putting together simpler parts. We can get lots of reuse from common pieces. 3. Testing and iterating on a small, standalone unit is much easier and faster.

Caveats 1. Standalone modules may
be inefficient in terms of duplicated service calls. However, de-duping is straightforward. 2. Have to merge and dedupe static content.

Quick Review 1. An Enumerator
is a Producer. It pumps out chunks of data. 2. An Iteratee is a Consumer. It reactively consumes chunks of data. 3. An Enumeratee is an Adapter. You can attach them in front of Iteratees and Enumerators to filter the chunks of data.

object EnumeratorExample extends Controller {
def index = Action { Ok.chunked(/* We need an Enumerator here */) } } app/controllers/EnumeratorExamples.scala Play has an Ok.chunked method which can stream out the contents of an Enumerator

@(contents: Html, id: String) <script
type="text/html-stream" id="@id-contents"> @contents </script> <script type="text/javascript"> document.getElementById("@id").innerHTML = document.getElementById("@id-contents").innerHTML; </script> app/views/ui/pagelet.scala.html Create a “pagelet” template that will take a snippet of HTML and use JS to inject it into the proper spot on the page.

Wins 1. Much faster time
to first byte 2. Static content can start loading much earlier 3. User can see and start interacting with the page almost immediately 4. Out of order rendering with JavaScript allows even deeper optimizations

Caveats 1. Once you’ve streamed
out the HTTP headers, you can’t change them. This means cookies and error handling may have to be done client-side. 2. Have to be careful with “pop in” and redraws. Client side code should intelligently choose when to insert items into the DOM. 3. Need to handle CSS and JS dependencies differently 4. Testing is harder 5. May need to disable JavaScript rendering for SEO