AGENTS.md Template for Spring Boot with Hexagonal Architecture
An AGENTS.md for a Spring Boot service built with hexagonal architecture (ports and adapters) and Gradle. It keeps your AI agent from putting JPA and HTTP code into the domain.
# Order Service REST API that manages customer orders. ## Tech stack - Java 25 - Spring Boot 4 - Gradle (Kotlin DSL) ## Commands - Build: `./gradlew build` - Run locally: `./gradlew bootRun` - Run all tests: `./gradlew test` - Run one test: `./gradlew test --tests OrderServiceTest` ## Architecture - `domain/` holds the business model and rules. It has no framework, database or HTTP imports. - `application/` holds the use cases and the ports (interfaces) they need. - `adapters/` (web, persistence, messaging) implement the ports. - Dependencies point inward: adapters → application → domain, never the other way. - Never put SQL, HTTP calls or framework annotations in `domain/`. ## Code style - Use constructor injection. Never use field injection with `@Autowired`. - Use records for DTOs. Never return JPA entities from controllers. - Validate request bodies with Bean Validation (`@Valid`) and map errors in one `@RestControllerAdvice`. - Use `java.time` types, never `java.util.Date`. - Return `Optional` for values that can be absent; never use it for fields or parameters. - Log with SLF4J. Never use `System.out.println`. ## Testing - JUnit and AssertJ for all tests; Mockito for unit tests. - Use test slices (`@WebMvcTest`, `@DataJpaTest`) instead of `@SpringBootTest` when one layer is enough. - Integration tests run against a real database with Testcontainers, not H2. ## Working rules - Every change comes with new or updated tests. - Write a failing test first, then the code that makes it pass. - Run the build and the tests before saying a task is done. If they fail, fix them or report the failure; never hide it. - Write complete code. No placeholders such as `// rest of the code unchanged` or `TODO: implement`. - Keep changes focused on the task. Don't refactor, rename or reformat unrelated code. - Ask before adding a new dependency. - Never commit secrets, API keys or passwords. Read them from environment variables. - Don't commit or push unless asked. ## Project notes - Packages: `com.example.orders.domain`, `com.example.orders.application`, `com.example.orders.adapters.web`, `com.example.orders.adapters.persistence`. - JPA entities live only in `adapters.persistence` and are mapped to domain objects there. Domain classes have no JPA annotations. - A new external system gets a new port in `application` and a new adapter; existing adapters don't change.
Want different commands, rules or file names? Customize this template in the generator →
Hexagonal architecture is easy to explain and easy to break: one @Entity annotation on a domain class, one repository call from a controller, and the boundaries are gone. AI agents break them all the time, because most Spring Boot code they learned from is layered. This template states the boundaries in a way an agent can check.
Why These Rules
- The domain has no framework imports. It’s the single most important rule of the architecture and the easiest for an agent to verify: it can look at the imports of any file in
domain/. - Dependencies point inward. Adapters know the application, the application knows the domain, never the other way. When the agent needs the database from a use case, the rule leads it to add a port (an interface) instead of a direct call.
- JPA entities only in the persistence adapter. Without this note, agents annotate domain classes with
@Entitybecause it saves a mapping class. The note says why the mapping class is worth it. - New external system, new adapter. This is the open-closed principle applied to the whole application: you add code instead of changing code that works.
- Test first. A framework-free domain is fast to test without Spring. Writing the test first keeps the agent’s changes small and verifiable.
Adapt It to Your Project
- Replace the package names with yours. Concrete package names work better than abstract layer names, because the agent can match them against real paths.
- List your adapters, for example
adapters.messagingfor Kafka oradapters.clientfor calls to other services. - Using Maven? Change the preset in the generator; the architecture rules stay the same.
- Domain-Driven Design? The generator has rules for aggregates, value objects and bounded contexts.
The ideas behind ports, adapters and a framework-free domain come from Domain-Driven Design. Eric Evans’ book is where they started, and it gives you the vocabulary to write architecture rules that people and agents can follow:
