Skip to main content
ElastiCORE 3.0 is a release candidate: 3.0.261022-rc1 on Maven Central. Projects on the 2.2 line: see the 2.2 documentation.
Version: 3.0

Your first project

Five steps from a Spring Boot project to a running API with access checks and validation. The files are the ones in the ElastiCORE README; the agent kit's CI builds this project (plus test dependencies) with the newest release every week.

① build.gradle​

Use the settings.gradle and build.gradle of Installation - architecture = 'hexagonal', language = 'java'.

② The blueprint: src/main/resources/blueprint/shop/​

A domain is a folder under blueprint/. It holds its settings (env.yml) and its model (model/*.yml).

src/main/resources/blueprint/shop/env.yml
elasticore:
enable: true
config:
domainName: shop
framework: springboot
mode: jpa
j2ee: jakarta
apiPath: "/api" # REST paths: /api/shop/...
namespace:
base: com.acme.shop # the whole package layout from this one line
src/main/resources/blueprint/shop/model/entity.yml
entity:
Product:
meta: entity @root @editable @expose(20) @access(read=public) @sort(name+) -- product
fields:
productId: long @id @sequence
name: string(100)! @search(like)
price: money(10.2)! @min(0.01, 'price must be positive')

③ Generate​

./gradlew elcore3

The code appears under src/main/java/com/acme/shop/:

domain/model/Product.java               JPA entity
application/ProductCoreService.java CRUD, search, access checks (regenerated)
application/ProductService.java where your code goes (generated once, yours afterwards)
application/dto/ProductDTO.java request and response (+ 400 validation)
application/dto/ProductSrchDTO.java search: name LIKE, paging, sorting (by name unless asked otherwise)
adapter/in/web/ProductController.java REST API + OpenAPI docs (generated once too)
domain/repository/... repository port
infrastructure/persistence/... its JPA adapter

④ Your part - what the model does not say​

  • An application class: @SpringBootApplication in com.acme.shop or a package above it, so that Spring finds the generated classes. Spring Initializr writes one in the package of its group and artifact (com.acme and shop give com.acme.shop).

    src/main/java/com/acme/shop/ShopApplication.java
    package com.acme.shop;

    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;

    @SpringBootApplication
    public class ShopApplication {
    public static void main(String[] args) {
    SpringApplication.run(ShopApplication.class, args);
    }
    }
  • Your business logic, in ProductService.java. Like the controller it is generated once: it follows the model until you edit it, and from then on generating again leaves it alone (SKIP(user-owned) in the log). See Your logic in the owned service.

  • Signing in. Changing a product needs the ADMIN role (@access(read=public): anyone reads, everything else is for the administrator). Either put @signin on an entity with @login and @password and the sign-in API is generated (Access and sign-in), or call SessionCurrentUser.signIn(request, userId, "ADMIN") from a sign-in of your own (package io.elasticore.springboot3.security, on Boot 4 too); with Spring Security, register a CurrentUser bean instead.

⑤ Run​

./gradlew bootRun

Open http://localhost:8080/swagger-ui.html: every operation the model exposes can be called from there. For Product, under /api/shop/product:

RequestDoes
POST / with the fieldscreate; answers the row with its key
GET /{productId}read one; 404 when there is none
POST /search with {} or conditionsa page (content, totalElements, ...) - there is no GET for a list
PUT / with the key in the bodyupdate (the entity is @editable)
DELETE /{productId}delete

What the rules answer:

POST /api/shop/product/search, GET /api/shop/product/{id}   → 200 for anyone
POST /api/shop/product (not signed in) → 401 "Sign in to create Product"
POST /api/shop/product with price 0 → 400 "price must be positive"

Validation comes before the access check: even without signing in, a price of 0 gets the 400.

From here on a change starts in the model: ./gradlew build and bootRun run elcore3 before compiling, so the code never lags behind the model.

Build commands​

CommandWhat it does
./gradlew elcore3Generates from the DSL with the 3.0 rule engine (Java or Kotlin, per language)
./gradlew elcore3tsWrites the TypeScript client to src/main/ts (TypeScript)
./gradlew compileJava / build / bootRunGenerate first, then compile (unless the block sets generateBeforeCompile = false)
./gradlew elcore3 -Pdomain=shopGenerates one domain only
./gradlew elcore3 -PlistRulesLists the rules selected for each domain, per file kind, in order - writes nothing
./gradlew elcore3 -PforceOverwrites generated files even when they were edited by hand - a decision, not a habit
./gradlew elcoreThe 2.x generator (projects without an elasticore { } architecture, language or rules)

typescriptClient = true in the elasticore { } block makes the build run elcore3ts after elcore3 and before compiling.

Read the whole output

The rule engine prints one line per file and a summary such as === elcore3: 20 written / 0 unchanged / 2 skipped / 0 failed ===. Problems in the model are listed under [elcore3] DSL validation warnings: and the build still succeeds - fix each one in the YAML. A YAML file that does not parse (* LOAD ERROR: <file>) stops the build from 3.0.261022-rc1 on: [elcore3] the model check failed.

Next: the project layout and who owns which file.