-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
core3 is based on Play 2.6.x and Akka 2.5.x, so some familiarity with them is essential (especially Play). Here are some resources that can help you get started:
- Scala Play Documentation
- New application (Play)
- Build overview (Play)
- Scala Akka Documentation
- Scala Best Practices
Before writing anything it might be a good idea to get to decide on your application's structure. Also, there are two example projects that can be used as a more hands-on starting point:
- Backend service - Example Engine
- Frontend service - Example UI
- Combined backend & frontend service - Example Engine + UI
libraryDependencies += "com.interelgroup" %% "core3" % "2.1.0"
Released for Scala 2.11 and 2.12
See Additional Dependencies for other libraries you may need.
You don't have to use containers if your application does not need to store anything or if persistence is handled in a different way.
The basic approach is to define all the fields, decide on the supported DALs and implement the appropriate container definition traits:
import core3.database
import core3.database.containers._
import core3.database._
import core3.utils.Time._
import core3.utils._
import play.api.libs.json._
case class TestContainer(
test: String,
created: Timestamp,
var updated: Timestamp,
var updatedBy: String,
id: ObjectID,
var revision: RevisionID,
var revisionNumber: RevisionSequenceNumber)
extends MutableContainer {
override val objectType: ContainerType = "TestContainer"
}
object TestContainer {
trait BasicDefinition extends BasicContainerDefinition {
override def getDatabaseName: String = "test-container"
override def matchCustomQuery(
queryName: String,
queryParams: Map[String, String],
container: Container):
Boolean = ???
}
//with Json support
trait JsonDefinition extends JsonContainerDefinition {
override def toJsonData(container: Container): JsValue = {
Json.toJson(container.asInstanceOf[Group])
}
override def fromJsonData(data: JsValue): Container = {
data.as[Group]
}
}
}Warning: Each container must have a unique
objectTypefor your application! This means that defining containers calledTransactionLog,LocalUserorGroupcan cause issues. You can go around that by not using transaction logs, not using local auth or not using groups.
Check out the containers page for more details.
You don't have to use workflows if they don't make sense for your application or if they are too restrictive; the data layer can be used directly through the
DatabaseAbstractionLayerinterface (seeProvide DB).
The easiest way to go about it is to define a list of actions that your users will be performing (for example: create project, update article, lock user, etc) and create a workflow per action. For each workflow you will need to define and implement the following:
- Workflow parameters (if any) - the parameters supplied by the user (or some other external entity)
- Input data (if any) - the data retrieved from the DB
- Action - the actual work to be done
Check out the workflows page for more details.
In your application's Module.scala (example with Redis):
import akka.actor.ActorSystem
import akka.util.Timeout
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.config.StaticConfig
import core3.database._
import core3.database.containers.{BasicContainerDefinition, JsonContainerDefinition, core}
import core3.database.dals.json.Redis
import core3.database.dals.{Core, DatabaseAbstractionLayer}
import core3.workflows._
import net.codingwell.scalaguice.ScalaModule
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
class Module extends AbstractModule with ScalaModule {
...
type DefsMap = Map[ContainerType, BasicContainerDefinition with JsonContainerDefinition]
@Provides
@Singleton
def provideContainerDefinitions(): DefsMap = {
val groupDefinitions =
new core.Group.BasicDefinition
with core.Group.JsonDefinition
val transactionLogDefinitions =
new core.TransactionLog.BasicDefinition
with core.TransactionLog.JsonDefinition
val localUserDefinitions =
new core.LocalUser.BasicDefinition
with core.LocalUser.JsonDefinition
Map(
"Group" -> groupDefinitions,
"TransactionLog" -> transactionLogDefinitions,
"LocalUser" -> localUserDefinitions
)
}
@Provides
@Singleton
def provideDB(definitions: DefsMap)
(implicit system: ActorSystem, ec: ExecutionContext): DatabaseAbstractionLayer = {
val storeConfig = StaticConfig.get.getConfig("database.redis")
implicit val timeout = Timeout(StaticConfig.get.getInt("database.requestTimeout").seconds)
val storeActor = system.actorOf(
Redis.props(
storeConfig.getString("hostname"),
storeConfig.getInt("port"),
storeConfig.getString("secret"),
storeConfig.getInt("connectionTimeout"),
definitions,
storeConfig.getInt("databaseID"),
storeConfig.getInt("scanCount")
)
)
new DatabaseAbstractionLayer(
system.actorOf(
Core.props(
Map(
"Group" -> Vector(storeActor),
"TransactionLog" -> Vector(storeActor),
"LocalUser" -> Vector(storeActor)
)
)
)
)
}
...
}Check out the databases page for more details and config.
In your application's Module.scala (example with all core workflows):
import akka.actor.ActorSystem
import akka.util.Timeout
import com.google.inject.{AbstractModule, Provides, Singleton}
import core3.config.StaticConfig
import core3.database.dals.DatabaseAbstractionLayer
import core3.workflows._
import net.codingwell.scalaguice.ScalaModule
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
class Module extends AbstractModule with ScalaModule {
...
@Provides
@Singleton
def provideWorkflows(): Vector[WorkflowBase] = {
Vector(
definitions.SystemCreateGroup,
definitions.SystemCreateLocalUser,
definitions.SystemDeleteGroup,
definitions.SystemDeleteLocalUser,
definitions.SystemQueryGroups,
definitions.SystemQueryLocalUsers,
definitions.SystemQueryTransactionLogs,
definitions.SystemUpdateGroup,
definitions.SystemUpdateLocalUserMetadata,
definitions.SystemUpdateLocalUserPassword,
definitions.SystemUpdateLocalUserPermissions
)
}
@Provides
@Singleton
def provideEngine(
system: ActorSystem,
workflows: Vector[WorkflowBase],
db: DatabaseAbstractionLayer,
definitions: DefsMap
)(implicit ec: ExecutionContext): WorkflowEngine = {
val engineConfig = StaticConfig.get.getConfig("engine")
implicit val timeout = Timeout(engineConfig.getInt("requestTimeout").seconds)
new WorkflowEngine(
system.actorOf(
WorkflowEngineComponent.props(
workflows,
db,
StoreTransactionLogs.fromString(engineConfig.getString("storeLogs")),
TransactionLogContent.WithParamsOnly,
TransactionLogContent.WithDataAndParams
)(ec, definitions)
)
)
}
...
}Check out the Workflow Engine page for more details and config.
You don't have to use controllers if communication and security is handled in a different way.
This will be the code that communicates with the outside world: renders pages, parses parameters, makes service requests, etc. You can start with the basics then move on to how to use and configure them.
Play's template engine is one way to go but this part is entirely up to you.
Home | Getting Started | Structure | Containers | Workflows | Controllers