AgentSkillsCN

implementation

当您在这一 Kotlin/Spring 项目中实施任何功能、添加代码,或修改现有代码时,此技能便能派上用场。当您进行新增实体、服务、门面、控制器,或任何领域逻辑的写入操作时,此技能便会自动触发。

SKILL.md
--- frontmatter
name: implementation
description: Use when implementing any feature, adding code, or modifying existing code in this Kotlin/Spring project. Triggers on write operations like adding entities, services, facades, controllers, or any domain logic.

Implementation Guide

Overview

This project follows responsibility assignment, object collaboration, and expression consistency.

Quick Decision

dot
digraph layer {
    "Single domain?" [shape=diamond];
    "Service" [shape=box];
    "Multiple domains?" [shape=diamond];
    "Facade" [shape=box];
    "HTTP?" [shape=diamond];
    "Controller" [shape=box];

    "Single domain?" -> "Service" [label="yes"];
    "Single domain?" -> "Multiple domains?" [label="no"];
    "Multiple domains?" -> "Facade" [label="yes"];
    "Multiple domains?" -> "HTTP?" [label="no"];
    "HTTP?" -> "Controller" [label="yes"];
}

Critical Rules (Non-negotiable)

1. Controller Flow

ALWAYS: Controller -> Facade -> Service (never Controller -> Service)

kotlin
@RestController
class ProductV1Controller(
    private val productFacade: ProductFacade,  // Facade, NOT Service
) : ProductV1ApiSpec

See references/layer-boundaries.md for detailed patterns.

2. Layer Responsibilities

Layer@TransactionalHorizontal Dependencies
FacadeYesMultiple Services OK
ServicereadOnly onlyNo other Services

Facade = COORDINATION ONLY - No business logic (if/when/switch). Delegate to Service/Entity.

See references/layer-boundaries.md for transaction boundaries and anti-patterns.

3. Error Handling

Required: CoreException + ErrorType (single exception type)

kotlin
throw CoreException(ErrorType.NOT_FOUND, "[id = $id] 엔티티를 찾을 수 없습니다.")

See references/error-handling.md for ErrorType enum and patterns.

4. DTO Flow

code
Request.toCriteria() -> Criteria.to() -> Command -> Entity -> Info.from() -> Response.from()

See references/dto-patterns.md for complete layer structure.

5. Domain Events

RequirementPattern
Naming{Action}EventV{n} (version suffix required)
InterfaceMust implement DomainEvent
FieldsoccurredAt: Instant required
Factorycompanion object { fun from(entity) }
ChildrenUse snapshots, not entity references
kotlin
data class OrderCreatedEventV1(
    val orderId: Long,
    val items: List<OrderItemSnapshot>,  // Snapshot, not entity
    override val occurredAt: Instant = Instant.now(),
) : DomainEvent {
    companion object {
        fun from(order: Order): OrderCreatedEventV1 = OrderCreatedEventV1(...)
    }
}

6. EventListener Pattern

TypePhaseError Handling
SyncBEFORE_COMMITFailure rolls back tx
AsyncAFTER_COMMITtry-catch required, log errors

Always: @TransactionalEventListener(phase = TransactionPhase.XXX) - never plain @EventListener

Logging format: logger.info("[Event] {Action} start/complete - eventType: ${event::class.simpleName}, id: $id")

7. Entity Encapsulation

Seven Rules:

  1. BaseEntity: ALL entities extend BaseEntity (provides id, createdAt, updatedAt, deletedAt)
  2. @Table indexes: ALWAYS define indexes for query optimization
  3. private set: ALL mutable properties
  4. Behavior methods: State changes via domain verbs (use(), pay()), not setters
  5. Immutable VOs: val fields, operations return new instances
  6. Init validation: Validate in init block or factory
  7. registerEvent(): Publish events for business-significant state changes

8. Naming Conventions

ComponentPatternExample
Controller{Domain}V{n}ControllerProductV1Controller
ApiSpec{Domain}V{n}ApiSpecProductV1ApiSpec
Facade{Domain}FacadeProductFacade
Service{Domain}ServiceProductService
Event{Action}EventV{n}OrderCreatedEventV1
Query{Domain}PageQueryProductPageQuery

Methods: Domain verbs (use, expire, cancel), not technical (process, handle, execute)

Variables: Full names (totalAmount, quantity), not abbreviations (amt, qty)

Booleans: is{Adjective}, has{Noun}, canBe{Verb}

9. Domain Purity

Direction: interfaces -> application -> domain <- infrastructure

Domain imports NOTHING from other layers.

Allowed in DomainForbidden in Domain
JPA: @Entity, @Table, @Column@Transactional
@Component on Service@JsonProperty, @JsonIgnore
Spring Data imports

Port/Adapter: Interface in domain, implementation in infrastructure.

10. Null Safety

RulePattern
Required fieldsNon-nullable (no ?)
Not found?: throw CoreException(ErrorType.NOT_FOUND, "[id = $id] ...")
Optional?.let { }, listOfNotNull()
Forbidden!! operator

11. API Patterns

  • ApiSpec interface: Swagger annotations go here, Controller implements it
  • Query/PageQuery: Encapsulate pagination with init validation
kotlin
data class ProductPageQuery(val page: Int, val size: Int) {
    init {
        require(page >= 0) { "[page = $page] 페이지는 0 이상이어야 합니다." }
        require(size in 1..100) { "[size = $size] 페이지 크기는 1~100이어야 합니다." }
    }
}

12. Messages

  • Error messages: Korean, [field = $value] prefix AT START
  • KDoc: Korean
kotlin
// Correct
"[userId = $userId] 사용자를 찾을 수 없습니다."

// Wrong (prefix at end)
"사용자를 찾을 수 없습니다. [userId = $userId]"

Red Flags (Top 25)

ThoughtReality
"Controller calling Service directly"Controller -> Facade -> Service is MANDATORY
"Facade is unnecessary for simple cases"Facade is ALWAYS required
"Service calling Service"Coordinate in Facade
"Facade->Facade dependency"Use domain events
"@Transactional on Service"Only readOnly or managed in Facade
"require() is fine"Use CoreException
"Domain exception per domain"Single CoreException + ErrorType
"Return Entity directly"DTO layer required
"English error message"Korean with [field = $value] prefix
"Entity without BaseEntity"ALL entities MUST extend BaseEntity
"var without private set"ALL mutable properties need private set
"@Table without indexes"ALWAYS define indexes
"Event without V1 suffix"Version suffix required
"Just @EventListener"Use @TransactionalEventListener with phase
"Async listener without try-catch"Async failures must be caught and logged
"Inject JpaRepository directly"Use Port/Adapter pattern
"@JsonProperty in domain"JSON is infrastructure concern
"Nullable for required fields"Non-nullable by default
"!! operator"Use ?: throw CoreException
"process/handle method names"Use domain verbs
"Short variable names (amt, qty)"Full descriptive names required
"Business logic in Facade"Facade coordinates only, logic in Service/Entity
"External call inside @Transactional"Use AFTER_COMMIT event listener
"Entity is just data holder"Anemic domain model anti-pattern - entities MUST have behavior
"Skip validation in init"Invalid objects are forbidden

References

Load these files ONLY when working on specific areas:

FileWhen to Load
references/layer-boundaries.mdCode placement, Service vs Facade, transactions
references/error-handling.mdException creation, validation failures, ErrorType enum
references/dto-patterns.mdAPI endpoints, Request/Response/Criteria/Command/Info classes
references/domain-events.mdEvent publishing, EventListener patterns, cross-domain communication
references/entity-patterns.mdEntity design, encapsulation rules, null safety, domain purity
references/naming-conventions.mdMethod/variable/message naming, Korean messages
references/api-patterns.mdApiSpec interface, Query/PageQuery patterns