scalaintermediate
Dependency Injection Patterns
Implement dependency injection in Scala: cake pattern, constructor injection, and ZIO ZLayer.
scalaPress ⌘/Ctrl + Shift + C to copy
// Pattern 1: Constructor injection (simplest)
trait UserRepository:
def findById(id: Long): Option[String]
def save(name: String): Long
trait EmailService:
def send(to: String, subject: String, body: String): Unit
class InMemoryUserRepo extends UserRepository:
private var users = Map(1L -> "Alice", 2L -> "Bob")
private var nextId = 3L
def findById(id: Long): Option[String] = users.get(id)
def save(name: String): Long =
val id = nextId; nextId += 1
users += (id -> name); id
class ConsoleEmailService extends EmailService:
def send(to: String, subject: String, body: String): Unit =
println(s" Email to $to: $subject")
// Service with constructor-injected dependencies
class UserService(repo: UserRepository, email: EmailService):
def register(name: String): Long =
val id = repo.save(name)
email.send(name, "Welcome", s"Hello $name, your ID is $id")
id
def getUser(id: Long): Option[String] = repo.findById(id)
// Pattern 2: Cake pattern (self-types)
trait UserRepositoryComponent:
val userRepository: UserRepository
trait EmailServiceComponent:
val emailService: EmailService
trait UserServiceComponent:
self: UserRepositoryComponent with EmailServiceComponent =>
lazy val userService = new UserService(userRepository, emailService)
// Wiring
object ProductionEnvironment
extends UserServiceComponent
with UserRepositoryComponent
with EmailServiceComponent:
val userRepository: UserRepository = InMemoryUserRepo()
val emailService: EmailService = ConsoleEmailService()
// Pattern 3: Simple reader/context
trait AppContext:
def userRepo: UserRepository
def emailSvc: EmailService
def registerUser(name: String)(using ctx: AppContext): Long =
val id = ctx.userRepo.save(name)
ctx.emailSvc.send(name, "Welcome", s"Your ID: $id")
id
def getUser(id: Long)(using ctx: AppContext): Option[String] =
ctx.userRepo.findById(id)
@main def run(): Unit =
// Constructor injection
println("=== Constructor Injection ===")
val repo = InMemoryUserRepo()
val email = ConsoleEmailService()
val service = UserService(repo, email)
val id = service.register("Carol")
println(s"Registered: ${service.getUser(id)}")
// Cake pattern
println("\n=== Cake Pattern ===")
val id2 = ProductionEnvironment.userService.register("Dave")
println(s"Registered: ${ProductionEnvironment.userService.getUser(id2)}")
// Context/Reader
println("\n=== Context Pattern ===")
given AppContext with
def userRepo = InMemoryUserRepo()
def emailSvc = ConsoleEmailService()
val id3 = registerUser("Eve")
println(s"Registered: ${getUser(id3)}")Use Cases
- Decoupled service architecture
- Testable component design
- Modular application wiring
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
typescriptadvanced
Dependency Injection Container
Build a lightweight DI container with singleton and transient scopes for testable Node.js applications.
Best for: Testable application architecture
#nodejs#dependency-injection
kotlinintermediate
Dependency Injection with Koin
Set up Koin DI: modules, scoped instances, factory vs single, and parameterized injection.
Best for: Application dependency management
#kotlin#koin
scalabeginner
Scala Hello World Application
Create a basic Scala application with main method, string interpolation, and val/var basics.
Best for: Getting started with Scala
#scala#basics
scalabeginner
Pattern Matching Fundamentals
Use Scala pattern matching with guards, type patterns, tuple patterns, and nested extractors.
Best for: Control flow with pattern matching
#scala#pattern-matching