Tutorial: the pet store
The pet store is the YAPS shop of the Java EE 7 sample (agoncal-application-petstore-ee7), rebuilt as ElastiCORE 3.0 + Spring Boot 4, hexagonal Java. It is a sample module of the ElastiCORE repository - generated, compiled and run on every generator change - and the public demo.
| Try it | petstore.elasticore.io - sign in as user / user, or as the administrator xsko / xsko. The data resets when the server restarts |
| The source | elasticore-project/elasticore-sample-petstore |
| The DSL | src/main/resources/blueprint/petstore/ - env.yml, model/entity.yml, enum.yml, dto.yml, metrics.yml |
| API docs | /swagger-ui.html |
| The DSL next to the code | /dsl-guide.html ("DSL Guide" in the shop's header) - per business area: the rule, the DSL, the answers checked, what was hand-written, what the DSL cannot do |
What the DSL says
| Business rule | DSL |
|---|---|
| anyone browses the catalog, only the administrator edits it | @access(read=public) |
| an order is the signed-in customer's; reading and cancelling it are theirs, shipping the administrator's | @owner(customerId) @access(read=owner, create=user, cancel=owner) |
| a customer cannot make themselves an administrator | role: UserRole @access(admin) |
| passwords as hashes only; sign-in | password: string(255)! @password, login: ... @login, @signin |
| an order is placed, then shipped or cancelled; a shipped order cannot be cancelled | states: status: { initial: PLACED, ship: PLACED -> SHIPPED, cancel: PLACED -> CANCELLED } |
| a line's price is the price when ordered | unitCost: money(10.2) @snapshot(item.unitCost) |
| subtotals and the total | @compute(unitCost * quantity), @compute(sum(lines.subTotal)) |
| the card number is never answered; an address's required fields answer 400 | the value objects' @writeonly and ! |
| a home address and a card are required | homeAddress: Address! @Embedded, creditCard: CreditCard! @Embedded |
What was written by hand
| File | What |
|---|---|
domain/model/PurchaseOrder.java | the VAT and discount policy (from settings), the home address as the default delivery address |
application/PurchaseOrderService.java | the policy values injected; place() when an order is first saved |
adapter/in/web/shop/AccountController.java | sign up and be signed in at once (sign-in, sign-out and me are generated) |
DemoAccounts.java, PetstoreApplication.java | the demo accounts, two beans |
Everything else - entities, DTOs, mapper, repositories, services, controllers, access checks, state
transitions, price calculation, the TypeScript client - is generated. The screen (web/src) uses the
generated client's labels (OrderStatusLabel), validation (validateAddress), server errors
(ApiError.fieldErrors) and button checks (canCancel).
Running it
From a clone of the ElastiCORE repository:
./gradlew :elasticore-project:elasticore-sample-petstore:test # MockMvc flow tests
./gradlew :elasticore-project:elasticore-sample-petstore:webBuild # the screen (Node.js needed)
./gradlew :elasticore-project:elasticore-sample-petstore:run # http://localhost:8080
Locally the demo accounts are user / user and admin / admin (petstore.demo-accounts=false
turns them off). The database is an in-memory H2, filled from data.sql at start-up.
The steps
- The catalog - categories, products, items: public reading, search, sorting, money with a rule.
- Customers who sign in - value objects,
@password,@signin, a role only the administrator sets, sign-up. - Orders - the lifecycle, prices kept and summed, the shop's own policy.
- The back office, events, metrics and messages - shipping, the events, the measures for BI, Korean error messages, the screen.
- Evolving a generated project - a change request on a finished shop: stock, reviews, a courier API, BI measures - measured with Claude Code and the agent kit.
The build file of the sample, abridged:
elasticore {
architecture = 'hexagonal'
language = 'java'
// Read off the Spring Boot Gradle plugin in a project; this build's
// Gradle predates the Boot 4 plugin, so it is said here.
springBoot = '4'
// The model as an Apache Ossie semantic model too: blueprint/semantic_model.yml
rules = ['ossie-export']
}