npx claudepluginhub pknull/asha-marketplace --plugin codeThis skill uses the workspace's default tool permissions.
Production architecture for scalable Spring Boot applications.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Production architecture for scalable Spring Boot applications.
src/main/java/com/example/app/
├── Application.java
├── config/
│ ├── SecurityConfig.java
│ ├── CacheConfig.java
│ └── AsyncConfig.java
├── controller/
│ └── UserController.java
├── service/
│ ├── UserService.java
│ └── impl/
│ └── UserServiceImpl.java
├── repository/
│ └── UserRepository.java
├── model/
│ ├── entity/
│ │ └── User.java
│ └── dto/
│ ├── UserRequest.java
│ └── UserResponse.java
├── exception/
│ ├── GlobalExceptionHandler.java
│ └── ResourceNotFoundException.java
└── util/
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping
public ResponseEntity<Page<UserResponse>> listUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Page<UserResponse> users = userService.findAll(PageRequest.of(page, size));
return ResponseEntity.ok(users);
}
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
return ResponseEntity.ok(userService.findById(id));
}
@PostMapping
public ResponseEntity<UserResponse> createUser(
@Valid @RequestBody UserRequest request) {
UserResponse created = userService.create(request);
URI location = URI.create("/api/v1/users/" + created.getId());
return ResponseEntity.created(location).body(created);
}
@PutMapping("/{id}")
public ResponseEntity<UserResponse> updateUser(
@PathVariable Long id,
@Valid @RequestBody UserRequest request) {
return ResponseEntity.ok(userService.update(id, request));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.noContent().build();
}
}
public record UserRequest(
@NotBlank(message = "Email is required")
@Email(message = "Invalid email format")
String email,
@NotBlank(message = "Name is required")
@Size(min = 2, max = 100, message = "Name must be 2-100 characters")
String name,
@FutureOrPresent(message = "Birth date cannot be in the past")
LocalDate birthDate
) {}
public record UserResponse(
Long id,
String email,
String name,
LocalDateTime createdAt
) {
public static UserResponse from(User user) {
return new UserResponse(
user.getId(),
user.getEmail(),
user.getName(),
user.getCreatedAt()
);
}
}
public interface UserService {
Page<UserResponse> findAll(Pageable pageable);
UserResponse findById(Long id);
UserResponse create(UserRequest request);
UserResponse update(Long id, UserRequest request);
void delete(Long id);
}
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
@Override
@Transactional(readOnly = true)
public Page<UserResponse> findAll(Pageable pageable) {
return userRepository.findAll(pageable)
.map(UserResponse::from);
}
@Override
@Transactional(readOnly = true)
public UserResponse findById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User", id));
return UserResponse.from(user);
}
@Override
@Transactional
public UserResponse create(UserRequest request) {
if (userRepository.existsByEmail(request.email())) {
throw new DuplicateResourceException("Email already exists");
}
User user = User.builder()
.email(request.email())
.name(request.name())
.build();
User saved = userRepository.save(user);
log.info("Created user id={}", saved.getId());
return UserResponse.from(saved);
}
@Override
@Transactional
public void delete(Long id) {
if (!userRepository.existsById(id)) {
throw new ResourceNotFoundException("User", id);
}
userRepository.deleteById(id);
log.info("Deleted user id={}", id);
}
}
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByEmail(String email);
Optional<User> findByEmail(String email);
@Query("SELECT u FROM User u WHERE u.status = :status")
Page<User> findByStatus(@Param("status") UserStatus status, Pageable pageable);
@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
Optional<User> findByIdWithRoles(@Param("id") Long id);
@Modifying
@Query("UPDATE User u SET u.lastLoginAt = :timestamp WHERE u.id = :id")
void updateLastLogin(@Param("id") Long id, @Param("timestamp") LocalDateTime timestamp);
}
@Entity
@Table(name = "users", indexes = {
@Index(name = "idx_users_email", columnList = "email")
})
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true)
private String email;
@Column(nullable = false)
private String name;
@Enumerated(EnumType.STRING)
@Column(nullable = false)
@Builder.Default
private UserStatus status = UserStatus.ACTIVE;
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles = new HashSet<>();
@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
private LocalDateTime updatedAt;
}
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ApiError> handleNotFound(ResourceNotFoundException ex) {
log.warn("Resource not found: {}", ex.getMessage());
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body(new ApiError("NOT_FOUND", ex.getMessage()));
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) {
List<FieldError> errors = ex.getBindingResult().getFieldErrors().stream()
.map(e -> new FieldError(e.getField(), e.getDefaultMessage()))
.toList();
return ResponseEntity
.status(HttpStatus.UNPROCESSABLE_ENTITY)
.body(new ApiError("VALIDATION_ERROR", "Invalid request", errors));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiError> handleGeneric(Exception ex) {
log.error("Unexpected error", ex);
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ApiError("INTERNAL_ERROR", "An error occurred"));
}
}
public record ApiError(
String code,
String message,
List<FieldError> details
) {
public ApiError(String code, String message) {
this(code, message, List.of());
}
}
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("users", "products");
}
}
@Service
public class UserServiceImpl {
@Cacheable(value = "users", key = "#id")
public UserResponse findById(Long id) {
// Cached by id
}
@CacheEvict(value = "users", key = "#id")
public UserResponse update(Long id, UserRequest request) {
// Evicts cache on update
}
@CacheEvict(value = "users", allEntries = true)
public void clearCache() {
// Clears all entries
}
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(8);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.initialize();
return executor;
}
}
@Service
public class NotificationService {
@Async
public CompletableFuture<Void> sendWelcomeEmail(User user) {
// Non-blocking email send
emailClient.send(user.getEmail(), "Welcome!");
return CompletableFuture.completedFuture(null);
}
}
@Component
@Slf4j
public class RequestLoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) throws ServletException, IOException {
long start = System.currentTimeMillis();
try {
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - start;
log.info("method={} uri={} status={} duration={}ms",
request.getMethod(),
request.getRequestURI(),
response.getStatus(),
duration);
}
}
}
# application.yml
spring:
datasource:
url: jdbc:postgresql://localhost:5432/app
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
format_sql: true
open-in-view: false
mvc:
problemdetails:
enabled: true
logging:
level:
org.springframework.web: INFO
com.example.app: DEBUG
org.hibernate.SQL: DEBUG
| Avoid | Instead |
|---|---|
| Field injection | Constructor injection |
| Logic in controllers | Service layer |
| N+1 queries | @EntityGraph or JOIN FETCH |
| Open Session in View | Explicit fetching |
Catching Exception | Specific exceptions |
@Transactional on class | Method-level |