design-patterns
Skill1.4k starsChanged 5 days agoApache-2.0View on GitHub
Design patterns with Java examples - classic (Factory, Builder, Strategy, Observer, Decorator, Adapter) and enterprise/microservices (Saga, Outbox, CQRS). Use when user asks about patterns, designing extensible components, or distributed transaction strategies.
33 other projects use a version of this, 5 of them edited.
Adopted by
Steady: 0 new projects took it in the last 4 weeks, none the 4 weeks before.thin data
Most recently taken up by
- thistleknot/skillstheir own version, first seen Nov 8, 2026
- gustaferiksson/skillstheir own version, first seen Nov 7, 2026
- alexandrecsp/skillstheir own version, first seen Nov 1, 2026
- cookieMonsterDev/agents-skillstheir own version, first seen Nov 1, 2026
- decebals/claude-code-javaedited, first seen Nov 1, 2026
- ralvarez-ai/ralvaskillstheir own version, first seen Nov 1, 2026
Who's talking about piomin/claude-ai-spring-boot
- sudosubin/agents.nix“claude-code-marketplaces.github.piomin.claude-ai-spring-boot: 1.2.0 → 1.2.1”GitHub · Nov 8, 2026
- sudosubin/agents.nix“agent-skills.github.piomin.claude-ai-spring-boot: 1.2.0 → 1.2.1”GitHub · Nov 8, 2026
- sudosubin/agents.nix“claude-code-plugins.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-28 → 1.2.0”GitHub · Oct 30, 2026
- sudosubin/agents.nix“agent-skills.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-27 → 1.2.0”GitHub · Oct 30, 2026
- sudosubin/agents.nix“claude-code-marketplaces.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-28 → 1.2.0”GitHub · Oct 30, 2026
The biggest 5 of 17 mentions linking to the repository that our crawl found. Each line is their own headline.
See all 17
Talked about on GitHub (17 times).
On GitHub · 17
- Nov 8, 2026claude-code-marketplaces.github.piomin.claude-ai-spring-boot: 1.2.0 → 1.2.1 — sudosubin/agents.nix0 reactions
- Nov 8, 2026agent-skills.github.piomin.claude-ai-spring-boot: 1.2.0 → 1.2.1 — sudosubin/agents.nix0 reactions
- Oct 30, 2026claude-code-plugins.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-28 → 1.2.0 — sudosubin/agents.nix0 reactions
- Oct 30, 2026agent-skills.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-27 → 1.2.0 — sudosubin/agents.nix0 reactions
- Oct 30, 2026claude-code-marketplaces.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-28 → 1.2.0 — sudosubin/agents.nix0 reactions
12 more on GitHub
- Oct 29, 2026agent-skills.github.piomin.claude-ai-spring-boot: 9f16f8d → 1f0e36f — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-plugins.github.piomin.claude-ai-spring-boot: init at 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-marketplaces.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-27 → 0-unstable-2026-10-28 — sudosubin/agents.nix0 reactions
- Oct 28, 2026claude-code-marketplaces.github.piomin.claude-ai-spring-boot: init at 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 27, 2026agent-skills.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-18 → 0-unstable-2026-10-27 — sudosubin/agents.nix0 reactions
- Oct 27, 2026Retrospective: align the plugin with spring-person-api-claude 3.4.3 and crud-ui-blueprint 1.2.3 — mmartofel/claude-ai-spring-boot0 reactions · 3 comments
- Oct 18, 2026agent-skills.github.piomin.claude-ai-spring-boot: 0d23826 → cf876a1 — sudosubin/agents.nix0 reactions
- Oct 18, 2026agent-skills.github.piomin.claude-ai-spring-boot: 0-unstable-2026-10-16 → 0-unstable-2026-10-18 — sudosubin/agents.nix0 reactions
- Oct 16, 2026agent-skills.github.piomin.claude-ai-spring-boot: 0-unstable-2026-04-27 → 0-unstable-2026-10-16 — sudosubin/agents.nix0 reactions
- Oct 1, 2026agent-skills.github.piomin.claude-ai-spring-boot: init at 0-unstable-2026-04-27 — sudosubin/agents.nix0 reactions
- Mar 29, 2026Create workspace-template SKILL.md with wizard flow — riccardomerolla/llm4zio0 reactions · 1 comment
- Mar 21, 2026feat: support GitHub tree URLs in GitInstaller — neovateai/neovate-desktop0 reactions · 1 comment
Used with
Projects with it also use tdd-rust, solid-principles and 7 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
FlorianBruniauxSkilltdd-rust
Also in 3 projects that use this
nek1987Skillsolid-principles
SOLID object-oriented design principles for maintainable code
Also in 5 projects that use this
ufodev-ioSkillsecurity-guardian
Comprehensive security layer for all interactions with Claude: audits code, dependencies, commands, configs, and AI-generated output for vulnerabilities. Protects against prompt injection, data leaks, unsafe advice, and social engineering. Works in Cursor, Claude Code, claude.ai, and API contexts. Use always as a background security protocol in any Claude-powered environment.
Also in 3 projects that use this
DIL8654Skillapi-contract-review
Review ASP.NET Core Web API contracts for HTTP semantics, versioning, backward compatibility, and response consistency. Use when the user asks to review an API, check endpoints, validate REST behavior, or before releasing contract changes.
Also in 3 projects that use this
zavierSkilljava-code-review
Java代码审查技能,通过比较两个git分支进行自动化代码审查
Also in 3 projects that use this
tossSkillpr-triage
Classify and summarize a PR for efficient review
Also in 3 projects that use this
Other versions33 projects use a version of this.
Word for word · 0
None found.
Edited · 5
- decebals/claude-code-java★ 76326% same
- piomin/claude-ai-quarkus★ 779% same
- iCesofT/awesome-ai★ 126% same
- piomin/spring-boot-app-with-claudecode★ 193% same
- mvonline/claude-skills★ 026% same
Wrote their own version · 28
- rtk-ai/rtk★ 86ksame name
- waybarrios/opencode-power-pack★ 547same name
- FlorianBruniaux/ccboard★ 94same name
- nimadorostkar/Claude-Skills-collection★ 33same name
- DIL8654/claude-code-dotnet-template★ 8same name
- tstapler/dotfiles★ 8same name
- cookieMonsterDev/agents-skills★ 7same name
- ralvarez-ai/ralvaskills★ 4same name
- ralvarezdev/ralvaskills★ 4same name
- kayaman/skills★ 3same name
18 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository7 other files it gives its agents.
- code-qualitySkill
- conventionsSkill
- java-modernSkill
- jpa-patternsSkill
- logging-patternsSkill
- spring-bootSkill
- testing-patternsSkill
The file
We split this skill into the rules it gives an agent
5 rules. 0 are common, 5 are distinctive, and 0 are very specific to this project.
0
Common
Most of the 32 edited versions we read keep these.
5
Distinctive
This author's own choices. Worth a look.
0
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 81
Problem: Create objects without knowing exact class upfront
- Distinctive: line 212
Problem: Add behavior dynamically without modifying class
- Distinctive: line 422
Problem: Read and write models have different optimization needs
- Distinctive: line 512
Read/write ratio is heavily skewed (many reads, few writes)
- Distinctive: line 513
Read model needs different shape than write model (denormalized views)
“Common” is measured: kept by at least 6 in 10 of the 32 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Design Patterns Skill
Quick reference for common design patterns in Java.
When to Use
- User asks to implement a specific pattern
- Designing extensible/flexible components
- Refactoring rigid code
Quick Reference: When to Use What
| Problem | Pattern | Use When |
| Complex object construction | Builder | Many parameters, some optional |
| Create objects without specifying class | Factory | Type determined at runtime |
| Multiple algorithms, swap at runtime | Strategy | Behavior varies by context |
| Add behavior without changing class | Decorator | Dynamic composition needed |
| Notify multiple objects of changes | Observer | One-to-many dependency |
| Convert incompatible interfaces | Adapter | Integrate legacy/3rd party code |
| DB update + event publish atomically | Outbox | Dual-write problem in microservices |
| Distributed transaction across services | Saga | Multi-service business process |
| Separate read/write models | CQRS | Heavy read/write ratio skew |
Creational Patterns
Builder
Problem: Telescoping constructors, many optional parameters
// ✅ Builder pattern
public class User {
private final String name; // required
private final String email; // required
private final int age; // optional
private User(Builder builder) {
this.name = builder.name;
this.email = builder.email;
this.age = builder.age;
}
public static Builder builder(String name, String email) {
return new Builder(name, email);
}
public static class Builder {
private final String name;
private final String email;
private int age = 0;
private Builder(String name, String email) {
this.name = name;
this.email = email;
}
public Builder age(int age) {
this.age = age;
return this;
}
public User build() {
return new User(this);
}
}
}
// Usage
User user = User.builder("John", "john@example.com")
.age(30)
.build();Factory
Problem: Create objects without knowing exact class upfront
// ✅ Factory pattern
public interface Notification {
void send(String message);
}
public class NotificationFactory {
public static Notification create(String type) {
return switch (type.toUpperCase()) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SmsNotification();
case "PUSH" -> new PushNotification();
default -> throw new IllegalArgumentException("Unknown: " + type);
};
}
}
// Spring version - preferred
@Component
public class NotificationFactory {
private final Map<String, NotificationSender> senders;
public NotificationFactory(List<NotificationSender> senderList) {
this.senders = senderList.stream()
.collect(Collectors.toMap(
NotificationSender::getType,
Function.identity()
));
}
public NotificationSender get(String type) {
return Optional.ofNullable(senders.get(type))
.orElseThrow(() -> new IllegalArgumentException("Unknown: " + type));
}
}Behavioral Patterns
Strategy
Problem: Multiple algorithms for same operation, choose at runtime
// ✅ Strategy pattern
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
public class CreditCardPayment implements PaymentStrategy {
private final String cardNumber;
public CreditCardPayment(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(BigDecimal amount) {
System.out.println("Paid " + amount + " with card " + cardNumber);
}
}
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(BigDecimal total) {
paymentStrategy.pay(total);
}
}
// Usage
cart.setPaymentStrategy(new CreditCardPayment("4111..."));
cart.checkout(new BigDecimal("99.99"));
// Functional variant (Java 8+)
@FunctionalInterface
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
PaymentStrategy creditCard = amount -> System.out.println("Card: " + amount);
cart.setPaymentStrategy(creditCard);Observer
Problem: Notify multiple objects when state changes
// ✅ Spring Events (preferred)
public record OrderPlacedEvent(Order order) {}
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
public void placeOrder(Order order) {
saveOrder(order);
eventPublisher.publishEvent(new OrderPlacedEvent(order));
}
}
@Component
public class InventoryListener {
@EventListener
public void handleOrderPlaced(OrderPlacedEvent event) {
// Reduce inventory
}
}
@Component
public class EmailListener {
@EventListener
@Async
public void handleOrderPlaced(OrderPlacedEvent event) {
// Send email
}
}Structural Patterns
Decorator
Problem: Add behavior dynamically without modifying class
// ✅ Decorator pattern
public interface Coffee {
String getDescription();
BigDecimal getCost();
}
public class SimpleCoffee implements Coffee {
public String getDescription() { return "Coffee"; }
public BigDecimal getCost() { return new BigDecimal("2.00"); }
}
public abstract class CoffeeDecorator implements Coffee {
protected final Coffee coffee;
public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
}
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) { super(coffee); }
public String getDescription() {
return coffee.getDescription() + ", Milk";
}
public BigDecimal getCost() {
return coffee.getCost().add(new BigDecimal("0.50"));
}
}
// Usage
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);Adapter
Problem: Make incompatible interfaces work together
// ✅ Adapter pattern
public interface MediaPlayer {
void play(String filename);
}
// Legacy code
public class LegacyAudioPlayer {
public void playMp3(String filename) { /* ... */ }
}
// Adapter
public class Mp3PlayerAdapter implements MediaPlayer {
private final LegacyAudioPlayer legacyPlayer = new LegacyAudioPlayer();
@Override
public void play(String filename) {
legacyPlayer.playMp3(filename);
}
}
// Usage
MediaPlayer player = new Mp3PlayerAdapter();
player.play("song.mp3");Enterprise / Microservices Patterns
Transactional Outbox
Problem: Atomically update database AND publish an event (dual-write problem)
// ✅ Outbox pattern - write event to DB in same transaction, poll and publish later
@Entity
@Table(name = "outbox_events")
public class OutboxEvent {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String aggregateType;
private String aggregateId;
private String eventType;
@Column(columnDefinition = "TEXT")
private String payload;
private LocalDateTime createdAt;
private boolean published;
// getters/setters
}
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final OutboxEventRepository outboxRepository;
public OrderService(OrderRepository orderRepository, OutboxEventRepository outboxRepository) {
this.orderRepository = orderRepository;
this.outboxRepository = outboxRepository;
}
@Transactional
public Order createOrder(OrderRequest request) {
Order order = new Order();
order.setStatus(OrderStatus.CREATED);
order = orderRepository.save(order);
OutboxEvent event = new OutboxEvent();
event.setAggregateType("Order");
event.setAggregateId(order.getId().toString());
event.setEventType("OrderCreated");
event.setPayload(toJson(order));
event.setCreatedAt(LocalDateTime.now());
event.setPublished(false);
outboxRepository.save(event);
return order;
}
}
// Poller publishes events and marks them as published
@Component
public class OutboxPoller {
private final OutboxEventRepository outboxRepository;
private final KafkaTemplate<String, String> kafka;
public OutboxPoller(OutboxEventRepository outboxRepository,
KafkaTemplate<String, String> kafka) {
this.outboxRepository = outboxRepository;
this.kafka = kafka;
}
@Scheduled(fixedDelay = 1000)
@Transactional
public void publishPendingEvents() {
List<OutboxEvent> events = outboxRepository.findByPublishedFalse();
for (OutboxEvent event : events) {
kafka.send(event.getAggregateType(), event.getAggregateId(), event.getPayload());
event.setPublished(true);
}
}
}Saga (Choreography)
Problem: Distributed transaction across multiple microservices
// ✅ Choreography-based saga - each service listens and reacts
// Order Service publishes OrderCreated
@Service
public class OrderService {
private final ApplicationEventPublisher eventPublisher;
private final OrderRepository orderRepository;
public OrderService(ApplicationEventPublisher eventPublisher, OrderRepository orderRepository) {
this.eventPublisher = eventPublisher;
this.orderRepository = orderRepository;
}
@Transactional
public Order createOrder(OrderRequest request) {
Order order = new Order();
order.setStatus(OrderStatus.PENDING);
order = orderRepository.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(order.getId(), request.items()));
return order;
}
@Transactional
public void handlePaymentFailed(Long orderId) {
Order order = orderRepository.findById(orderId).orElseThrow();
order.setStatus(OrderStatus.CANCELLED);
orderRepository.save(order);
}
}
// Payment Service listens to OrderCreated, publishes PaymentCompleted or PaymentFailed
@Component
public class PaymentEventHandler {
private final PaymentService paymentService;
public PaymentEventHandler(PaymentService paymentService) {
this.paymentService = paymentService;
}
@KafkaListener(topics = "order-events")
public void handleOrderCreated(OrderCreatedEvent event) {
try {
paymentService.charge(event.orderId(), event.amount());
// Publish PaymentCompleted
} catch (PaymentException e) {
// Publish PaymentFailed → triggers compensating action
}
}
}Choreography vs Orchestration:
| Aspect | Choreography | Orchestration |
| Coupling | Low - services react to events | Higher - orchestrator knows all steps |
| Complexity | Hard to trace full flow | Centralized, easier to understand |
| Use when | Few steps, simple flows | Many steps, complex compensation |
CQRS (Command Query Responsibility Segregation)
Problem: Read and write models have different optimization needs
// ✅ CQRS - separate command and query models
// Command side - normalized, validates business rules
@Service
@Transactional
public class OrderCommandService {
private final OrderRepository orderRepository;
private final ApplicationEventPublisher eventPublisher;
public OrderCommandService(OrderRepository orderRepository,
ApplicationEventPublisher eventPublisher) {
this.orderRepository = orderRepository;
this.eventPublisher = eventPublisher;
}
public Long createOrder(CreateOrderCommand command) {
Order order = new Order();
order.setCustomerId(command.customerId());
order.setStatus(OrderStatus.CREATED);
command.items().forEach(order::addItem);
order = orderRepository.save(order);
eventPublisher.publishEvent(new OrderCreatedEvent(order));
return order.getId();
}
}
// Query side - denormalized, optimized for reads
@Service
@Transactional(readOnly = true)
public class OrderQueryService {
private final OrderViewRepository viewRepository;
public OrderQueryService(OrderViewRepository viewRepository) {
this.viewRepository = viewRepository;
}
public OrderView findById(Long id) {
return viewRepository.findById(id)
.orElseThrow(() -> new EntityNotFoundException("Order not found"));
}
public Page<OrderView> findByCustomer(Long customerId, Pageable pageable) {
return viewRepository.findByCustomerId(customerId, pageable);
}
}
// Read model - flat, pre-joined
@Entity
@Table(name = "order_views")
public class OrderView {
@Id
private Long id;
private Long customerId;
private String customerName;
private String status;
private BigDecimal totalAmount;
private int itemCount;
private LocalDateTime createdAt;
// getters/setters
}
// Projection updater - keeps query model in sync
@Component
public class OrderViewUpdater {
private final OrderViewRepository viewRepository;
public OrderViewUpdater(OrderViewRepository viewRepository) {
this.viewRepository = viewRepository;
}
@EventListener
public void on(OrderCreatedEvent event) {
OrderView view = new OrderView();
view.setId(event.order().getId());
view.setCustomerId(event.order().getCustomerId());
view.setStatus(event.order().getStatus().name());
view.setTotalAmount(event.order().getTotalAmount());
view.setItemCount(event.order().getItems().size());
view.setCreatedAt(event.order().getCreatedAt());
viewRepository.save(view);
}
}When to use CQRS:
- Read/write ratio is heavily skewed (many reads, few writes)
- Read model needs different shape than write model (denormalized views)
- Different scaling requirements for reads vs writes
When NOT to use: Simple CRUD applications - adds unnecessary complexity.
Pattern Selection Guide
| Situation | Pattern |
| Object creation is complex | Builder, Factory |
| Need to add features dynamically | Decorator |
| Multiple implementations of algorithm | Strategy |
| React to state changes | Observer (Spring Events) |
| Integrate with legacy code | Adapter |
| Update DB + publish event atomically | Outbox |
| Distributed transaction across services | Saga |
| Separate read/write optimization | CQRS |
Anti-Patterns to Avoid
| Anti-Pattern | Problem | Better Approach |
| Singleton abuse | Global state, hard to test | Dependency Injection |
| Factory everywhere | Over-engineering | Simple new if type known |
| Deep decorator chains | Hard to debug | Composition, keep chains short |
| CQRS for simple CRUD | Unnecessary complexity | Single model, standard repository |
| Saga without idempotency | Duplicate processing | Idempotent consumers + dedup key |
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

