vernac / architecture, expressed.
Browse documentation

Vernac

Architecture, expressed.

Vernac is a Domain‑Driven Architecture Language.
It turns your domain model into a working Java application.

Modern systems grow fast — and architecture often decays just as fast.
Domain logic spreads across layers, persistence leaks into business code, REST DTOs creep into aggregates, and events become an afterthought.

Vernac addresses this by making architecture explicit.

It describes IDs, values, aggregates, behavior, usecases, events, ports, adapters, repositories, persistence and integration in one language — then generates clean Java and Spring infrastructure without hidden framework magic.


Small project. Clear domain. Less boilerplate.

You do not need a large team or microservices to benefit from Domain-Driven Design. A task board, booking tool or small SaaS deserves meaningful names, valid values and business rules that are easy to find. Vernac generates the repeated Java structure so those choices remain affordable from the first useful operation.

Start small. You do not have to introduce every layer, event or integration on day one. Learn what DDD means, explore DDD for a small project, or create a working Spring Boot application.

Where would you like to start?

Open the documentation map for a guide by goal: first model, working application, persistence, integration, modules, IDE tooling or contributing. The tutorial path builds the concepts step by step; the language reference helps while writing code.


Why Vernac?

Vernac is designed for applications where correctness, clarity and maintainability matter, whether they begin as a small tool or grow into a long-lived system.


The architecture at a glance

Vernac architecture diagram

Domain → Usecases → Repositories → Persistence
Usecases → Ports → Adapters
Usecases / Adapters → Events

One language connects all of them.


A complete operation, expressed

A domain model like this one Tour Domain Model can be expressed in Vernac like this:

namespace example.dispatch;

id TourId;
value Title(String) validates {
    require(!self.string().isBlank(), "Title must not be blank");
};
value TourStatus = PLANNED | COMPLETED;
event TourCompleted(TourId);

aggregate Tour[TourId](Title, mut TourStatus) behavior {
    modify void complete() {
        self.tourStatus(TourStatus.COMPLETED);
    }
} list Tours;

repository TourRepository for Tour {
    find Tours withStatus(TourStatus status)
        where tourStatus = :status order by title;
}

usecase CompleteTour(TourId) uses TourRepository tourRepository behavior {
    execute {
        var tour = self.tourRepository().byId(tourId);
        tour.complete();
        self.tourRepository().save(tour);
        self.emit(TourCompleted.create(tour.id()));
    }
}

Follow CompleteTour: load a complete aggregate, invoke domain behavior, save under optimistic locking, and publish a fact inside the usecase transaction. A listener chooses synchronous participation or durable deferred handling.

CompleteTour transaction and event sequence


Designed for AI‑assisted development

Vernac’s language reference is deterministic and self‑contained.
It avoids hidden defaults, ambiguous semantics and framework‑specific magic.

See the AI‑specific language reference:
ai-language-reference.md


Choose your next step

Your goal Start here
Understand DDD without prior experience What is DDD?
Keep a small project simple DDD for small projects
Get a model compiling in minutes Your first model
Start with a working UI and database Spring Boot starter
Use the editor and project wizard IntelliJ setup
See persistence, events and external calls work together Database walkthrough
Find exact syntax Language reference
Understand guarantees and limits Specification
Solve a common problem FAQ

Version and prerequisites

These pages describe the Vernac 0.5.0 language and API. Source checkouts default to 0.5.0-SNAPSHOT. Released consumers select matching 0.5.0 artifacts after publication; older versions do not support all features below. Use JDK 25 to build the repository; generated applications target Java 21.
Persistence examples use PostgreSQL 17.

The tutorials start without a database and gradually add architecture.
The example guide contains build commands and separates database‑free tests from PostgreSQL checks.

Vernac is under active development. Migration review, external‑call idempotency and business invariants remain application responsibilities.


Explore a complete application

Vernac Dispatch puts the delivery story on screen.
Balance tour capacity, release a plan and follow a durable event through a simulated carrier outage and retry.

Generated HTTP endpoints and handwritten Java adapters connect the browser to Vernac usecases. Business decisions stay in the domain.

Run the Dispatch showcase.


GitHub ·
Contributing ·
Apache License 2.0 ·
Impressum ·
Privacy

Reuse architecture across applications

Publish a Vernac capability as a versioned Maven module. Export its public domain contracts, keep implementation helpers internal, and consume the compiled JAR without copying or regenerating its definitions. Applications explicitly select providers and activate usecases, listeners or HTTP entrypoints; required modules activate transitively.

The module lifecycle tutorial follows values from publication through consumption and schema evolution. The persistent module application demonstrates Billing, Receipts, durable events, an HTTP API and independent migration histories. Migration candidates remain subject to application-owner review and testing.

The ubiquitous-language tutorial shows how to keep domain meanings beside model definitions. Namespaced terms appear in type hover, travel with exported module types and produce readable/machine-readable glossaries.

See the architecture as a diagram: export a namespace from Maven or open its diagram directly from IntelliJ.

Debug the business rule, in its source

With the 0.5.0 compiler and plugin, put a breakpoint inside an inline Vernac behavior and inspect the live JVM state without first finding its generated Java implementation. Step through the rule, inspect self and locals, evaluate Java expressions and use conditional breakpoints.

The debugging walkthrough takes you through a small runnable example, including private helpers, handwritten Java transitions and logpoints. It explains the current coverage and limits. Use matching 0.5.0 release artifacts, or matching 0.5.0-SNAPSHOT artifacts when building this source checkout.

Server-rendered applications

The Spring Boot starter now declares its HTML pages and forms in Vernac. Learn how inbound MVC adapters connect typed usecases to Thymeleaf views, preserve permitted form inputs after validation errors and generate the controller and scoped error handling.