Arkhe-claude-plugins spring-boot-data-ddd

Spring Boot 4 data layer implementation for Domain-Driven Design. Use when implementing JPA or JDBC aggregates, Spring Data repositories, transactional services, projections, or entity auditing. Covers aggregate roots with AbstractAggregateRoot, value object mapping, EntityGraph for N+1 prevention, and Spring Boot 4 specifics (JSpecify null-safety, AOT repositories). For DDD concepts and design decisions, see the domain-driven-design skill.

install
source · Clone the upstream repo
git clone https://github.com/joaquimscosta/arkhe-claude-plugins
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/joaquimscosta/arkhe-claude-plugins "$T" && mkdir -p ~/.claude/skills && cp -r "$T/plugins/spring-boot/skills/spring-boot-data-ddd" ~/.claude/skills/joaquimscosta-arkhe-claude-plugins-spring-boot-data-ddd && rm -rf "$T"
manifest: plugins/spring-boot/skills/spring-boot-data-ddd/SKILL.md
source content

Spring Boot Data Layer for DDD

Implements DDD tactical patterns with Spring Data JPA and Spring Data JDBC in Spring Boot 4.

Technology Selection

ChooseWhen
Spring Data JPAComplex queries, existing Hibernate expertise, need lazy loading
Spring Data JDBCDDD-first design, simpler mapping, aggregate-per-table, no lazy loading

Spring Data JDBC enforces aggregate boundaries naturally—recommended for new DDD projects.

Core Workflow

  1. Define aggregate root → 2. Map value objects → 3. Create repository → 4. Implement service layer → 5. Add projections

See WORKFLOW.md for detailed step-by-step instructions with code examples.

Quick Patterns

See EXAMPLES.md for complete working examples including:

  • Aggregate Root with AbstractAggregateRoot and domain events (Java + Kotlin)
  • Repository with EntityGraph for N+1 prevention
  • Transactional Service with proper boundaries
  • Value Objects (Strongly-typed IDs, Money pattern)
  • Projections for efficient read operations
  • Auditing with automatic timestamps

Spring Boot 4 Specifics

  • JSpecify null-safety:
    @NullMarked
    and
    @Nullable
    annotations
  • AOT Repository Compilation: Enabled by default for faster startup
  • Jakarta EE 11: All imports use
    jakarta.*
    namespace
  • ListCrudRepository: New interface returning
    List<T>
    instead of
    Iterable<T>

ListCrudRepository (Spring Data 3.1+)

New repository interface returning

List<T>
for better API ergonomics:

// OLD: CrudRepository returns Iterable<T>
public interface UserRepository extends CrudRepository<User, Long> {
    Iterable<User> findAll();  // Requires conversion to List
}

// NEW: ListCrudRepository returns List<T>
public interface UserRepository extends ListCrudRepository<User, Long> {
    List<User> findAll();  // Direct List return
    List<User> findAllById(Iterable<Long> ids);  // Also List
}

// Can also extend both for full functionality
public interface UserRepository extends
    ListCrudRepository<User, Long>,
    ListPagingAndSortingRepository<User, Long> {
}

Benefits: No more

StreamSupport.stream(iterable.spliterator(), false).toList()
conversions.

Detailed References

Related Skills

NeedSkill
DDD concepts and design
domain-driven-design
REST API for aggregates
spring-boot-web-api
Module boundaries
spring-boot-modulith
Repository testing
spring-boot-testing

Anti-Pattern Checklist

Anti-PatternFix
FetchType.EAGER
on associations
Use
LAZY
+
@EntityGraph
when needed
Returning entities from controllersConvert to DTOs in service layer
@Transactional
on private methods
Use public methods (proxy limitation)
Missing
readOnly = true
on queries
Add for read operations (performance)
Direct aggregate-to-aggregate referencesReference by ID only
Multiple aggregates in one transactionUse domain events for eventual consistency

Critical Reminders

  1. One aggregate per transaction — Cross-aggregate changes via domain events
  2. Repository per aggregate root — Never for child entities
  3. Value objects are immutable — No setters, return new instances
  4. Flush before events — Call
    repository.save()
    before events dispatch
  5. Test with
    @DataJpaTest
    — Use
    TestEntityManager
    for setup