This guide will lead you through setting up a simple Spine-based project with a Java backend and a JavaScript frontend. The project defines a domain model using Protobuf, which is then converted into Java for server usage and JavaScript for client usage.
Configuring a project with Gradle
Spine provides build-time tools for code generation and analysis. These tools are implemented as Gradle plugins — an extendable, uniform way to tap into a project build process.
The minimal Gradle configuration you will need to start a new project is:
plugins {
id("io.spine.tools.gradle.bootstrap").version("1.7.0")
}
Place the config into your root build.gradle
file and execute a Gradle build. This will apply
the Spine Bootstrap plugin to your project.
You can also find this declaration on the Gradle Plugin Portal, or on our Getting Started page.
Spine Bootstrap plugin
Spine Bootstrap plugin (Bootstrap for short) serves to automate the configuration of the modules in your Spine-based app.
We recommend having separate Gradle subprojects for domain model definition, server implementation, and client code. Bootstrap is applied a bit differently in each of these cases.
Model definitions
Let’s assume one of the subprojects contains Protobuf definitions for the domain model. This
subproject is typically called model
. In build.gradle
for that subproject, declare:
spine.assembleModel()
This way, the subproject will receive all the required Spine dependencies but no code will be generated from Protobuf. Instead, we generate code for specific languages in subprojects where that code is needed. By placing model definitions into a separate subproject, we allow them to be shared between other, language-specific subprojects.
Java server implementation
A separate Gradle subproject is dedicated to the server-side business logic of your Bounded Context.
Usually, this subproject is named server
or after the Bounded Context it implements, e.g. users
.
In build.gradle
for that subproject declare:
spine.enableJava().server()
dependencies {
protobuf(project(':model'))
}
This will add dependencies to the spine-server
artifact and set up code generation for
the domain model types. Also, use spine.enableJava().withDatastore()
to add the Google Cloud
Datastore-based storage implementation to the given subproject.
It is perfectly normal to have more Protobuf types in these modules, as long as those types are internal to your Java implementation and are not a part of the publicly-visible domain model.
For any specific subproject, you can configure to run or skip certain code generation routines. For example:
spine.enableJava {
server() // Enable Server API.
codegen {
protobuf = true // Generate default Protobuf Java classes.
spine = false // Avoid enhancing Protobuf Java classes with Spine validation, interfaces, etc.
grpc = true // Generate gRPC stubs and implementation bases from Protobuf service definitions.
}
}
Note the use of the protobuf
configuration. This tells our tools that the Protobuf definitions
in the subproject model
must be converted into Java code in the current subproject.
Alternatively, if, for instance, the upstream project already contains code generated from Protobuf,
and no additional codegen is required, the api
/implementation
configurations should be used. See
this Gradle doc
for more info.
Java web server
If your project contains a JavaScript frontend, you may declare a web-server
subproject, which
processes the HTTP requests from JS. In web-server/build.gradle
:
spine.enableJava().webServer()
dependencies {
implementation(project(':server'))
}
Using webServer()
has the same effect as just declaring the subproject to be a part of server()
and also adds the io.spine:spine-web
dependency to the subproject. This dependency provides
components for handling requests from a JavaScript frontend See also firebaseWebServer()
for using
a Firebase database to communicate between the server and the client.
JavaScript client
Finally, a JavaScript client is also one or more Gradle subprojects. In build.gradle
for those
subprojects, declare:
spine.enableJavaScript()
dependencies {
protobuf(project(':model'))
}
This configuration sets up JavaScript code generation from the model
definitions. Handle NPM
dependencies separately (e.g. adding the dependency for spine-web
).
Working with many Bounded Contexts
In a system with more than one Bounded Context, we recommend a similar project structure. Instead of
having a single model
subproject, you should form a subproject per Bounded Context, for example,
users-model
, trains-model
, billing-model
, etc. If one of the Bounded Contexts shares some
domain language with another, add a dependency between them. This way, the downstream context may
use Protobuf definitions of the upstream context.
dependencies {
implemetation(project(':model-users'))
}
For domain logic implementation, also use a single subproject per Bounded Context. The convention
for calling those projects by the context names: users
, trains
, billing
, etc. It is a good
idea to have a server implementation subproject depend only on one model subproject to preserve
language and responsibility boundaries.
If your server should be deployed as a whole, use a single web-server
for all the contexts. If you
would like to deploy different contexts separately, declare a specific web-server
subprojects
for each of those contexts. See this guide
on the principles of integrating separate Bounded Contexts and third-party systems in Spine.
Verbose configuration
If the Bootstrap configuration is not customizable enough for you, there are other Gradle plugins which may provide fine-grained API.
Those plugins are Spine Model Compiler for Java subprojects and Spine ProtoJs plugin for JavaScript submodules. Under the hood, Bootstrap uses those plugins to do the work. This means that Bootstrap automatically applies the correct low-level plugin for you.
Model Compiler
Spine Model Compiler is a Gradle plugin which executes all the code generation routines via several
Gradle tasks as well as the modelCompiler { }
extension, which allows you to configure those
tasks.
See the API reference for the list of the declared tasks and the codegen configuration options
ProtoJS Plugin
ProtoJs Gradle plugin manages and enhances JavaScript code generation from Protobuf definitions.
The plugin adds the generateJsonParsers
task, which appends generated JS files with code parsing
Protobuf messages out of plain JS objects.
The plugin also provides the protoJs { }
extension, which allows you to configure JS code
generation. See the API reference
for more info.