Agent skill
java-backend
Java backend development skill for Spring Boot 3.x microservices using Java 21, Spring Data JPA, and Modulith. Use when creating or modifying Java backend code including REST controllers, services, repositories, entities, DTOs, mappers, tests, or configuration. Covers feature development patterns, testing conventions, and microservice best practices.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/java-backend
SKILL.md
Java Backend Development
Technology Stack
- Java 21 - Use records, pattern matching, text blocks, virtual threads
- Spring Boot 3.x - Latest stable version
- Spring Data JPA - Hibernate ORM with PostgreSQL/MySQL
- Spring Modulith - Modular monolith structure
- Maven/Gradle - Build tool (prefer Gradle with Kotlin DSL)
Project Structure
src/main/java/com/company/project/
├── module/ # Spring Modulith modules
│ ├── order/
│ │ ├── Order.java # Entity (aggregate root)
│ │ ├── OrderRepository.java
│ │ ├── OrderService.java
│ │ ├── OrderController.java
│ │ ├── dto/
│ │ │ ├── CreateOrderRequest.java
│ │ │ └── OrderResponse.java
│ │ └── mapper/
│ │ └── OrderMapper.java
│ └── package-info.java # Module boundary
├── common/ # Shared utilities
│ ├── exception/
│ │ ├── BusinessException.java
│ │ └── GlobalExceptionHandler.java
│ └── config/
└── Application.java
Layer Conventions
Entity Layer
@Entity
@Table(name = "orders")
class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Embedded
private CustomerInfo customerInfo;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "order_id")
private List<OrderItem> items = new ArrayList<>();
@Enumerated(EnumType.STRING)
private OrderStatus status;
// Factory method
public static Order create(CustomerInfo customer, List<OrderItem> items) {
Order order = new Order();
order.customerInfo = customer;
order.items = items;
order.status = OrderStatus.CREATED;
return order;
}
// Business method
public void cancel() {
if (status != OrderStatus.CREATED) {
throw new BusinessException("Cannot cancel order in status: " + status);
}
this.status = OrderStatus.CANCELLED;
}
}
Repository Layer
interface OrderRepository extends JpaRepository<Order, Long> {
@Query("SELECT o FROM Order o WHERE o.customerInfo.email = :email")
List<Order> findByCustomerEmail(@Param("email") String email);
@Modifying
@Query("UPDATE Order o SET o.status = :status WHERE o.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") OrderStatus status);
}
Service Layer
@Service
@Transactional
class OrderService {
private final OrderRepository orderRepository;
private final OrderMapper orderMapper;
public OrderResponse createOrder(CreateOrderRequest request) {
Order order = orderMapper.toEntity(request);
Order saved = orderRepository.save(order);
return orderMapper.toResponse(saved);
}
@Transactional(readOnly = true)
public OrderResponse getOrder(Long id) {
return orderRepository.findById(id)
.map(orderMapper::toResponse)
.orElseThrow(() -> new NotFoundException("Order not found: " + id));
}
public void cancelOrder(Long id) {
Order order = orderRepository.findById(id)
.orElseThrow(() -> new NotFoundException("Order not found: " + id));
order.cancel(); // Business logic in entity
}
}
Controller Layer
@RestController
@RequestMapping("/api/v1/orders")
@RequiredArgsConstructor
class OrderController {
private final OrderService orderService;
@PostMapping
ResponseEntity<OrderResponse> createOrder(@Valid @RequestBody CreateOrderRequest request) {
return ResponseEntity.status(HttpStatus.CREATED)
.body(orderService.createOrder(request));
}
@GetMapping("/{id}")
ResponseEntity<OrderResponse> getOrder(@PathVariable Long id) {
return ResponseEntity.ok(orderService.getOrder(id));
}
@PostMapping("/{id}/cancel")
ResponseEntity<Void> cancelOrder(@PathVariable Long id) {
orderService.cancelOrder(id);
return ResponseEntity.noContent().build();
}
}
DTO and Mapper Pattern
Request DTO
public record CreateOrderRequest(
@NotBlank String customerEmail,
@NotBlank String customerName,
@NotEmpty List<OrderItemRequest> items
) {}
public record OrderItemRequest(
@NotNull Long productId,
@Min(1) int quantity
) {}
Response DTO
public record OrderResponse(
Long id,
String customerEmail,
List<OrderItemResponse> items,
OrderStatus status,
BigDecimal totalAmount,
Instant createdAt
) {}
Mapper (using MapStruct)
@Mapper(componentModel = "spring")
interface OrderMapper {
@Mapping(target = "id", ignore = true)
@Mapping(target = "status", constant = "CREATED")
@Mapping(target = "createdAt", expression = "java(java.time.Instant.now())")
Order toEntity(CreateOrderRequest request);
OrderResponse toResponse(Order order);
List<OrderResponse> toResponseList(List<Order> orders);
}
Exception Handling
@ControllerAdvice
class GlobalExceptionHandler {
@ExceptionHandler(NotFoundException.class)
ResponseEntity<ErrorResponse> handleNotFound(NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(new ErrorResponse("NOT_FOUND", e.getMessage()));
}
@ExceptionHandler(BusinessException.class)
ResponseEntity<ErrorResponse> handleBusiness(BusinessException e) {
return ResponseEntity.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(new ErrorResponse("BUSINESS_ERROR", e.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException e) {
List<String> errors = e.getBindingResult().getFieldErrors().stream()
.map(error -> error.getField() + ": " + error.getDefaultMessage())
.toList();
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ErrorResponse("VALIDATION_ERROR", String.join(", ", errors)));
}
}
public record ErrorResponse(String code, String message) {}
Testing Conventions
See references/testing.md for detailed testing patterns.
Unit Test Pattern
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private OrderRepository orderRepository;
@Mock
private OrderMapper orderMapper;
@InjectMocks
private OrderService orderService;
@Test
void createOrder_shouldReturnOrderResponse() {
// Given
var request = new CreateOrderRequest("[email protected]", "John", List.of());
var order = new Order();
var expected = new OrderResponse(1L, "[email protected]", List.of(), OrderStatus.CREATED, null, null);
when(orderMapper.toEntity(request)).thenReturn(order);
when(orderRepository.save(order)).thenReturn(order);
when(orderMapper.toResponse(order)).thenReturn(expected);
// When
var result = orderService.createOrder(request);
// Then
assertThat(result).isEqualTo(expected);
}
}
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| Entity | Noun, singular | Order, OrderItem |
| Repository | Entity + Repository | OrderRepository |
| Service | Entity + Service | OrderService |
| Controller | Entity + Controller | OrderController |
| Request DTO | Action + Entity + Request | CreateOrderRequest |
| Response DTO | Entity + Response | OrderResponse |
| Method (service) | Verb + Noun | createOrder, findOrderByEmail |
| Method (controller) | Verb + Noun | createOrder, getOrder |
| Exception | Descriptive noun | OrderNotFoundException |
References
- Testing patterns: See references/testing.md for unit, integration, and contract testing
- API design: See references/api-design.md for REST conventions and pagination
- Configuration: See references/configuration.md for Spring Boot config patterns
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?