Java/Spring Boot conventions for Violet services
This skill inherits all available tools. When active, it can use any tool Claude has access to.
src/main/java/io/drizzl/platform/{service}/
├── config/ # Spring beans, configuration classes
├── temporal/ # Workflow and activity implementations
├── service/ # Core business logic
├── repository/ # Data access layer
├── web/ # REST controllers, webhook endpoints
├── model/ # Domain models and DTOs
└── {platform}/ # Platform-specific integrations (e.g., shopify/)
CRITICAL: Always use path parameters for database queries, NOT header parameters!
// CORRECT - Path parameter for database queries
@PostMapping("/apps/{app_id}/some-operation")
public ResponseEntity<?> someOperation(
@RequestHeader("X-Violet-Token") String token, // Auth only
@RequestHeader("X-Violet-App-Secret") String appSecret, // Auth only
@RequestHeader("X-Violet-App-Id") Integer appIdHeader, // Auth only
@PathVariable("app_id") Integer appId, // USE THIS for DB
@RequestBody SomeRequest request) {
repository.findByAppId(appId); // Use path parameter
}
// WRONG - Using header for database queries
@PostMapping("/some-operation")
public ResponseEntity<?> someOperation(
@RequestHeader("X-Violet-App-Id") Integer appId) {
repository.findByAppId(appId); // Security risk!
}
Standard Violet Endpoint Pattern:
POST /v1/{service}/apps/{app_id}/endpoint-name
Headers: X-Violet-Token, X-Violet-App-Secret, X-Violet-App-Id (authentication)
Path: app_id (target for database operations)
Query: Optional parameters (filters, dry_run, etc.)
Body: Request payload
Always verify nested fields are not null before accessing:
// WRONG - Assumes field is always present
if (updateDetails.getChanges() != null) {
input.put("value", updateDetails.getChanges().getValue().getNewValue());
}
// CORRECT - Check nested field first
if (updateDetails.getChanges() != null
&& updateDetails.getChanges().getValue() != null) {
input.put("value", updateDetails.getChanges().getValue().getNewValue());
}
Fresh database queries immediately after save can retrieve stale data:
// CORRECT - Pass-through pattern
Entity updated = repository.save(entity);
processWithUpdatedData(updated); // Use passed object, not fresh query
@Document annotation instead of @Entity@Field instead of @ColumnMongoRepository@CreatedDate / @LastModifiedDate for timestamps@Field annotations@Document(collection = "my_collection")
public class MyDocument {
@Id
private String id;
@Field("app_id")
private Integer appId;
@CreatedDate
@Field("created_at")
private Instant createdAt;
}
spring.jackson.property-naming-strategy=SNAKE_CASE
violet.env=production)security.aes.key=${AES_KEY:defaultKey}
security.aes.vector=${AES_VECTOR:defaultVector}
mvn clean install spring-boot:run