Create JPA Persistence Adapter

VerifiedSafe

Generate or update JPA persistence adapter files (entity, repository, mapper, adapter) for a domain aggregate.

Sby Skills Guide Bot
DevelopmentIntermediate
8/3/2026
Claude Code
#kotlin#spring-data-jpa#hexagonal-architecture#persistence#code-scaffolding

Recommended for

Automated summary

Scaffolds or updates the four Kotlin/JPA files (entity, Spring Data repository, mapper, and adapter) needed to implement an outbound repository port for a domain aggregate.

Strengths

  • Generates all four files in one pass and only fills in missing methods or fields when files already exist.
  • Enforces consistent conventions: singleton mapper object, snake_case columns, string-stored enums, and JpaRepository<Entity, UUID>.
  • Accepts a specific port file or discovers and lists available outbound ports for selection.
  • Requires every port method to be implemented and distinguishes find (throws) from search (returns null).

Limitations

  • The SKILL.md is truncated at the start of Step 2 (package determination), so the package and source-root resolution procedure is incomplete; full patterns live in an external reference.
  • Assumes Kotlin, Spring Data JPA, and a hexagonal architecture layout; not applicable to other stacks.
  • Does not describe tests, validation, schema migrations, or database generation.
When to use it

Use when adding or updating the persistence layer for a domain aggregate that already has an outbound repository port in a Kotlin/Spring hexagonal project.

When not to use it

Do not use for non-JPA storage, non-Kotlin projects, or when no outbound port or domain aggregate exists yet.

Security analysis

Safe
Quality score88/100

The skill only uses Read, Write, and Glob to inspect and generate Kotlin/Spring persistence-layer files within the repository. It contains no destructive commands, network access, credential handling, obfuscated payloads, or attempts to alter agent safety behavior. File-writing is legitimate and bounded to the described adapter package.

No concerns found

How this analysis is made

Examples

Scaffold from a port file
Create a persistence adapter for services/example-service/src/main/kotlin/com/example/newsletter/application/port/outbound/NewsletterSubscriptionRepository.kt
Discover outbound ports
Scaffold the persistence layer for one of my outbound repository ports — list the candidates first so I can choose.
Update an existing adapter
Update the existing JPA persistence adapter for the Order aggregate and add any missing port methods and entity fields.

name: create-persistence-adapter argument-hint: "[<path-to-Repository-port-file-or-domain-entity>]" allowed-tools: Read, Write, Glob description: Scaffold or update a complete JPA persistence adapter (outbound adapter) — entity, JPA repository, mapper, and adapter class — for a domain aggregate. Use when the user asks to "create a persistence adapter", "generate a JPA adapter", "scaffold the persistence layer", or "create an outbound db adapter". Accepts a repository port file or domain entity; generates all four files at once; updates existing files by adding missing methods or fields.

Skill: create-persistence-adapter

Generates or updates up to four files per aggregate in adapter/outbound/db/:

  • *Entity.kt — JPA entity mapping the aggregate to a database table
  • *JpaRepository.kt — Spring Data JpaRepository interface
  • *EntityMapper.kt — singleton object for entity ↔ domain conversion
  • *PersistenceAdapter.kt — @Component implementing the outbound repository port

If any files already exist, the skill reads them, compares against the port interface and domain entity, and applies only what is missing or outdated.

Full code patterns: see references/full-pattern.md

Pattern

Trigger: "Create a persistence adapter for newsletter subscription"
Input:   services/example-service/src/main/kotlin/.../port/outbound/NewsletterSubscriptionRepository.kt
Output — four files under adapter/outbound/db/:
  NewsletterSubscriptionEntity.kt          – @Entity data class
  NewsletterSubscriptionJpaRepository.kt   – JpaRepository interface
  NewsletterSubscriptionEntityMapper.kt    – object with toDomain / toEntity
  NewsletterSubscriptionPersistenceAdapter.kt – @Component implementing the port

What is a Persistence Adapter?

A persistence adapter is an outbound adapter. It acts as the interaction layer between your application and the database. Your code calls it via a port interface. It handles the translation between domain objects and the underlying storage mechanism (JPA/Spring Data).

IMPORTANT

  • Implement every method defined in the outbound repository port interface
  • find throws NoSuchElementException when the entity is not found; search returns null
  • All entity ↔ domain mapping goes through the singleton mapper object — never inline conversion.
  • The mapper is a Kotlin object (singleton), not a class or a companion object
  • Column names use snake_case; the @Entity(name = ...) value matches the database table name
  • Enum fields use @Enumerated(EnumType.STRING) — never store the ordinal
  • The JPA repository extends JpaRepository<EntityClass, UUID>
  • It adds adds one findBy<IdField> method returning a nullable entity for kotlin-friendly find

Instructions

Step 1 – Resolve the input source

Two possible inputs:

Option A – Repository port file path provided (a .kt file under port/outbound/):

  1. Read the file. Extract: interface name, all method signatures (names, parameter types, return types).
  2. Derive the aggregate name by stripping the Repository suffix (e.g. NewsletterSubscriptionRepository → NewsletterSubscription).
  3. Find the domain aggregate: Glob **/domain/<AggregateName>.kt. Read it to discover all fields.
  4. If you can't find the matching aggregate or port pause the execution
  5. Then ask the developer to provide correct information

Option B – No argument provided:

  • Search: Glob **/application/port/outbound/**/*.kt.
  • Filter the files to include only ones that could match as a port for a repository.
  • If no files are found, inform the developer that no outbound port files were discovered, ask them to provide the port file path directly, then proceed as Option A. -
  • If files were found list those files and ask the user which one to scaffold.
  • Proceed as Option A once the user selects a file.

Step 2 – Determine the target package and source root

Derive the base package from the repository port package (e.g. io.miragon.example.application.port.outbound → base: io.miragon.example).

Adapter package: <base>.adapter.outbound.db.

Determine the source root from the port file path (e.g. services/example-service/src/main/kotlin/).

Target directory: <source-root>/<base-package-path>/adapter/outbound/db/

Step 3 – Check existing files and determine should-state for each

Derive the four class names from the aggregate name (e.g. NewsletterSubscription):

  • Entity: NewsletterSubscriptionEntity
  • JPA Repository: NewsletterSubscriptionJpaRepository
  • Mapper: NewsletterSubscriptionEntityMapper
  • Adapter: NewsletterSubscriptionPersistenceAdapter

Check which of these files already exist at the target directory. For each file, determine its should-state as follows:

3.1 Entity (<AggregateName>Entity.kt)

Read the domain aggregate class. For each field:

  • Map domain value objects to their primitive type (e.g. Email(val value: String) → String, SubscriptionId(val value: UUID) → UUID)
  • Derive the column name as snake_case of the field name (e.g. registrationDate → registration_date, newsletterId → newsletter_id)
  • Mark enum fields with @Enumerated(EnumType.STRING)
  • Mark the ID field with @Id and @Column(name = "<id-column>", nullable = false)

Derive the table name as snake_case of the aggregate class name (e.g. NewsletterSubscription → newsletter_subscription).

Should-state: data class with @Entity(name = "table_name"), one column per aggregate field, correct annotations.

If file exists: compare fields against the domain aggregate. Add missing fields, correct outdated column names or annotations. Preserve any custom annotations already present.

If file does not exist: generate from the should-state above.

3.2 JPA Repository (<AggregateName>JpaRepository.kt)

Should-state: interface extending JpaRepository<EntityClass, UUID> with exactly one findBy<IdField>(id: UUID): EntityClass? query method.

If the file exists: check if any query methods are missing based on the port interface. Add them.

If file does not exist: generate from the should-state above.

3.3 Mapper (<AggregateName>EntityMapper.kt)

Should-state: object with two functions:

  • toDomain(entity: EntityClass): DomainClass — wraps each primitive in its domain value object
  • toEntity(domain: DomainClass): EntityClass — extracts .value from each domain value object

If a file exists: check for missing field mappings and add them.

If file does not exist: generate from the should-state above.

3.4 Adapter (<AggregateName>PersistenceAdapter.kt)

Should-state: @Component class implementing the repository port, constructor-injected JPA repository, all port methods delegating through the mapper:

  • find → throws NoSuchElementException when not found
  • search → returns null when not found
  • save → delegates to repository.save(...)
  • delete → delegates to repository.deleteById(id.value)

If file exists: compare against all port methods. Add any missing ones, preserving existing business logic.

If file does not exist: generate from the should-state above.

Step 4 – Write all files

Apply all creations and updates identified in Step 3. Use references/full-pattern.md as a concrete reference.

Step 5 – Report

List each file created, updated, or skipped (if nothing changed) and remind the developer to:

  1. Add a database migration (Flyway/Liquibase) if a new table is introduced
  2. Run /test-persistence-adapter to generate unit tests for the adapter
  3. Wire the adapter into the application service via the port interface (automatic via Spring if the package is within the base component-scan path)

From the file to a first result

The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.

View the source and supporting files

This download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.

1. Download the file

Download SKILL.md

From the root of a test project, save the file as .skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.

Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.

mkdir -p '.skill-downloads/create-jpa-persistence-adapter-mscrezqb' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/create-jpa-persistence-adapter-mscrezqb/download' --output '.skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md.part' && mv '.skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md.part' '.skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md'

2. Check prerequisites

Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.

Tools declared by the author (check availability in your agent): Read, Write, Glob

No recognized prerequisites section in this file. This does not mean it is self-contained: libraries, tools and access requirements may be mentioned elsewhere in the content or source repository.

The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.

3. Load the skill

After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.

Claude Code
test -f '.skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md' && test -s '.skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/create-persistence-adapter' && cp -R '.skill-downloads/create-jpa-persistence-adapter-mscrezqb' '.claude/skills/create-persistence-adapter'

OpenSkills

OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.

Official OpenSkills installation
openskills install './.skill-downloads/create-jpa-persistence-adapter-mscrezqb'

openskills read 'create-jpa-persistence-adapter-mscrezqb'

Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.

For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.

openskills sync

Manual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.

4. Try a first task

Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:

Read .skill-downloads/create-jpa-persistence-adapter-mscrezqb/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.

Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.

Related skills