JHipster — What We Keep, What We Drop
1. Overview
admin-service was generated with JHipster. So was registration-portal. So was admin-ui (JHipster 7, long out of sync). admin-portal is greenfield — not generated — and so is any future portal.
This doesn’t mean we throw JHipster’s patterns away. Many are good. It means we cherry-pick: the backend service generators remain useful, the generator-driven frontend scaffolding does not. This page is the explicit inventory of which JHipster patterns we keep (and carry into new greenfield work) and which we drop.
If you’re bootstrapping a new EMS service or portal: read this, then copy from the reference implementations — registration-portal for portals, admin-service for backends.
See also: Portal Pattern, Maven POM Conventions.
2. Backend (Spring Boot) — Keep
2.1. Kept wholesale
| Pattern | Why |
|---|---|
Maven POM structure with profile-driven build variants |
Proven at scale; operators know what |
|
Easy to discover; one concern per file |
Liquibase with |
Gold standard for schema-migrations-as-code |
JWT via Nimbus with custom claims ( |
Battle-tested claim shape; extensions layered on top (see OIDC Token Exchange) |
|
One |
|
Consistent error response shape across endpoints |
Logback config with CRLF converter + ANSI/plain switching per profile |
Safe log output; K8s-friendly |
Startup banner in |
Operator diagnostic; see SpringApplication Bootstrap |
Profile validation rejecting |
Cheap safety net; log a loud error on misconfiguration |
|
Retained from the JHipster framework library; we don’t invent our own |
Hazelcast second-level cache + Spring Cache |
Required in admin-service; opt-in per service; see Hazelcast Configuration |
springdoc-openapi auto-generation |
Reflection-based spec; |
2.2. Kept as principle, customised heavily
| Pattern | Our adaptation |
|---|---|
SecurityConfig filter chain |
Stock JHipster has JWT and anonymous access. We layer API-key filter, tenant-resolution filter, OIDC login, and the JWT relay filter (portals) on top. Radically different configuration, same skeleton. |
Caching configuration |
Stock has user/authority caches. admin-service extends with |
DTO / Mapper pattern |
Stock uses Mapstruct. We use it too. The handful of DTOs where Mapstruct’s defaults didn’t fit (e.g. |
Exception-translation |
Stock returns RFC-7807 ProblemDetail. We follow it. Extend with custom fields for |
3. Backend — Drop
| Pattern | Why |
|---|---|
Auto-generated entity CRUD endpoints |
We don’t run |
JDL import |
Related to the above. We model schema by writing Liquibase XML directly; JDL adds a translation layer we don’t benefit from. |
Elasticsearch integration |
Stock JHipster offers |
User / authorities as separate service |
Microservice split. admin-service is a monolith; users and authorities live alongside business entities. Don’t split unless there’s a reason. |
Gatling performance tests in-repo |
We run load tests out-of-repo. Empty |
|
Generator state that tracks each entity. We don’t regenerate; the files go stale; delete. |
|
Generator configuration. Kept for historical reference but the answers it records no longer reflect reality (e.g. |
4. Frontend (Angular) — Keep
4.1. Kept wholesale
| Pattern | Why |
|---|---|
|
Imperfect but workable. Feature modules can live under |
|
Single place for microservice endpoint wiring. See OpenAPI Contract Flow for how it interacts with the generated client. |
JHipster language / i18n stack |
Full i18n infrastructure for free: |
Alert / toast model |
Integrates with the stock |
|
Standardised; avoids direct |
dayjs integration (date pipes, helpers, interceptor for JSON → dayjs) |
Core of our client-side date handling — extracted into |
Interceptor chain shape (auth / error-handler / auth-expired / notification) |
Composable, well-ordered; add new interceptors into the chain rather than overriding |
Auth directive |
Clean route-protection primitive; extracted into |
Sort / filter directives |
Retained functionally; restyled when Bootstrap 4 visual artefacts leak |
Pagination helpers |
Server-side pagination assumption that matches admin-service controllers |
|
Same as backend reasoning — one concern per file |
4.2. Kept as principle, customised heavily
| Pattern | Our adaptation |
|---|---|
|
registration-portal skips these in favour of webpack |
Feature module structure by entity |
We prefer module-by-bounded-context (events, membership, persons, finance). CRUD-per-entity was a generator artefact. |
JHipster routes ( |
Stock JHipster adds this guard to every generated route. Many become no-ops. We apply it only to routes that actually need authority checks (see registration-portal’s cleanup US #497). |
Styling (Bootstrap 4 / ng-bootstrap) |
Retained ng-bootstrap; Bootstrap 4 theme replaced with PrimeNG + custom styles. The retained primitives adapt. |
5. Frontend — Drop
| Pattern | Why |
|---|---|
Generator-driven entity modules |
Same reasoning as backend — the generator overwrites manual edits on re-run. We don’t regenerate. |
|
Goes stale. For admin-portal: build module-by-bounded-context; admin-ui’s 52 generated entity modules are an anti-pattern. |
Bootstrap 4 visual artefacts coupled into |
Retain the directives functionally, restyle with our own CSS. |
Translated error messages from |
Too many; unmaintained. Keep a minimal set and fall back to server-provided messages. |
|
Adds 2+ MB to the SPA bundle for a feature we don’t use in the portal. Skip unless the portal specifically wants an in-app API explorer. |
Cypress in registration-portal’s |
Kept optionally. If E2E tests are running, great; if not, don’t ship the infra unused. |
JHipster login / register / password-reset components (registration-portal generated them) |
Unused when OIDC is the only auth path. Delete. |
|
Audit and prune. Only keep what the app actually imports. |
6. Generator-Driven Regeneration — Dropped Entirely
We do not regenerate. Period. If we ever do again, it is a project-wide decision and it is preceded by a design-journal thread evaluating alternatives.
Reasons:
-
We diverge from generator assumptions. Custom caches, custom auth flows, custom tenant resolution — all would fight a regeneration attempt.
-
The frontend generator’s output drifts from our Angular version. admin-ui was Angular 14; registration-portal is Angular 16; new generator output is Angular 17+. Each regeneration is a migration too.
-
Manual work always follows regeneration. Non-trivial merge of the delta into our customisations. The hours saved by scaffolding are lost in the merge.
-
Claude Design pushes us further away. When the target UI is designed, not templated, regenerator templates are a liability.
The JHipster generator has delivered two good things we keep: a proven POM + profile layout (backend) and a set of reusable primitives (frontend). We take those and move on.
7. Reference
| File | Role |
|---|---|
|
Backend reference POM (JHipster 8.11-derived) |
|
Portal reference POM (JHipster 8.11-derived) |
|
Pre-pattern reference — what not to carry forward |
|
Historical generator configuration; kept for reference, not load-bearing |