Getting Started with ZenWave SDK
Section titled “Getting Started with ZenWave SDK”Generate your first Spring Boot service from a ZDL model
What you will build
Section titled “What you will build”You will write one small ZDL model and generate a Spring Boot service from it. The service exposes a REST API defined with OpenAPI, publishes domain events documented with AsyncAPI, and comes with unit and integration tests.
Prerequisites
Section titled “Prerequisites”- Java 17+
- Maven (or Gradle, see Maven & Gradle Support)
- JBang
We recommend SDKMAN! to install all of them.
Temurin (Eclipse) is a good open-source option for the Java JDK.
Install the CLI
Section titled “Install the CLI”Use JBang to install an evergreen self-updating CLI for the ZenWave SDK.
jbang alias add --force --fresh --name=zw release@zenwave360/zenwave-sdk
NOTE: You can install ZenWave versions from different channels: release for the latest stable version and snapshots for development versions. They can be installed side by side using different --name=<alias> values, or independently.
Use your own Spring Boot project
Section titled “Use your own Spring Boot project”ZenWave SDK generates business code and tests with no exotic dependencies. It works with any Spring Boot project.
You can start from a fresh project on start.spring.io or from a project you already have.
The simplest setup is a single-module Maven or Gradle project. This is a recommendation, not a requirement. Multi-module Maven projects also work, but you need to point each generator to the right module folder.
Start from a ZenWave archetype
Section titled “Start from a ZenWave archetype”For the fastest start, use the ZenWave archetypes. They are kept up to date and come with the OpenAPI Generator and ZenWave SDK Maven plugins already configured. You choose JPA or MongoDB, Kafka and Spring Modulith when you generate the project.
Java:
mvn archetype:generate \
-DarchetypeCatalog=internal \
-DarchetypeGroupId=io.zenwave360.archetypes \
-DarchetypeArtifactId=zenwave-java-archetype \
-DarchetypeVersion=RELEASE \
-DinteractiveMode=true
Kotlin (see Kotlin Backend Application):
mvn archetype:generate \
-DarchetypeCatalog=internal \
-DarchetypeGroupId=io.zenwave360.archetypes \
-DarchetypeArtifactId=zenwave-kotlin-archetype \
-DarchetypeVersion=RELEASE \
-DinteractiveMode=true
Maven asks for the project name, description, artifactId, groupId and package. Then it asks which features to include:
[FEATURE] Support for JPA? y
[FEATURE] Support for MONGODB? n
[FEATURE] Support for KAFKA? n
[FEATURE] Support for MODULITH? y
Choose exactly one of JPA or MongoDB. Modulith implies Kafka. See the Java archetype and Kotlin archetype READMEs for all options.
The generated project already contains a sample zenwave-model.zdl and a zenwave-scripts.zw with the plugin configuration.
Write your first model
Section titled “Write your first model”Create a zenwave-model.zdl file in the project root. This model has one aggregate, one service exposed as a REST API and one domain event. The plugins block in config lists the SDK plugins you will run in the next section.
config {
title "ZenWave Customer JPA Example"
basePackage "io.zenwave360.example"
persistence jpa
databaseType postgresql
layout CleanHexagonalProjectLayout
// these should match the values of openapi-generator-maven-plugin
// used by the OpenAPIControllersPlugin and SpringWebTestClientPlugin
openApiApiPackage "{{basePackage}}.adapters.web"
openApiModelPackage "{{basePackage}}.adapters.web.model"
openApiModelNameSuffix DTO
plugins {
ZDLToOpenAPIPlugin {
idType integer
idTypeFormat int64
targetFile "src/main/resources/public/apis/openapi.yml"
}
ZDLToAsyncAPIPlugin {
asyncapiVersion v3
idType integer
idTypeFormat int64
targetFile "src/main/resources/public/apis/asyncapi.yml"
}
BackendApplicationDefaultPlugin {
includeEmitEventsImplementation true
}
OpenAPIControllersPlugin {
openapiFile "src/main/resources/public/apis/openapi.yml"
}
}
}
/**
* Customer entity
*/
@aggregate
@auditing // adds auditing fields to the entity
entity Customer {
name String required maxlength(254) /** Customer name */
email String required maxlength(254)
}
@rest("/customers")
service CustomerService for (Customer) {
@post
createCustomer(Customer) Customer withEvents CustomerEvent
@get("/{id}")
getCustomer(id) Customer?
}
@copy(Customer)
@asyncapi({ channel: "CustomersChannel", topic: "customers" })
event CustomerEvent {
id Long required
version Integer
}
persistenceselectsjpaormongodb.layoutsets the package structure of the generated code.@rest,@postand@getdescribe how the service is exposed as a REST API.withEventsmakescreateCustomerpublishCustomerEvent.@asyncapidocuments it in AsyncAPI.
The full syntax is in ZDL Domain Language. A larger version of this model is the Customer Address JPA example.
Generate
Section titled “Generate”Run every command from the project root.
1. Generate OpenAPI and AsyncAPI from the ZDL
Section titled “1. Generate OpenAPI and AsyncAPI from the ZDL”jbang zw -p io.zenwave360.sdk.plugins.ZDLToOpenAPIPlugin \
specFile=zenwave-model.zdl \
idType=integer \
idTypeFormat=int64 \
targetFile=src/main/resources/public/apis/openapi.yml
jbang zw -p io.zenwave360.sdk.plugins.ZDLToAsyncAPIPlugin \
specFile=zenwave-model.zdl \
idType=integer \
idTypeFormat=int64 \
targetFile=src/main/resources/public/apis/asyncapi.yml
These API definitions are the source of truth for your inbound and outbound communications. You can edit them before the next step.
2. Configure the API-first generators in pom.xml
Section titled “2. Configure the API-first generators in pom.xml”If you started from a ZenWave archetype, skip this step. Both generators are already configured.
Otherwise, add openapi-generator-maven-plugin to generate Spring MVC interfaces and DTOs from openapi.yml. Its packages must match openApiApiPackage and openApiModelPackage in your ZDL:
openapi-generator-maven-plugin in pom.xml
<plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <version>${openapi-generator-maven-plugin.version}</version> <executions> <execution> <goals> <goal>generate</goal> </goals> <phase>generate-sources</phase> <configuration> <inputSpec>${project.basedir}/src/main/resources/public/apis/openapi.yml</inputSpec> <skipIfSpecIsUnchanged>true</skipIfSpecIsUnchanged> <generatorName>spring</generatorName> <apiPackage>${openApiApiPackage}</apiPackage> <modelPackage>${openApiModelPackage}</modelPackage> <modelNameSuffix>DTO</modelNameSuffix> <addCompileSourceRoot>true</addCompileSourceRoot> <generateSupportingFiles>false</generateSupportingFiles> <typeMappings> <typeMapping>Double=java.math.BigDecimal</typeMapping> </typeMappings> <configOptions> <useSpringBoot3>true</useSpringBoot3> <documentationProvider>none</documentationProvider> <openApiNullable>false</openApiNullable> <useOptional>false</useOptional> <useTags>true</useTags> <interfaceOnly>true</interfaceOnly> <skipDefaultInterface>true</skipDefaultInterface> <delegatePattern>false</delegatePattern> <sortParamsByRequiredFlag>false</sortParamsByRequiredFlag> </configOptions> </configuration> </execution> </executions> </plugin>Then add zenwave-sdk-maven-plugin with the AsyncAPIGenerator to generate event producers and DTOs from asyncapi.yml. Follow AsyncAPI Generator: Maven Usage. For Gradle, see Maven & Gradle Support.
3. Generate the backend code and tests
Section titled “3. Generate the backend code and tests”Generate the domain entities, services, repositories and their tests:
jbang zw -p BackendApplicationDefaultPlugin \
zdlFile=zenwave-model.zdl \
includeEmitEventsImplementation=true \
targetFolder=.
Generate the Spring MVC controllers that implement the OpenAPI interfaces:
jbang zw -p io.zenwave360.sdk.plugins.OpenAPIControllersPlugin \
openapiFile=src/main/resources/public/apis/openapi.yml \
zdlFile=zenwave-model.zdl \
targetFolder=.
To see all options of a plugin, run it with --help:
jbang zw -p io.zenwave360.sdk.plugins.BackendApplicationDefaultPlugin --help
Run it
Section titled “Run it”Run the tests:
mvn test
Start the infrastructure and the application. Projects created from the archetypes include a docker-compose.yml:
docker-compose up -d
mvn spring-boot:run
Open SwaggerUI at http://localhost:8080/swagger-ui/index.html.
What you got
Section titled “What you got”With CleanHexagonalProjectLayout, the generated code is organized under your basePackage:
core.domain: theCustomerentity.core.inbound: theCustomerServiceinterface and its DTOs.core.outbound: theCustomerRepositorySpring Data interface.core.implementation:CustomerServiceImpland its mappers. It saves the entity and publishesCustomerEvent.adapters.web:CustomerApiController, implementing the interface generated fromopenapi.yml.src/test:CustomerServiceTestwith an in-memory repository, repository integration tests with Testcontainers, and controller tests.
OpenAPI interfaces, AsyncAPI producers and their DTOs are generated by the Maven build into target/generated-sources.
Generated service implementations, mappers and tests are a starting point. Add your business logic and test data there. To change layouts or templates, see Customizing Generated Code.
Next steps
Section titled “Next steps”Iterative, AI-Assisted Development
Section titled “Iterative, AI-Assisted Development”Learn how to evolve your models, generated code and AI-implemented business logic together: Iterative, AI-Assisted Model-Driven Development with ZenWave SDK.
ZenWave Editor for IntelliJ
Section titled “ZenWave Editor for IntelliJ”The IntelliJ plugin is optional. It adds editing support for .zdl and .zw files and runs each plugin from a play button.
For rendering class diagrams in markdown preview, install the PlantUML Integration plugin.
NOTE: ZenWave Editor uses the jbang zw command to execute plugins, so you need JBang available in the PATH of the console configured by default in IntelliJ.
Examples
Section titled “Examples”Tutorial
Section titled “Tutorial”DDD In Practice: From Ubiquitous Language DSL to Complete Spring Boot Kotlin Project