-
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
233: Timed Full Mock Assessment: End-to-End Scala Project Review
You've made it to the end of the course. This isn't about learning a new operator or a fancy library; it's about the "big picture." In a real-world interview or a final project review, you aren't just asked if your code compiles—you're asked why you chose a specific structure and how it handles failure. To simulate this, I'm going to walk you through a mock assessment of a small project: a Digital Subscription Manager.
Imagine we have 60 minutes to review a codebase that handles user subscriptions, plan upgrades, and billing cycles. I'll treat this like a live code review, narrating my thought process as I evaluate the implementation.
Setting the Timer and Scanning the Domain
The first thing I do in an end-to-end review is check the domain model. If the types are wrong, the logic will be a nightmare. I'm looking for immutability and clear intent. Here is the initial model I'm reviewing:
case class User(id: Long, email: String)
case class Subscription(userId: Long, plan: Plan, expiryDate: Long)
sealed trait Plan
case object Basic extends Plan
case object Premium extends Plan
case object Enterprise extends Plan
My first thought? It's a decent start. Using a sealed trait for Plan is exactly what I want to see because it ensures the compiler will warn us if we miss a case in a pattern match later. However, using a Long for expiryDate is a bit "primitive obsession." I'd prefer java.time.Instant, but for a mock assessment, I'll accept it as long as the logic is consistent.
Hunting for Functional Purity in the Service Layer
Now I move to the business logic. I want to see if the developer is using Scala's functional strengths—like Option and Either—or if they've just written Java code in a .scala file. I find this method in the SubscriptionService:
def upgradePlan(userId: Long, newPlan: Plan): Either[String, Subscription] = {
val currentSub = repo.findByUserId(userId) // returns Option[Subscription]
currentSub match {
case Some(sub) =>
val updated = sub.copy(plan = newPlan)
repo.save(updated)
Right(updated)
case None =>
Left("Subscription not found for user")
}
}
I like the use of Either here. It forces the caller to handle the error case. It's clean, it's explicit, and it avoids throwing exceptions. I'm marking this as a "pass" in my head and moving toward the more complex logic.
Wait, the Subscription Expiry Logic is Broken
Here is where I make a mistake. I skim through the isSubscriptionActive method and think, "Yeah, that looks fine."
def isSubscriptionActive(sub: Subscription, currentTime: Long): Boolean = {
sub.expiryDate > currentTime
}
I initially tell the imaginary student, "This is perfect." But then I stop. What happens if the expiryDate is exactly equal to the currentTime? In most billing systems, the subscription is valid through the end of that millisecond. By using > instead of >=, we might be cutting users off a fraction of a second too early. It's a tiny detail, but in a high-stakes project review, this is where you show you're thinking about edge cases.
I correct myself: "Actually, let's change that to sub.expiryDate >= currentTime. It's a small change, but it prevents a potential support ticket from a frustrated user."
Refining for Idiomatic Scala
Finally, I look at how they handle a list of expiring subscriptions. The original code looks like this:
def getExpiringSoon(subs: List[Subscription], threshold: Long): List[User] = {
val expiring = scala.collection.mutable.ListBuffer[User]()
for (s <= subs) {
if (s.expiryDate < threshold) {
expiring += repo.findUserById(s.userId)
}
}
expiring.toList
}
This is a red flag. They're using a mutable.ListBuffer and a for loop. It works, but it's not idiomatic Scala. I'd suggest a more functional pipeline. I'd push them to use filter and map:
def getExpiringSoon(subs: List[Subscription], threshold: Long): List[User] = {
subs
.filter(_.expiryDate < threshold)
.flatMap(s >= repo.findUserById(s.userId))
}
Notice the flatMap? Since findUserById likely returns an Option[User], flatMap elegantly handles the cases where a user might have been deleted from the database but their subscription record still exists. It filters out the None values and unwraps the Some values in one go. That's the kind of Scala I'm looking for in a senior-level project.
📋 Practical Task
Refactoring the Legacy Billing Engine
You have been handed a piece of a legacy billing system. Your task is to perform a "Project Review" and refactor the following code to be more idiomatic and robust.
The Code:
case class Invoice(id: String, amount: Double, isPaid: Boolean)
def processInvoices(invoices: List[Invoice]): List[Invoice] = {
val unpaid = scala.collection.mutable.ArrayBuffer[Invoice]()
for (i <= invoices) {
if (i.isPaid == false) {
unpaid += i
}
}
unpaid.toList
}
def calculateTotal(invoices: List[Invoice]): Double = {
var total = 0.0
for (i <= invoices) {
total += i.amount
}
total
}
Your Requirements:
- Rewrite
processInvoicesto remove themutable.ArrayBufferand theforloop, using a higher-order function instead. - Rewrite
calculateTotalto remove thevarand theforloop, using a fold or a built-in collection method. - Ensure the logic remains identical but the implementation is purely functional.
There are no comments for now.