Skip to content

Data model — JDL

Every gStack application describes its data model once, in a single JDL file, and generates the code from it. JDL is the source of truth — not the generated Java or TypeScript.

JDL (JHipster Domain Language) is a small, readable DSL for declaring an application’s domain: its entities, their fields (with validation), the relationships between them, enums, and per-entity options (REST + service + DTO + pagination + filtering + search). One *.jdl file captures the whole model.

enum Statut { DRAFT, ACTIV, SUSPENDAT, RIDICATA, EXPIRAT }
entity Interdictie {
identificator String required unique
statut Statut required
dataEmitere Instant required
atribute TextBlob // JSON payload, later converted to jsonb
}
entity Entitate {
categorie CategorieEntitate required
idnpIdno String maxlength(13)
}
relationship ManyToOne {
Interdictie{entitate(identificator)} to Entitate
}
// options: expose a filtered, paginated REST API + DTOs + Elasticsearch
filter Interdictie, Entitate
paginate Interdictie with pagination
service all with serviceImpl
dto * with mapstruct
search Interdictie, Entitate with elasticsearch

Running the generator turns that file into a full vertical slice:

Terminal window
jhipster jdl app.jdl --force

From the JDL above JHipster generates, for each entity:

  • the JPA entity + Spring Data repository,
  • the service layer (*Service + *ServiceImpl) and, for filtered entities, a *QueryService (JPA Criteria),
  • MapStruct mappers and DTOs (entities never leave the service layer),
  • the *Resource REST controller (/api/...),
  • Liquibase changelogs (the schema migration),
  • the Angular standalone CRUD screens + i18n stubs,
  • and tests (JUnit + Jest/Cypress).

Because the generator re-writes the generated files, gStack treats the JDL as authoritative:

  1. Change the model in the .jdl.
  2. Regenerate (jhipster jdl …).
  3. Re-apply hand-written logic, which is always kept in separately-named classes (e.g. a *LifecycleService, a custom *QueryService method) so regeneration never clobbers it.

Manual edits to generated *.java / *.ts / *.html survive only until the next regeneration — so business logic lives beside the generated code, not inside it.

  • Bilingual domain names. Entity and field names are the Romanian legal terms (Interdictie, Entitate, statut, idnpIdno) and are never translated; only framework plumbing is English.
  • Append-only audit. The event-log entities (Eveniment*) forbid UPDATE/DELETE (Hibernate guard + a DB trigger) — enforced in code, not expressible in JDL alone.
  • JSONB. A polymorphic/flexible field is modelled as TextBlob in JDL, then converted to a real PostgreSQL jsonb column by an extra Liquibase changelog + @JdbcTypeCode(SqlTypes.JSON) — a deliberate post-generation hardening step (JDL has no native JSONB type).
  • Liquibase is append-only. A model change adds a new changelog; committed changelogs are never edited.

One DSL per app means every gStack application has the same shape — the same layering, the same REST conventions, the same migration discipline — so an engineer (or an agent) moving between Registrul Interdicțiilor, GDocs or GRegistry finds an identical structure. The data model is reviewable in one file, and the whole stack regenerates deterministically from it.