disclaimer: I am converting existing Java+spring webapp into scala+spring. So my design is still javaish and object oriented.
There are only few key components that I'll explain breifly here.
Application computes some data metrics in real time when request comes in. Core components are DataContext[DBObject] that holds some massaged data we pull from database, multiple instance of these are generated per each request based on how many measures(avg, percentile) contained in request; DataContextBuilder[DBOject] that has business logic to populate(massage) DataContext[DBObject], these are singleton objects (defined by spring annotations; I am will to change to scala singleton if I can). And then there are ViewWorkers[DBObject, IsDimensionLike] that does final individual metrics computation from data held in DataContext[DBObject].
Here are definitions of above components.
trait DBObject
trait AggDBObject extends DBObject
trait RawDBObject extends DBObject
class Dimensions1Agg extends AggDBObject
class Dimensions2Agg extends AggDBObject
class Dimensions1Raw extends RawDBObject
trait IDataContext[A <: DBObject] {
var XsummaryData: A = _
var XByDateData : Map[String, A] = _
}
class DataContext[A <: DBObject] extends IDataContext[A]{
def restrictAccess = {.. some impl ..}
}
trait IDataContextBuilder[A <: DBObject] {
def initDataPoints(dataContext: IDataContext[A]): Unit
}
class Dimension1AggContextBuilder extends IDataContextBuilder[Dimension1Agg] {
.. override method impl..
}
trait IViewWorker[A <: DBObject] {
def getData(
DataContextMap: Map[Int, IDataContext[A]]
): Map[Int, Map[Int, List[DataView]]] //DataView can be type parameter here!?
}
class AbstractViewWorker[A <: DBObject, C : IsDimensionLike] extends IViewWorker[A] {
abstract def formula[A](dbObject: A): C
def getData(DataContextMap: Map[Int, IDataContext[A]]) = {some common impl goes here. ..}
}
class Metrix1ViewWorker extends AbstractViewWorker[Dimension1Agg]
For all of above DBObject
I can create a specific DataContext
subtype that represent each but instead I want to use DataContext
itself typed with different DBObject
. DataContext is created at runtime based on some request parameter.
Here's the companion object with apply method:
object DataContext {
def apply(sId: Int, mId: Int): DataContext[_ <: DBObject] = {
(sId, mId) match {
case (1, 1) => {
new DataContext[Dimension1Agg]()
}
case (1, 2) => {
new DataContext[Dimension1Raw]()
}
}
}
}
Do I have right return type of apply method for object I am creating? If I remove it then I get some complex return type at call site "val dataContext: DataContext[_ >: Dimension1Agg with Dimension1Raw <: DBObject] with Product with Serializable { .. }"
Do you see any issue in general with above factory method?
Anyway, I get following error when passing that DataContext to DataContextBuilder
type mismatch; found : IDataContext[_$1] where type _$1 <: DBObject required: IDataContext[_$19]
is _$1 and _$19 because scalac can't determine types?
Here's DataContextBuilderFactory object
object IDataContextBuilder {
def apply(sId: Int, daId: Int): IDataContextBuilder[_ <: DBObject] = {
(sId, daId) match {
case (1, 1) => {
new Dimension1AggContextBuilder
}
case (1, 2) => {
new Dimension1RawContextBuilder
}
}
}
}
Call site it a DataService class that receive request params, orchestrate all above components and compose a response. I know its not functional but I am trying to do incremental refactoring. DataService check request params, passes that information to factory methods to create mutliple DataContext and DataContextBuilder and DataWorker; Call all the DataContextBuilder's initDataPoints methods, wait for them to finish; call all the Dataworker's generateView method, wait for them to finish, finally compose response.
EDIT 1:
What is a difference in following two approach for defining types or generics?
trait IDataContextBuilder[A <: DBObject] {
def initMetrixDataPoints(dataContext: IDataContext[A]): Unit
}
trait IDataContextBuilder {
def initMetrixDataPoints(dataContext: IDataContext[_ <: DBObject]): Unit
}