Install Layered Architecture
Pick your agent. Every command is ready to paste.
npx skills add rrezartprebreza/spring-boot-skills --skill layered-architectureThe open skills CLI detects your agents (Claude Code, Cursor, Codex and 70+ more) and installs to the right folder. Add -g to install globally.
01
Install it
Run the command above, or upload the folder in Claude.
02
Ask naturally
Describe the task. Your agent loads layered-architecture when the request matches its description.
03
Name it if needed
If it does not trigger, say “use the layered-architecture skill”. In Codex, type $layered-architecture.
Layered Architecture
Layer Rules
@RestController ← HTTP only. No business logic. No JPA entities in responses.
↓ DTOs
@Service ← All business logic lives here. Orchestrates repositories.
↓ Domain objects / Entities
@Repository ← Data access only. No business logic. Returns entities or projections.
↓ JPA / JDBC
Database
Controller Layer
- Handles HTTP: parsing requests, validating input (
@Valid), returning responses - Calls ONE service method per endpoint — no orchestration in controllers
- Never returns
@Entityclasses directly — always map to response DTOs - Never injects
@Repository— always goes through a@Service - Exception handling via
@ControllerAdvice, never try/catch in controllers
// GOOD
@PostMapping("/orders")
public ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody CreateOrderRequest request) {
Order order = orderService.createOrder(request);
return ResponseEntity.status(HttpStatus.CREATED).body(OrderResponse.from(order));
}
// BAD — business logic in controller
@PostMapping("/orders")
public ResponseEntity<Order> createOrder(@RequestBody CreateOrderRequest request) {
if (request.getItems().isEmpty()) throw new RuntimeException("No items");
Order order = orderRepository.save(new Order(request)); // direct repo access
return ResponseEntity.ok(order); // returning entity
}
Service Layer
- Contains all business logic, validation rules, and orchestration
@Transactionallives here, not in controllers or repositories- Constructor injection only — never
@Autowiredfield injection - One service per aggregate root (OrderService, not OrderAndPaymentService)
- Returns domain objects or DTOs — never
HttpServletRequest/HttpServletResponse
// GOOD
@Service
@RequiredArgsConstructor
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
@Transactional
public Order createOrder(CreateOrderRequest request) {
inventoryService.reserve(request.items()); // records use accessor names, not getX()
Order order = Order.from(request);
return orderRepository.save(order);
}
}
// BAD — field injection, HTTP concern in service
@Service
public class OrderService {
@Autowired private OrderRepository orderRepository;
public ResponseEntity<Order> createOrder(...) { ... } // HTTP type in service
}
Repository Layer
- Extends
JpaRepository<Entity, ID>orCrudRepository - Custom queries via
@Queryor query derivation — no raw SQL unless unavoidable - Returns entities or Spring Data Projections — never raw
Object[] - No business logic — pure data access
DTOs
- Separate Request / Response DTOs — never use the same class for both
- Validation annotations (
@NotNull,@Size, etc.) on Request DTOs only - Static factory method
ResponseDto.from(Entity entity)for mapping - Use records for immutable DTOs (Java 16+)
// GOOD
public record OrderResponse(UUID id, String status, List<LineItemResponse> items, Instant createdAt) {
public static OrderResponse from(Order order) {
return new OrderResponse(order.getId(), order.getStatus().name(),
order.getItems().stream().map(LineItemResponse::from).toList(),
order.getCreatedAt());
}
}
Mapper Pattern
- Keep mapping logic out of controllers and services — use dedicated mapper classes or static factory methods
- Mapper is a plain class or utility — not a Spring bean unless it needs injected dependencies
- Entity → Response DTO: static method on the response DTO (
OrderResponse.from(order)) - Request DTO → Entity: static factory on the entity (
Order.from(request)) or a mapper class - Collection mapping: use
.stream().map(OrderResponse::from).toList()— never manual loops
// GOOD — dedicated mapper for complex mappings
public class OrderMapper {
public static OrderResponse toResponse(Order order) {
return new OrderResponse(
order.getId(),
order.getStatus().name(),
order.getItems().stream().map(OrderMapper::toLineItem).toList(),
order.getCreatedAt()
);
}
public static Order toEntity(CreateOrderRequest request, User user) {
Order order = Order.create(request.customerEmail(), user);
request.items().forEach(item ->
order.addItem(item.productId(), item.quantity()));
return order;
}
private static LineItemResponse toLineItem(OrderItem item) {
return new LineItemResponse(item.getProductId(), item.getQuantity(), item.getPrice());
}
}
Configuration Layer
@Configurationclasses live in aconfig/package — never inservice/orcontroller/- Configuration never imports service or controller classes
- Use
@ConfigurationPropertiesfor type-safe config — never raw@Valuefor groups of related settings - Bean definitions for infrastructure concerns only (RestTemplate, ObjectMapper, SecurityFilterChain)
Cross-Cutting Concerns
- Logging: use
@Slf4j— neverSystem.out.println - Validation:
@Validon controller parameters, custom validators as@Component - Exception handling: single
@RestControllerAdviceclass, never try/catch in controllers - Auditing:
@CreatedDate/@LastModifiedDatewith@EnableJpaAuditing
Gotchas
- Agent tends to put
@Transactionalon controllers — move it to services - Agent uses
@Autowiredfield injection — always use constructor injection (@RequiredArgsConstructor) - Agent returns
List<Entity>from controllers — always map toList<ResponseDto> - Agent creates
OrderAndInventoryServicegod classes — split by aggregate - Agent puts mapping logic inside controllers — extract to mapper class or DTO factory method
- Agent creates
@Configurationclasses that depend on@Servicebeans — configuration should only wire infrastructure
Tips for Layered Architecture
- • Start a fresh session after installing so your agent picks up the new skill.
- • It bundles reference files. The agent reads them only when needed, so they cost no context until then.
- • It is part of a pack with 30 skills. Install only the ones you need with --skill to keep triggers precise.
- • If two skills overlap, mention the one you want by name.
Questions about Layered Architecture
What does the Layered Architecture skill do?
Use when generating or modifying any Spring Boot class — controllers, services, repositories, DTOs, mappers, or configuration. Enforces strict layer separation and prevents business logic from leaking across boundaries.
How do I install Layered Architecture?
Run "npx skills add rrezartprebreza/spring-boot-skills --skill layered-architecture" in your project. The skills CLI installs it for Claude Code, Cursor, Codex and 70+ other agents. In Claude on the web, upload the skill folder as a ZIP under Customize > Skills.
Does Layered Architecture work in Cursor and Codex?
Yes. Layered Architecture uses the open SKILL.md format, so it works in Claude Code, Claude, Cursor, OpenAI Codex, Gemini CLI, GitHub Copilot and other agents that support agent skills.
Is Layered Architecture free?
Layered Architecture is open source on GitHub under the MIT license. It is free to install; check the license before commercial use.
Who made Layered Architecture?
Layered Architecture is published by rrezartprebreza in the rrezartprebreza/spring-boot-skills repository, together with 29 other skills.

