Communitygithub.com

springboot-patterns

Patrones de arquitectura Spring Boot, diseño de APIs REST, servicios en capas, acceso a datos, caché, procesamiento asíncrono y logging. Útil para el desarrollo backend con Java Spring Boot.

¿Qué es springboot-patterns?

Este skill proporciona una guía completa de patrones de arquitectura y diseño para construir servicios backend robustos y escalables con Spring Boot. Cubre desde la estructuración de APIs REST con controladores, servicios y repositorios, hasta la implementación de caché con `@Cacheable`, procesamiento asíncrono con `@Async`, logging estructurado con SLF4J, y filtros de middleware. También incluye prácticas avanzadas como paginación y ordenamiento, llamadas externas resilientes con reintentos, limitación de tasa con Bucket4j, manejo seguro de cabeceras `X-Forwarded-For`, y recomendaciones de producción como inyección por constructor y configuración de HikariCP. Es ideal para equipos que trabajan con Java Spring Boot y buscan estandarizar su código backend, mejorar la mantenibilidad y asegurar buenas prácticas de seguridad y observabilidad. El skill se activa automáticamente al construir o revisar APIs REST, estructurar capas, configurar JPA, o implementar procesamiento basado en eventos.

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/affaan-m/everything-claude-code/tree/main/skills/springboot-patterns

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Patrones de Desarrollo Spring Boot

Patrones de arquitectura y API de Spring Boot para servicios escalables y de grado de producción.

Cuándo Activar

  • Al construir APIs REST con Spring MVC o WebFlux
  • Al estructurar las capas controlador → servicio → repositorio
  • Al configurar Spring Data JPA, caché o procesamiento asíncrono
  • Al añadir validación, manejo de excepciones o paginación
  • Al configurar perfiles para entornos de desarrollo, staging y producción
  • Al implementar patrones basados en eventos con Spring Events o Kafka

Estructura REST API

@RestController
@RequestMapping("/api/markets")
@Validated
class MarketController {
  private final MarketService marketService;

  MarketController(MarketService marketService) {
    this.marketService = marketService;
  }

  @GetMapping
  ResponseEntity<Page<MarketResponse>> list(
      @RequestParam(defaultValue = "0") int page,
      @RequestParam(defaultValue = "20") int size) {
    Page<Market> markets = marketService.list(PageRequest.of(page, size));
    return ResponseEntity.ok(markets.map(MarketResponse::from));
  }

  @PostMapping
  ResponseEntity<MarketResponse> create(@Valid @RequestBody CreateMarketRequest request) {
    Market market = marketService.create(request);
    return ResponseEntity.status(HttpStatus.CREATED).body(MarketResponse.from(market));
  }
}

Patrón Repositorio (Spring Data JPA)

public interface MarketRepository extends JpaRepository<MarketEntity, Long> {
  @Query("select m from MarketEntity m where m.status = :status order by m.volume desc")
  List<MarketEntity> findActive(@Param("status") MarketStatus status, Pageable pageable);
}

Capa de Servicio con Transacciones

@Service
public class MarketService {
  private final MarketRepository repo;

  public MarketService(MarketRepository repo) {
    this.repo = repo;
  }

  @Transactional
  public Market create(CreateMarketRequest request) {
    MarketEntity entity = MarketEntity.from(request);
    MarketEntity saved = repo.save(entity);
    return Market.from(saved);
  }
}

DTOs y Validación

public record CreateMarketRequest(
    @NotBlank @Size(max = 200) String name,
    @NotBlank @Size(max = 2000) String description,
    @NotNull @FutureOrPresent Instant endDate,
    @NotEmpty List<@NotBlank String> categories) {}

public record MarketResponse(Long id, String name, MarketStatus status) {
  static MarketResponse from(Market market) {
    return new MarketResponse(market.id(), market.name(), market.status());
  }
}

Manejo de Excepciones

@ControllerAdvice
class GlobalExceptionHandler {
  @ExceptionHandler(MethodArgumentNotValidException.class)
  ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) {
    String message = ex.getBindingResult().getFieldErrors().stream()
        .map(e -> e.getField() + ": " + e.getDefaultMessage())
        .collect(Collectors.joining(", "));
    return ResponseEntity.badRequest().body(ApiError.validation(message));
  }

  @ExceptionHandler(AccessDeniedException.class)
  ResponseEntity<ApiError> handleAccessDenied() {
    return ResponseEntity.status(HttpStatus.FORBIDDEN).body(ApiError.of("Forbidden"));
  }

  @ExceptionHandler(Exception.class)
  ResponseEntity<ApiError> handleGeneric(Exception ex) {
    // Log errores inesperados con trazas de pila
    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
        .body(ApiError.of("Internal server error"));
  }
}

Caché

Requiere @EnableCaching en una clase de configuración.

@Service
public class MarketCacheService {
  private final MarketRepository repo;

  public MarketCacheService(MarketRepository repo) {
    this.repo = repo;
  }

  @Cacheable(value = "market", key = "#id")
  public Market getById(Long id) {
    return repo.findById(id)
        .map(Market::from)
        .orElseThrow(() -> new EntityNotFoundException("Market not found"));
  }

  @CacheEvict(value = "market", key = "#id")
  public void evict(Long id) {}
}

Procesamiento Asíncrono

Requiere @EnableAsync en una clase de configuración.

@Service
public class NotificationService {
  @Async
  public CompletableFuture<Void> sendAsync(Notification notification) {
    // enviar email/SMS
    return CompletableFuture.completedFuture(null);
  }
}

Logging (SLF4J)

@Service
public class ReportService {
  private static final Logger log = LoggerFactory.getLogger(ReportService.class);

  public Report generate(Long marketId) {
    log.info("generate_report marketId={}", marketId);
    try {
      // lógica
    } catch (Exception ex) {
      log.error("generate_report_failed marketId={}", marketId, ex);
      throw ex;
    }
    return new Report();
  }
}

Middleware / Filtros

@Component
public class RequestLoggingFilter extends OncePerRequestFilter {
  private static final Logger log = LoggerFactory.getLogger(RequestLoggingFilter.class);

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    long start = System.currentTimeMillis();
    try {
      filterChain.doFilter(request, response);
    } finally {
      long duration = System.currentTimeMillis() - start;
      log.info("req method={} uri={} status={} durationMs={}",
          request.getMethod(), request.getRequestURI(), response.getStatus(), duration);
    }
  }
}

Paginación y Ordenamiento

PageRequest page = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
Page<Market> results = marketService.list(page);

Llamadas Externas Resilientes

public <T> T withRetry(Supplier<T> supplier, int maxRetries) {
  int attempts = 0;
  while (true) {
    try {
      return supplier.get();
    } catch (Exception ex) {
      attempts++;
      if (attempts >= maxRetries) {
        throw ex;
      }
      try {
        Thread.sleep((long) Math.pow(2, attempts) * 100L);
      } catch (InterruptedException ie) {
        Thread.currentThread().interrupt();
        throw ex;
      }
    }
  }
}

Limitación de Tasa (Filtro + Bucket4j)

Nota de Seguridad: La cabecera X-Forwarded-For no es confiable por defecto porque los clientes pueden falsificarla. Solo use cabeceras reenviadas cuando:

  1. Su aplicación está detrás de un proxy inverso confiable (nginx, AWS ALB, etc.)
  2. Ha registrado ForwardedHeaderFilter como un bean
  3. Ha configurado server.forward-headers-strategy=NATIVE o FRAMEWORK en las propiedades de la aplicación
  4. Su proxy está configurado para sobrescribir (no añadir) la cabecera X-Forwarded-For

Cuando ForwardedHeaderFilter está configurado correctamente, request.getRemoteAddr() devolverá automáticamente la IP correcta del cliente a partir de las cabeceras reenviadas. Sin esta configuración, use request.getRemoteAddr() directamente — devuelve la IP de conexión inmediata, que es el único valor confiable.

@Component
public class RateLimitFilter extends OncePerRequestFilter {
  private final Map<String, Bucket> buckets = new ConcurrentHashMap<>();

  /*
   * SEGURIDAD: Este filtro usa request.getRemoteAddr() para identificar clientes para la limitación de tasa.
   *
   * Si su aplicación está detrás de un proxy inverso (nginx, AWS ALB, etc.), DEBE configurar
   * Spring para manejar las cabeceras reenviadas correctamente para una detección precisa de la IP del cliente:
   *
   * 1. Configure server.forward-headers-strategy=NATIVE (para plataformas en la nube) o FRAMEWORK en
   *    application.properties/yaml
   * 2. Si usa la estrategia FRAMEWORK, registre ForwardedHeaderFilter:
   *
   *    @Bean
   *    ForwardedHeaderFilter forwardedHeaderFilter() {
   *        return new ForwardedHeaderFilter();
   *    }
   *
   * 3. Asegúrese de que su proxy sobrescribe (no añade) la cabecera X-Forwarded-For para evitar falsificaciones
   * 4. Configure server.tomcat.remoteip.trusted-proxies o equivalente para su contenedor
   *
   * Sin esta configuración, request.getRemoteAddr() devuelve la IP del proxy, no la IP del cliente.
   * NO lea X-Forwarded-For directamente—es trivialmente falseable sin el manejo de proxy confiable.
   */
  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain) throws ServletException, IOException {
    // Use getRemoteAddr() que devuelve la IP correcta del cliente cuando ForwardedHeaderFilter
    // está configurado, o la IP de conexión directa en caso contrario. Nunca confíe en las cabeceras
    // X-Forwarded-For directamente sin una configuración de proxy adecuada.
    String clientIp = request.getRemoteAddr();

    Bucket bucket = buckets.computeIfAbsent(clientIp,
        k -> Bucket.builder()
            .addLimit(Bandwidth.classic(100, Refill.greedy(100, Duration.ofMinutes(1))))
            .build());

    if (bucket.tryConsume(1)) {
      filterChain.doFilter(request, response);
    } else {
      response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());
    }
  }
}

Trabajos en Segundo Plano

Use @Scheduled de Spring o intégrese con colas (ej., Kafka, SQS, RabbitMQ). Mantenga los manejadores idempotentes y observables.

Observabilidad

  • Logging estructurado (JSON) mediante encoder Logback
  • Métricas: Micrometer + Prometheus/OTel
  • Trazado: Micrometer Tracing con backend OpenTelemetry o Brave

Valores por Defecto para Producción

  • Prefiera inyección por constructor, evite la inyección de campo
  • Active spring.mvc.problemdetails.enabled=true para errores RFC 7807 (Spring Boot 3+)
  • Configure los tamaños de pool HikariCP según la carga de trabajo, establezca tiempos de espera
  • Use @Transactional(readOnly = true) para consultas
  • Exija seguridad nula mediante @NonNull y Optional cuando corresponda

Recuerde: Mantenga los controladores ligeros, los servicios enfocados, los repositorios simples y los errores manejados de forma centralizada. Optimice para la mantenibilidad y la testabilidad.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

accessibility

Design, implement, and audit inclusive digital products using WCAG 2.2 Level AA. Use when building or auditing UI that must meet WCAG 2.2 Level AA, or when reviewing a change for keyboard, contrast, or screen-reader support.

affaan-m/claude-api

Anthropic Claude API patterns for Python and TypeScript. Covers Messages API, streaming, tool use, vision, extended thinking, batches, prompt caching, and Claude Agent SDK. Use when building applications with the Claude API or Anthropic SDKs.

affaan-m/everything-claude-code

End-to-end marketing campaign planning and execution. Covers audience research, positioning, campaign angle definition, landing page copy, email sequences, social posts, ad copy, short-form video scripts, and content calendars. Use as the orchestration layer for multi-channel product launches. Use when planning or executing a multi-channel product launch, or producing landing page, email, social, or ad copy.

affaan-m/everything-claude-code

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/everything-claude-code-conventions

Development conventions and patterns for everything-claude-code. JavaScript project with conventional commits.

affaan-m/frontend-design

Create distinctive, production-grade frontend interfaces with high design quality. Use when the user asks to build web components, pages, or applications and the visual direction matters as much as the code quality.

affaan-m/gget

gget CLI and Python workflow for quick genomic database queries, sequence lookup, BLAST-style searches, enrichment checks, and reproducible bioinformatics evidence logs.

affaan-m/literature-review

Systematic literature-review workflow for academic, biomedical, technical, and scientific topics, including search planning, source screening, synthesis, citation checks, and evidence logging.

affaan-m/motion-ui

Production-ready UI motion system for React/Next.js. Use when implementing animations, transitions, or motion patterns.

affaan-m/project-guidelines-example

Example project-specific skill template based on a real production application.

affaan-m/pubmed-database

Direct PubMed and NCBI E-utilities search workflows for biomedical literature, MeSH queries, PMID lookup, citation retrieval, and API-backed literature monitoring.

affaan-m/scholar-evaluation

Structured scholarly-work evaluation for papers, proposals, literature reviews, methods sections, evidence quality, citation support, and research-writing feedback.

affaan-m/uspto-database

USPTO patent and trademark data workflow for official record lookup, PatentSearch queries, TSDR checks, assignment data, and reproducible IP research logs.

agent-architecture-audit

Full-stack diagnostic for agent and LLM applications. Audits the 12-layer agent stack for wrapper regression, memory pollution, tool discipline failures, hidden repair loops, and rendering corruption. Produces severity-ranked findings with code-first fixes. Essential for developers building agent applications, autonomous loops, or any LLM-powered feature. Use when an agent or LLM feature misbehaves and the failing layer is unknown, or before shipping an agent stack.

agent-eval

Head-to-head comparison of coding agents (Claude Code, Aider, Codex, etc.) on custom tasks with pass rate, cost, time, and consistency metrics. Use when choosing between coding agents, or when a change to an agent setup needs measured pass rate, cost, and time rather than an impression.

agent-harness-construction

Design and optimize AI agent action spaces, tool definitions, and observation formatting for higher completion rates. Use when defining or revising an agent

agentic-engineering

Operate as an agentic engineer using eval-first execution, decomposition, and cost-aware model routing. Use when planning or executing engineering work that agents will carry out end to end.

agentic-os

Build persistent multi-agent operating systems on Claude Code. Covers kernel architecture, specialist agents, slash commands, file-based memory, scheduled automation, and state management without external databases. Use when building a persistent multi-agent system on Claude Code with its own memory, commands, and scheduling.

agent-introspection-debugging

Structured self-debugging workflow for AI agent failures using capture, diagnosis, contained recovery, and introspection reports. Use when an agent run fails and you need a reproducible diagnosis instead of a retry.

agent-payment-x402

Add x402 payment execution to AI agents with per-task budgets, spending controls, and non-custodial wallets. Supports Base through agentwallet-sdk and X Layer through OKX Payments / OKX Agent Payments Protocol. Use when an agent must pay for something itself and needs per-task budgets, spending controls, and a non-custodial wallet.

Skills relacionados