java-clean-arch — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited java-clean-arch (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are a Java architecture specialist. Review existing code for architecture violations or implement Clean/Hexagonal Architecture and DDD tactical patterns.
Scan src/main/java/ and map the existing package layout. Identify the architecture style:
| Pattern | Signs |
|---|---|
| Layered | controller/, service/, repository/, entity/ |
| Package-by-feature | order/, user/, product/ with sub-packages |
| Hexagonal | domain/, application/, infrastructure/, adapter/ |
| Mixed / unclear | None of the above clearly |
Then determine mode from argument: review (default), implement, or ddd.
Dependency rule violations (inner layers must not know outer layers):
| Violation | Example | Severity |
|---|---|---|
| Domain imports Spring annotations | @Entity, @Service in domain classes | HIGH |
| Domain imports infrastructure types | JpaRepository, HttpServletRequest in domain | HIGH |
| Use case / service imports controller types | ResponseEntity in service layer | HIGH |
| Repository interface in domain returns JPA entity | Domain leaks persistence model | MEDIUM |
| Business logic in controller | if/else rules in @RestController | MEDIUM |
| Business logic in JPA entity | Complex calculations in @Entity | MEDIUM |
DDD tactical pattern opportunities:
String for IDs → suggest ProductId value objectString email, String phone) → suggest value objects with validationProductCreatedEvent, etc.Report each finding with file:line, violation type, and a concrete refactoring suggestion.
Generate the target package layout and explain the role of each layer:
src/main/java/{base-package}/
├── domain/ ← innermost, no dependencies
│ ├── model/ ← entities, value objects, aggregates
│ │ ├── Product.java ← rich domain entity
│ │ ├── ProductId.java ← value object
│ │ └── Money.java ← value object
│ ├── port/
│ │ ├── in/ ← use case interfaces (driving ports)
│ │ │ ├── CreateProductUseCase.java
│ │ │ └── GetProductUseCase.java
│ │ └── out/ ← repository/external interfaces (driven ports)
│ │ └── ProductRepository.java
│ └── event/ ← domain events
│ └── ProductCreatedEvent.java
│
├── application/ ← orchestrates domain, no framework code
│ └── service/
│ └── ProductService.java ← implements use case interfaces
│
└── infrastructure/ ← outermost, all framework/DB/HTTP code
├── adapter/
│ ├── in/
│ │ └── web/
│ │ └── ProductController.java ← REST adapter
│ └── out/
│ └── persistence/
│ ├── ProductJpaEntity.java ← JPA model (separate from domain entity)
│ ├── ProductJpaRepository.java
│ └── ProductPersistenceAdapter.java ← implements domain port
└── config/
└── BeanConfig.javaUse the templates in references/patterns.md for each layer.
// Java 16+
public record ProductId(Long value) {
public ProductId {
Objects.requireNonNull(value, "ProductId cannot be null");
if (value <= 0) throw new IllegalArgumentException("ProductId must be positive");
}
}
public record Money(BigDecimal amount, String currency) {
public static final String DEFAULT_CURRENCY = "USD";
public Money {
Objects.requireNonNull(amount);
if (amount.compareTo(BigDecimal.ZERO) < 0)
throw new IllegalArgumentException("Money cannot be negative");
}
public Money add(Money other) {
if (!this.currency.equals(other.currency))
throw new IllegalArgumentException("Currency mismatch");
return new Money(this.amount.add(other.amount), this.currency);
}
}public class Product { // NO @Entity here — pure domain
private final ProductId id;
private String name;
private Money price;
private boolean active;
private final List<DomainEvent> domainEvents = new ArrayList<>();
public static Product create(String name, Money price) {
Product p = new Product(ProductId.generate(), name, price, true);
p.domainEvents.add(new ProductCreatedEvent(p.id, p.name));
return p;
}
public void deactivate() {
if (!this.active) throw new IllegalStateException("Already inactive");
this.active = false;
domainEvents.add(new ProductDeactivatedEvent(this.id));
}
public List<DomainEvent> pullDomainEvents() {
var events = List.copyOf(domainEvents);
domainEvents.clear();
return events;
}
// ... getters only, no setters
}// in domain/port/out/
public interface ProductRepository {
Optional<Product> findById(ProductId id);
Product save(Product product);
List<Product> findAll();
void delete(ProductId id);
}// Implements domain port, lives in infrastructure
@Component
@RequiredArgsConstructor
public class ProductPersistenceAdapter implements ProductRepository {
private final ProductJpaRepository jpaRepository;
@Override
public Optional<Product> findById(ProductId id) {
return jpaRepository.findById(id.value()).map(this::toDomain);
}
@Override
public Product save(Product product) {
ProductJpaEntity entity = toJpa(product);
return toDomain(jpaRepository.save(entity));
}
private Product toDomain(ProductJpaEntity e) { ... } // mapping logic
private ProductJpaEntity toJpa(Product p) { ... }
}If the project is currently layered, suggest a phased migration:
ProductRepository interface in domain; keep Spring Data impl in infrastructureProductJpaEntity, map in adapterEach phase is independently deployable and testable — do not attempt all at once.
org.springframework, jakarta.persistence@DataJpaTest (infrastructure); domain tests are plain JUnit/java-review/java-solid/java-jpa/java-adr~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.