🎯

kotlin-spring-boot

🎯Skill

from ashchupliak/dream-team

VibeIndex|
What it does

kotlin-spring-boot skill from ashchupliak/dream-team

πŸ“¦

Part of

ashchupliak/dream-team(13 items)

kotlin-spring-boot

Installation

git cloneClone repository
git clone https://github.com/ashchupliak/dream-team.git
Shell ScriptRun shell script
./install.sh
πŸ“– Extracted from docs: ashchupliak/dream-team
40Installs
1
-
Last UpdatedDec 8, 2025

Skill Details

SKILL.md

Kotlin/Spring Boot 3.x patterns - use for backend services, REST APIs, dependency injection, controllers, and service layers

Overview

# Kotlin Spring Boot Patterns

Project Configuration

```kotlin

// build.gradle.kts

plugins {

kotlin("jvm") version "2.2.21"

kotlin("plugin.spring") version "2.2.21"

id("org.springframework.boot") version "3.5.7"

}

dependencies {

implementation("org.springframework.boot:spring-boot-starter-web")

implementation("org.springframework.boot:spring-boot-starter-data-jdbc")

implementation("org.springframework.boot:spring-boot-starter-validation")

implementation("com.fasterxml.jackson.module:jackson-module-kotlin")

}

```

Entity Pattern

```kotlin

data class Environment(

val id: UUID,

val name: String,

val status: EnvironmentStatus,

val createdAt: Instant,

val updatedAt: Instant?

)

enum class EnvironmentStatus {

PENDING, RUNNING, STOPPED, FAILED

}

```

Service Pattern

```kotlin

@Service

class EnvironmentService(

private val repository: EnvironmentRepository,

private val computeClient: ComputeClient

) {

// Use NEVER propagation - let caller control transaction

@Transactional(propagation = Propagation.NEVER)

fun create(request: CreateEnvironmentRequest): Pair {

// Check for existing (idempotency)

repository.findByName(request.name)?.let {

return Pair(it.toResponse(), false) // existing

}

// Create new

val environment = Environment(

id = UUID.randomUUID(),

name = request.name,

status = EnvironmentStatus.PENDING,

createdAt = Instant.now(),

updatedAt = null

)

val saved = repository.save(environment)

return Pair(saved.toResponse(), true) // created

}

fun findById(id: UUID): Environment =

repository.findById(id)

?: throw ResourceNotFoundRestException("Environment", id)

fun findAll(): List =

repository.findAll()

}

```

Controller Pattern

```kotlin

@RestController

class EnvironmentController(

private val service: EnvironmentService

) : EnvironmentApi {

override fun create(request: CreateEnvironmentRequest): ResponseEntity {

val (result, isNew) = service.create(request)

return if (isNew) {

ResponseEntity.status(HttpStatus.CREATED).body(result)

} else {

ResponseEntity.ok(result)

}

}

override fun getById(id: UUID): ResponseEntity =

ResponseEntity.ok(service.findById(id).toResponse())

override fun list(): ResponseEntity> =

ResponseEntity.ok(service.findAll().map { it.toResponse() })

}

```

API Interface Pattern (OpenAPI)

```kotlin

@Tag(name = "Environments", description = "Environment management")

interface EnvironmentApi {

@Operation(summary = "Create environment")

@ApiResponses(

ApiResponse(responseCode = "201", description = "Created"),

ApiResponse(responseCode = "200", description = "Already exists"),

ApiResponse(responseCode = "400", description = "Validation error")

)

@PostMapping("/api/v1/environments")

fun create(

@RequestBody @Valid request: CreateEnvironmentRequest

): ResponseEntity

@Operation(summary = "Get environment by ID")

@GetMapping("/api/v1/environments/{id}")

fun getById(@PathVariable id: UUID): ResponseEntity

@Operation(summary = "List all environments")

@GetMapping("/api/v1/environments")

fun list(): ResponseEntity>

}

```

DTO Pattern

```kotlin

data class CreateEnvironmentRequest(

@field:NotBlank(message = "Name is required")

@field:Size(max = 100, message = "Name must be <= 100 chars")

val name: String,

@field:Size(max = 500)

val description: String? = null

)

data class EnvironmentResponse(

val id: UUID,

val name: String,

val status: String,

val createdAt: Instant

)

// Extension function for mapping

fun Environment.toResponse() = EnvironmentResponse(

id = id,

name = name,

status = status.name,

createdAt = createdAt

)

```

Exception Handling

```kotlin

// Typed exceptions

throw ResourceNotFoundRestException("Environment", id)

throw ValidationRestException("Name cannot be empty")

throw ConflictRestException("Environment already exists")

// Global handler

@RestControllerAdvice

class GlobalExceptionHandler {

@ExceptionHandler(ResourceNotFoundRestException::class)

fun handleNotFound(ex: ResourceNotFoundRestException): ResponseEntity =

ResponseEntity.status(HttpStatus.NOT_FOUND)

.body(ErrorResponse(ex.message ?: "Not found"))

@ExceptionHandler(MethodArgumentNotValidException::class)

fun handleValidation(ex: MethodArgumentNotValidException): ResponseEntity {

val errors = ex.bindingResult.fieldErrors.map { "${it.field}: ${it.defaultMessage}" }

return ResponseEntity.badRequest()

.body(ErrorResponse("Validation failed", errors))

}

}

```

Kotlin Idioms

```kotlin

// Use ?.let for optional operations

user?.let { repository.save(it) }

// Use when for exhaustive matching

when (status) {

EnvironmentStatus.PENDING -> startEnvironment()

EnvironmentStatus.RUNNING -> return // already running

EnvironmentStatus.STOPPED -> restartEnvironment()

EnvironmentStatus.FAILED -> throw IllegalStateException("Cannot start failed env")

}

// Avoid !! - use alternatives

repository.findById(id).single() // throws if not exactly one

repository.findById(id).firstOrNull() // returns null if none

// Data class copy for immutable updates

val updated = environment.copy(

status = EnvironmentStatus.RUNNING,

updatedAt = Instant.now()

)

```

Configuration Properties

```kotlin

@ConfigurationProperties(prefix = "orca")

data class OrcaProperties(

val compute: ComputeProperties,

val timeouts: TimeoutProperties

) {

data class ComputeProperties(

val url: String,

val timeout: Duration = Duration.ofSeconds(30)

)

data class TimeoutProperties(

val creation: Duration = Duration.ofMinutes(5),

val termination: Duration = Duration.ofMinutes(2)

)

}

```