Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
54: An Overview of the Play Framework
Think of the Play Framework like a well-run professional kitchen. When a customer walks in, they don't just wander into the pantry and start grabbing ingredients. There's a system. First, they look at a menu to see what's available. Then, they tell a waiter what they want. The waiter doesn't cook the food; they just take the order and tell the chef exactly what needs to happen. Finally, the food is plated beautifully before it hits the table. If the waiter tried to cook, or the chef tried to take orders, the whole place would crash during the dinner rush.
In Play, this is exactly how your web requests are handled. The "menu" is your routes file. The "waiter" is the Controller. The "chef" is your service layer or model where the actual Scala logic lives. And the "plating" is the Twirl template engine that turns your data into HTML.
The Traffic Cop: The Routes File
Unlike many frameworks where you scatter annotations like @Get("/home") all over your classes, Play uses a centralized routes file. I personally find this much cleaner because I can see the entire map of my application in one glance without hunting through twenty different files.
GET /books/:isbn controllers.BookController.details(isbn: String)
In this line, we're telling Play: "If a GET request comes in for /books/ followed by some text, hand that text over to the details method in the BookController." It's a direct mapping that keeps your controllers focused on logic rather than configuration.
Handling the Request in the Controller
The Controller is where you'll spend a lot of your time. Its only job is to receive the request, call the right service, and return a result. Since we're in Scala, we aren't blocking threads. We use Action and Future to make sure our app can handle thousands of concurrent users without freezing up.
Let's look at a specific example. Imagine we're building a digital library. Here is how a basic controller action looks:
class BookController @Inject()(cc: ControllerComponents, bookService: BookService) extends AbstractController(cc) {
def details(isbn: String) = Action.async {
bookService.findBookByIsbn(isbn).map {
case Some(book) => Ok(views.html.bookDetail(book))
case None => NotFound(views.html.errorPage("Book not found"))
}
}
}
Notice the @Inject()? Play uses Dependency Injection heavily. I don't manually instantiate BookService; I just tell Play I need it, and the framework provides it. Also, notice the Action.async. We're returning a Future[Result], which means the server can go do other things while the database is searching for that ISBN.
Plating the Data with Twirl
Now, you can't just send a Scala Case Class to a browser; the browser only speaks HTML. This is where Twirl comes in. Twirl templates are actually compiled into Scala code, which means if you make a typo in your template, the compiler will catch it before you even run the app. This is a massive advantage over frameworks that use plain text templates (like JSP or Thymeleaf) where you only find out something is broken when the page crashes in production.
A Twirl template (bookDetail.scala.html) looks like this:
@(book: Book)
@book.title
Author: @book.author
ISBN: @book.isbn
Back to Library
The @ symbol is the magic switch. Everything else is standard HTML; anything starting with @ is executed as Scala code. It's a tight, type-safe loop from the route to the controller and finally to the view.
📋 Practical Task
Build a "User Profile" Route and View
You are tasked with adding a user profile feature to an existing Play application. You need to create the plumbing that allows a user to be looked up by their username.
Requirements:
- The Route: Add a route to the
conf/routesfile that accepts a GET request at/user/:usernameand maps it to a method calledprofileinUserController. - The Controller: In the
UserController, implement theprofilemethod. It should call a mock serviceuserService.getUser(username)(which returns aFuture[Option[User]]).- If the user exists, return an
Okresponse rendering a template calleduserProfile.scala.html. - If the user does not exist, return a
NotFoundresponse.
- If the user exists, return an
- The Template: Create a basic Twirl template
userProfile.scala.htmlthat takes aUserobject and displays the user'sdisplayNameandemailinside<h2>and<p>tags respectively.
There are no comments for now.