Skip to main content
ElastiCORE 3.0 is a release candidate: 3.0.261022-rc1 on Maven Central. Projects on the 2.2 line: see the 2.2 documentation.
Version: 3.0

Choosing an architecture

One model, three readings. The architecture word in the elasticore { } block picks the bundle; the model stays as it is.

build.gradle
elasticore {
architecture = 'hexagonal' // layered | ddd | hexagonal
language = 'java' // java | kotlin
}

What each one gives​

LayeredDDDHexagonal
Java bundlejpa-springboot (-Pv3: jpa-springboot-v3)ddd-springboothexagonal-springboot
Kotlin bundlejpa-springboot-ktddd-springboot-kthexagonal-springboot-kt
Entities, DTOs, mapper, repository, services, controllersyesyesyes
Repository, service, controller forevery exposed entityaggregate roots onlyaggregate roots only
@access, @owner, @password, @login, @signin enforcedJava: not in the default 2.x-compatible output (the generator warns); -Pv3 and Kotlin: yesyesyes
Lifecycles (states:), @snapshot, @computenoyesyes
Invariants, factories, specifications, domain events, commands:noyesyes
Sealed roots (XxxBase + your Xxx), @editablenoyesyes
References to other roots by id (customerId)noyesyes
Repository port in the domain + JPA adapternoyesyes
Inbound port <Entity>UseCase, controllers inject itnonoyes
Problem+json error answers (XxxApiErrors)-Pv3 and Kotlinyesyes

How to choose​

  • A new project: hexagonal or ddd. They enforce everything the DSL can say - access, ownership, lifecycles, derived values - in regenerated code, so a rule holds at every entry point. The pet store, the quick start and the agent kit's recipes use hexagonal.
  • hexagonal when you want the application's surface as an interface (OrderUseCase) and the packages laid out as ports and adapters (application.port.in, adapter.in.web, infrastructure.persistence).
  • ddd for the same domain model with the controller calling the application service directly.
  • layered for a 2.x project moving to 3.0: in Java it is the 2.x-compatible output on the rule engine. Add -Pv3 for the cleaned-up output with access checks and problem+json answers.
Packages are configuration

Where each kind of file goes is set in env.yml (namespace:), not by the bundle. namespace: base: com.acme.shop lays out the hexagonal packages under one base; any key written next to it wins. The bundles decide what is generated; the namespaces decide where.

Changing the architecture later​

Changing architecture is a decision: the files generated per root change (a split root, a use case, id references), and the generate-once files you already own are not rewritten. The generator tells you what it cannot rewrite - for example, an extension service whose constructor no longer matches gets the warning constructor out of date with its core service - the generator now calls it with (...).