Skip to content

Instantly share code, notes, and snippets.

@addybhardwaj
Last active December 15, 2015 17:59
Show Gist options
  • Save addybhardwaj/5300589 to your computer and use it in GitHub Desktop.
Save addybhardwaj/5300589 to your computer and use it in GitHub Desktop.
How to beat spring dependency injection in Scala natively. Following Gist illustrates Scala cake pattern for repositories and services design used by tiered architectures in Java world especially. It uses inheritance and generics. Gist also provides how these services can be accessed based on environment i.e. dev/prod/test in Play framework para…
/**
* Following Gist provides
*
* - BaseRepository,
* - Respositories Trait/Implementation,
* - BaseService,
* - Services Traits/Implementations
* - Config to tie services and repositories
* - Registry to add environment specific config like spring profiles (this is for Play framework)
*/
/**
* Common repository functions.
*
* @tparam A
*/
trait BaseRepository[A] {
def _save(instance: A): A
def _getById(id: String): A
}
/**
* Defines venue repository contract.
*/
trait VenueRepositoryComponent {
val venueRepository: VenueRepository
trait VenueRepository extends BaseRepository[Venue] {
def _findVenuesBy(geoLocation: GeoLocation, radius: Int): Seq[Venue]
}
}
/**
* Defines player repository contract.
*/
trait PlayerRepositoryComponent {
val playerRepository: PlayerRepository
trait PlayerRepository extends BaseRepository[Player] {
def _findPlayersBy(venue: Venue): Seq[Player]
}
}
/**
* Mocked implementation of repository
*/
trait MockedVenueRepositoryComponent extends VenueRepositoryComponent {
val venueRepository = new MockedVenueRepository
class MockedVenueRepository extends VenueRepository {
def _save(instance: Venue) = ???
def _getById(id: String) = ???
def _findVenuesBy(geoLocation: GeoLocation, radius: Int) = ???
}
}
/**
* Mocked Player implementation
*/
trait MockedPlayerRepositoryComponent extends PlayerRepositoryComponent {
val playerRepository = new MockedPlayerRepository
class MockedPlayerRepository extends PlayerRepository {
def _save(instance: Player) = ???
def _getById(id: String) = ???
def _findPlayersBy(geoLocation: GeoLocation, radius: Int) = ???
}
}
/**
* Common service implementations which are based on BaseRepository
*/
trait BaseService[A] {
def baseRepository: BaseRepository[A]
def save(instance: A): A = {
baseRepository._save(instance)
}
def getById(id: String): A = {
baseRepository._getById(id)
}
}
trait VenueServiceComponent {
this: VenueRepositoryComponent =>
val venueService: VenueService
trait VenueService extends BaseService[Venue] {
def baseRepository = venueRepository
def findVenuesBy(geoLocation: GeoLocation, radius: Int): Seq[Venue] = {
venueRepository._findVenuesBy(geoLocation, radius)
}
}
}
trait PlayerServiceComponent {
this: PlayerRepositoryComponent =>
val playerService: PlayerService
trait PlayerService extends BaseService[Player] {
def baseRepository = playerRepository
def findPlayersBy(venue: Venue): Seq[Player] = {
playerRepository._findPlayersBy(venue)
}
}
trait VenueServiceComponentImpl extends VenueServiceComponent {
this : VenueRepositoryComponent =>
val venueService = new VenueServiceImpl
class VenueServiceImpl extends VenueService {}
}
trait PlayerServiceComponentImpl extends PlayerServiceComponent {
this : PlayerRepositoryComponent =>
val playerService = new PlayerServiceImpl
class PlayerServiceImpl extends PlayerService {}
}
/**
* Config which ties the pieces together
*/
trait ServiceConfigurations {
val venueService: VenueServiceComponent#VenueService
val playerService: PlayerServiceComponent#PlayerService
}
object MockServiceConfigurations
extends ServiceConfigurations
with VenueServiceComponentImpl
with PlayerServiceComponentImpl
with MockedVenueRepositoryComponent
with MockedPlayerRepositoryComponent {
}
/**
* Following is play specific config which takes environment into account. Just like spring profiles
*/
import play.api.Play._
import play.api.{Mode, Play}
object Registry {
def envConfig[A](dev: A, test: A, prod : A) : A = {
Play.application.mode match {
case Mode.Dev => dev
case Mode.Test => test
case Mode.Prod => prod
}
}
val serviceConfig : ServiceConfigurations = envConfig(MockServiceConfigurations, ???, ???)
}
/**
* ServiceRegistry can be used to get access to correct implementation of the services
*/
trait ServiceRegistry {
val venueService = Registry.serviceConfig.venueService
val playerService = Registry.serviceConfig.playerService
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment