-
Notifications
You must be signed in to change notification settings - Fork 1
Getting Started
core3 is based on Play 2.5.x and Akka 2.4.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
libraryDependencies += "com.interelgroup" %% "core3" % "1.0.0"
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 companion traits:
import core3.database
import core3.database.containers._
import core3.database._
import core3.utils.Time._
import core3.utils._
import play.api.libs.json._
import slick.jdbc.MySQLProfile.api._
import slick.jdbc.MySQLProfile.backend.DatabaseDef
import slick.jdbc.SQLActionBuilder
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
//with JSON support (CouchDB, Redis, Elasticsearch)
extends JSONContainerCompanion
//with Slick support (MariaDB)
with SlickContainerCompanionImpl[(/*... def ...*/)] {
private type ContainerTupleDef = (/*... def ...*/)
// ... implementation ...
}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.{JSONContainerCompanion, JSONConverter, core}
import core3.database.dals.json.Redis
import core3.database.dals.{Core, DatabaseAbstractionLayer}
import net.codingwell.scalaguice.ScalaModule
import play.api.{Environment, Mode}
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
class Module extends AbstractModule with ScalaModule {
...
@Provides
@Singleton
def provideDB(
environment: Environment
)(
implicit system: ActorSystem, ec: ExecutionContext
): DatabaseAbstractionLayer = {
//creates the JSON container companion descriptor
val storeCompanions = Map[ContainerType, JSONContainerCompanion](
"Group" -> core.Group,
"TransactionLog" -> core.TransactionLog,
"LocalUser" -> core.LocalUser
)
//ensures that the JSON converted is
//properly initialized on Play reloads (in dev)
environment.mode match {
case Mode.Dev =>
if (!JSONConverter.isInitialized)
JSONConverter.initialize(storeCompanions)
case _ =>
JSONConverter.initialize(storeCompanions)
}
val storeConfig = StaticConfig.get.getConfig("database.redis")
implicit val timeout = Timeout(
StaticConfig.get.getInt("database.requestTimeout").seconds
)
//initializes the Redis DAL
val storeActor = system.actorOf(
Redis.props(
storeConfig.getString("hostname"),
storeConfig.getInt("port"),
storeConfig.getString("secret"),
storeConfig.getInt("connectionTimeout"),
storeCompanions,
storeConfig.getInt("databaseID"),
storeConfig.getInt("scanCount")
)
)
//builds the Core DAL
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 provideEngine(system: ActorSystem, db: DatabaseAbstractionLayer)
(implicit ec: ExecutionContext): WorkflowEngine = {
val workflows = Seq(
definitions.SystemCreateGroup,
definitions.SystemCreateLocalUser,
definitions.SystemDeleteGroup,
definitions.SystemDeleteLocalUser,
definitions.SystemQueryGroups,
definitions.SystemQueryLocalUsers,
definitions.SystemQueryTransactionLogs,
definitions.SystemUpdateGroup,
definitions.SystemUpdateLocalUserMetadata,
definitions.SystemUpdateLocalUserPassword,
definitions.SystemUpdateLocalUserPermissions
)
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")
)
)
)
)
}
...
}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