Team rules (rulesets) overview
Every company and team wants something in the generated code.
- "Our annotation on every aggregate root"
- "Personal data fields never go out in the response JSON"
- "A LIKE search needs at least two characters"
- "A root that publishes events must name its topic - otherwise the build fails"
- "A message topic constants class for every event"
Edit a generated file by hand and the change disappears on the next generation (a generator's file), or has to be repeated for every entity. Write a rule once instead, and it applies to every domain and every entity on every generation. A rule is a plain Java class, and one method is one rule. ElastiCORE's own Java, Kotlin, DDD and hexagonal generation are all rules of the same kind - a team rule is just one more added after them.
public class PetstoreRules implements CodeRules {
/** A field marked @pii in the DSL is accepted in requests but never sent back. */
@Rule(value = "ps-pii", target = "dto")
void pii(EntitySpec dto, FieldSpec field) {
if (field.hasDsl("pii")) {
ctx(dto).field(field).ensure(AnnoCode.of("com.fasterxml.jackson.annotation.JsonProperty")
.valRaw("access", "JsonProperty.Access.WRITE_ONLY"));
}
}
}
email: string(100) @email @pii # our team's DSL word
elasticore { rules = ['com.example.rules.PetstoreRules'] } // added after the bundle
dependencies { elasticore project(':ps-rules') } // the generator's classpath
When to use a rule
| Situation | How |
|---|---|
| Something for one entity, one field | Check the DSL first - DSL reference |
| Business logic of one entity | By hand in the developer's file - Adding business logic |
| The same thing on every entity, field and DTO, as a team standard | A rule |
A DSL word of your team's own (@pii, @topic) | Read by a rule |
| Stopping the build when the model breaks a team rule | A rule (ModelCheck) |
| A file ElastiCORE does not make, made from the model | A rule + a skeleton (a new target) |
| Leaving out part of what ElastiCORE makes | suppress or a rule |
How it works
./gradlew build runs generation (elcore3) before compiling. Team rules run inside it, after
ElastiCORE's default rules, for every file it makes.
entity.yml ─▶ ① read YAML ─▶ ② decide files ─▶ ③ default rules ─▶ ④ team rules ─▶ ⑤ write .java ─▶ ⑥ compile
env.yml (Spec) Customer.java @Entity, PetstoreRules
CustomerDTO.java … @Column …
- ③ and ④ repeat per file: once when
Customer.javais made, again whenCustomerDTO.javais made. - ④ always comes after ③, so it can remove what the default rules added or add on top of it.
- A rule does not write the file itself. It says "this annotation on this class" or "this value in this slot"; the generator sorts the imports and writes the file. That is why the same rule applies to Java and Kotlin, and why several rules can contribute to one file.
Every rule has the same shape: read (EntitySpec, FieldSpec - the YAML as written),
decide (a plain if), write (ctx(entity) - the file being made).
| YAML | Read in a rule |
|---|---|
Customer: | entity.getName() → "Customer" |
meta: entity @root @expose(20) | entity.aggregateRoot() → true, entity.metaValue("expose") → "20" |
email: string(100) @email @pii | field.name() → "email", field.hasDsl("pii") → true |
name: string(30)! @search(like) | field.required() → true, field.dslValue("search") → "like" |
What a rule can do
| What | API | Page |
|---|---|---|
| Add or remove annotations on classes, fields and methods, add attributes | ensureClassAnnotation, field(f).ensure/merge/removeAnnotation, method(n).ensure | Annotations and methods |
| Add a method to a class, leave out an endpoint | addMethod, excludeMethod | Annotations and methods |
| DSL words of your team's own | hasDsl, dslValue, hasMeta, metaValue | DSL words and model checks |
| Warnings, build failures, team settings | ModelCheck.warn/fail, config("…") | DSL words and model checks |
| DTO, search, SQL, external integration and enum files | target = "dto" … | Targets |
| Make new files | ArtifactContributor, a .tmpl skeleton | Making new files |
| Turn default rules off, a bundle per domain | suppress, publisher.rules | Turning off and bundles |
| Test only the rules, quickly | RuleEngine.run(...).render() | Testing |
| The same ruleset in several projects | ship it as a jar | Publishing and sharing |
Reading order
- Your first ruleset - attach a rules module to the pet store and see the first rule change the generated output
- Annotations and methods
- DSL words and model checks
- Targets
- Making new files
- Turning default rules off and choosing a bundle
- Testing
- Publishing and sharing
- API reference, Troubleshooting
Every example in these pages was built and run on the pet store project with ElastiCORE 3.0.261022-rc1.