env.yml settings
Each domain folder src/main/resources/blueprint/<domain>/ has an env.yml.
elasticore:
enable: true
config:
domainName: petstore
framework: springboot
mode: jpa
j2ee: jakarta
apiPath: "/api" # REST paths: /api/petstore/...
table:
prefix: ps_
referenceColumn: short # references to other aggregates: category_id (default 2.x-style category_category_id)
annotations:
genid: "genid(io.elasticore.springboot3.util.IdUtils.newId)"
namespace:
base: io.elasticore.sample.petstore
config:
| Key | Means |
|---|---|
domainName | the domain's name - in packages, file names and API paths |
framework, mode, j2ee | springboot, jpa (also mongo, uml, dsl; comma-separated for several), jakarta |
apiPath | the base of the REST paths (default /api): /api/v1 gives /api/v1/<domain>/<entity> |
table.prefix | a prefix for table names (ps_ gives ps_purchase_order) |
table.autoJoin | true: JOIN FETCH every reference on findById (otherwise only @fetchjoin fields) |
tableSchema | the database schema of every table (an entity's @schema wins) |
annotations | named annotations for @env:name fields, and the genid generator |
testCode: "true" | JUnit 5 + Mockito service tests |
supportAI: "true" | Spring AI @Tool methods on the extension services (Spring Boot) |
jsonview | false (default), true, auto - Jackson @JsonView visibility |
publisher.rules, publisher.suppress | the rule bundle of this domain, rules turned off (The rule engine) |
springBoot: 4 | the Boot version when generating without the plugin |
httpPort: spring | HTTP ports as Spring @HttpExchange clients (Boot 4) (HTTP ports) |
context | the bounded context's boundaries (below) |
referenceColumn: short | DDD id references use the field's name as column (customer_id) - for new projects, since it renames existing columns |
adminRole | the role @access(admin) checks (default ADMIN) |
outbox: true | domain events through a transactional outbox (events) |
mapping: xml | JPA mapping in META-INF/orm.xml, no jakarta.persistence in the domain classes |
archTest: true | <Domain>ArchitectureTest with ArchUnit, generated once (needs com.tngtech.archunit:archunit-junit5:1.3.0) |
namespace:
base lays out the hexagonal packages under one package; any key written next to it wins:
| Key | Package with base: com.acme.erp |
|---|---|
entity, enumeration | com.acme.erp.domain.model |
event / domainService / domainRepository | .domain.event / .domain.service / .domain.repository |
service / dto | .application / .application.dto |
inboundPort / queryPort, port | .application.port.in / .application.port.out |
control | .adapter.in.web |
repository, persistence | .infrastructure.persistence |
Presets for the layered, DDD and hexagonal layouts: Architectures.
Multi-module
In a multi-module build a namespace can point into another module with module://package, so that,
for example, only services and controllers go to an application module:
namespace:
service: app-admin://com.acme.shop.service
The plugin applied to the root finds every subproject with src/main/resources/blueprint
(elasticore { modules = ['core-domain', 'app-admin'] } to name them).
Bounded contexts
A blueprint domain is a bounded context. A domain with context: says what it uses from other
contexts (uses) and what others may use of it (publishes), and the build enforces it.
# blueprint/catalog/env.yml
config:
context:
publishes: [ProductDTO, Unit] # what other contexts may use as field types (DTOs, enums, @embeddable)
# blueprint/shop/env.yml
config:
context:
uses: catalog # the upstream contexts this one relies on
publishes: [OrderPlaced] # events too (root name + event name)
| Situation | Result |
|---|---|
a shop field uses catalog's ProductDTO (listed in both) | passes |
a shop field uses catalog's entity Product | build fails: another context's entity is no field type - hold its id (productId: long) or use a DTO it publishes |
a type of a context not in uses | build fails (... does not declare it (config.context.uses: catalog)) |
a type missing from the other context's declared publishes | build fails (... which catalog does not publish) |
a process manager on: [shop:OrderPlaced] in another context | checked like a type |
neither side declares context: | as before |
[elcore3] context: [shop] Order.product is catalog's entity Product: another context's entity is no field type
- hold its id (productId: long), or use a DTO catalog publishes.
Any context: also generates blueprint/context-map.md, a Mermaid map of the contexts (it shows on
GitHub as is). The 2.x generator does not read context:.
Generating one domain
./gradlew elcore3 -Pdomain=shop
All domains are loaded (so cross-domain references resolve), then only shop is written. Cleaning up
files the DSL no longer produces happens only in a run that generated every domain.