Fields and types
A field is one line: name: type[(size)][!] [@annotations] -- label.
Value types
Every value type a field can have, with the Java type, the JSON shape, the TypeScript type and the Apache Ossie DataType it is exported as.
| DSL type | Java | JSON | TS | Ossie | Notes |
|---|---|---|---|---|---|
string / string(n) | String | "text" | string | String | n = maximum length, ! = NOT NULL |
text / longtext | String | "text" | string | String | columnDefinition = "TEXT" / "LONGTEXT" |
clob | String | "text" | string | String | @Lob |
json | String | "{...}" (a string) | string | Opaque | a JSON column |
int / Integer | Integer | 42 | number | Integer | |
long / Long | Long | 42 | number | Integer | |
biginteger | BigInteger | 123456789012345678901 | number | Integer | |
quantity | Quantity | 5 | number | Integer | 3.0 value type: a count that is never negative |
decimal(p.s) / bigdecimal(p.s) | BigDecimal | 12.34 | number | Decimal | precision, scale |
money / money(p.s) | Money | 99.95 | number | Decimal | 3.0 value type: an amount, default (19,2) |
float / double | Float / Double | 1.5 | number | Float | approximate - not for money |
boolean | Boolean | true | boolean | Boolean | |
date | LocalDate | "2026-10-03" | string | Date | |
time | LocalTime | "09:30:00" | string | Time | |
datetime | LocalDateTime | "2026-10-03 09:30:00" | string | DateTime | no time zone; not ISO T |
timestamp | java.time.Instant | "2026-10-03T00:30:00Z" | string | DateTimeTz | an instant; answered in UTC |
blob | byte[] | "AQID/w==" (base64) | string | Opaque | @Lob |
EnumName | the enum | its code ("PLC") | the enum | String | the @db(code) value is stored |
EntityName | a reference | (its id field) | - | relationship | @ManyToOne by default |
- Which time type: what people read (a business day, an appointment) is
date/datetime; "when did it happen" (a payment approved, an event, an audit) istimestamp. - Not types:
uuid(usestring(36), orstring @id @genidfor a UUID key),short/byte(int),char(string(1)),OffsetDateTime/ZonedDateTime(timestamp). - Collections:
List<T>,Set<T>,Map<K,V>, arraysint[],string[]. - The table is the 3.0 rule engine's; every type in it is generated, saved and sent over HTTP by
TypeRoundTripTestinelasticore-template-ddd.
money and quantity
Amounts and counts as value objects (io.elasticore.springboot3.domain). The column and the JSON
stay decimal and integer; the code in between uses Money and Quantity.
SalesOrderLine:
fields:
qty: quantity! @min(1)
unitPrice: money(12.2)! @min(0.01, 'a unit price must be positive')
amount: money(14.2)
line.setAmount(line.getUnitPrice().times(line.getQty()));
total = total.plus(line.getAmount());
stock.setOnHand(stock.getOnHand().minus(qty)); // not enough: 422
Money: plus, minus, times(Quantity / long / BigDecimal), rounded(n) (half-up),
isNegative; 10 and 10.00 are the same amount. Quantity is never negative (minus below zero
answers 422 not enough). @default(0) becomes Money.of("0") / Quantity.of(0). A signed change
(stock in +, out -) is not a quantity: use int.
Keys
| Write | Means |
|---|---|
id: long @id @sequence | numbered by the database; leave it out when creating |
id: string @id @genid | a UUID (UUID.randomUUID()) |
code: string(15) @id @genid | IdUtils.newId(15): a 3-letter prefix and 12 random letters and digits |
id: string @id @genid(com.acme.Ids.next) | a static method of yours |
isbn: string(13)! @id | a key the client sends; a create with an existing key answers 409 |
Validation
| Write | Means |
|---|---|
string(100)! | required, at most 100 characters - "" still passes |
string(100)! @notblank | required and not empty or blank |
string(20)! @length(min=4) | 4 to 20 characters |
string(100) @email | an e-mail address |
int @min(1) @max(10) | a whole number from 1 to 10 (on an integer field whole numbers only; decimal and money fields get @DecimalMin/@DecimalMax) |
money(10.2)! @min(0.01, 'price must be positive') | a minimum with its message - @DecimalMin |
date @past, date @future | before / after today (@pastorpresent, @futureorpresent include today) |
string(13)! @pattern('^[0-9]{13}$', 'ISBN must be 13 digits') | the whole value matches the regex; 400 with the message |
lines: List<OrderLine> ... @minsize(1, 'an order needs a line') | at least n members; @maxsize(n, '...') at most |
@pattern: also @pattern(regexp="...", message="..."). Without a message both server and
client say "<label> is not in the expected form". Write a backslash doubled (\\d) or use a class
such as [0-9] - a single \d is a DSL warning. It goes into the DTO and the TypeScript client (not
the entity, not the search DTO); on a date field @pattern(yyyyMMdd) is still a date format.
@minsize(n) with n of 1 or more also adds @NotNull: an empty list and a missing list both
answer 400; the TypeScript validate...() asks the same. In the DDD bundles the root checks it at
save time too (422).
Values the server sets
| Write | Means |
|---|---|
joinedAt: date @readonly | in answers, never taken from a request, not required - your code fills it (beforeSave) |
grade: string(10)! @noinput | the same (@noinput is an alias); the column stays NOT NULL, so the server must fill it |
orderedAt: datetime @readonly @default(now) | the time the row is made - request values ignored, kept on every update |
joinedOn: date @readonly @default(now) | today |
createdAt: datetime @updatable(false) @jpa:org.springframework.data.annotation.CreatedDate | the audit pair - needs @EnableJpaAuditing |
modifiedAt: datetime @jpa:org.springframework.data.annotation.LastModifiedDate | changes on every save (audit) |
On the v3 bundles @readonly:
- drops the DTO's
@NotNull/@NotBlank(a request cannot send the value) and adds@Schema(accessMode = READ_ONLY); - is not written by the mapper from a DTO to the entity. Code that put a value in a DTO and saved
it (
dto.setGrade("A"), thenservice.save(dto)) no longer stores it - set it on the entity (beforeSave, a method of the root); - on a field of an entity applies to all its DTOs; on a field of a
dto.ymlDTO, to that DTO only.
@default(now) is LocalDateTime.now() for datetime, and now for date, time and timestamp
(Instant.now()) - an initial value of the entity, never a default of the DTO. There is no
@createtime.
Other field annotations
| Write | Means |
|---|---|
@default(value) | a default: @default("ACTIVE"), @default(true), @default(UserRole.USER) |
@updatable(false) | @Column(updatable = false); on a reference in the DDD bundles the id field inherits it - an update keeps the stored one |
@writeonly | accepted in requests, never in answers (a card number) |
@password | stored as a hash only - see Access and sign-in |
@access(admin) | only an administrator writes it - see Access and sign-in |
@search(...) | a search condition - see Search |
@formula("sql") | a column the database computes (Hibernate) |
@Column(name="col", length=n) / @column(name) | the column mapping |
@jpa:<annotation> | a JPA or Spring Data annotation written as is |
@env:name | an annotation defined in env.yml (config.annotations) |
@terms(price, 가격) | synonyms for the Ossie export |