Manual vs Generated: Code Comparisons
This page shows side-by-side comparisons between manually writing domain interfaces and using Kastor Gen’s automatic generation.
Defining a Catalog Interface
Manual Approach
// You write this manually - 30-60 minutes
@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")
interface Catalog {
@Rdf(iri = "http://purl.org/dc/terms/title")
val title: String // Did you verify minCount/maxCount?
@Rdf(iri = "http://purl.org/dc/terms/description")
val description: String? // Is this really optional?
@Rdf(iri = "http://www.w3.org/ns/dcat#dataset")
val dataset: List<Dataset>
@Rdf(iri = "http://purl.org/dc/terms/publisher")
val publisher: Agent?
@Rdf(iri = "http://purl.org/dc/terms/issued")
val issued: String? // Should this be a Date type?
// ... 20+ more properties
// Risk: Typos, wrong IRIs, missing properties, wrong types
}
Time: 30-60 minutes | Risk: High | Maintenance: Manual
Kastor Gen approach
// You configure this - 2 minutes
import com.geoknoesis.kastor.gen.annotations.Rdf
@Rdf(
shacl = "ontologies/dcat.shacl.ttl",
context = "ontologies/dcat.context.jsonld",
)
class OntologyGenerator
// Kastor Gen generates the interface automatically
// All properties, correct types, proper nullability
Time: 2 minutes | Risk: Zero | Maintenance: Automatic
Improvement: 90-95% time savings, 100% consistency
Handling Ontology Changes
Manual Approach
When SHACL shape changes:
- Read updated SHACL (5 min)
- Identify what changed (5 min)
- Manually update interface (15-30 min)
- Update wrapper code if needed (10-20 min)
- Test changes (10 min)
- Fix any inconsistencies (10-20 min)
| Total: 55-90 minutes | Risk: Easy to miss changes |
Kastor Gen Approach
When SHACL shape changes:
- Update SHACL file (5 min)
- Run
./gradlew build(2 min) - Test changes (10 min)
| Total: 17 minutes | Risk: Zero - all changes automatically applied |
Improvement: 70-80% time savings, zero risk of missing changes
Type Safety Examples
Manual (Runtime Errors)
// Manual interface - no compile-time validation
@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")
interface Catalog {
@Rdf(iri = "http://purl.org/dc/terms/title")
val title: String? // ❌ Wrong! SHACL says minCount 1 (required)
@Rdf(iri = "http://purl.org/dc/terms/description")
val description: List<String> // ❌ Wrong! SHACL says maxCount 1
}
// Runtime error when data doesn't match
val catalog: Catalog = catalogRef.asType()
println(catalog.title) // ❌ NullPointerException if title missing
Kastor Gen (Compile-Time Safety)
// Generated interface - types match SHACL exactly
interface Catalog {
val title: String // ✅ Not nullable (minCount 1 from SHACL)
val description: String? // ✅ Single value (maxCount 1 from SHACL)
}
// Compile-time safety
val catalog: Catalog = catalogRef.asType()
println(catalog.title) // ✅ Always has value (type system guarantees it)
Improvement: 100% type safety, zero runtime type errors
Complex Relationships
Manual (Error-Prone)
// Manual interface - easy to get relationships wrong
@Rdf(iri = "http://www.w3.org/ns/dcat#Dataset")
interface Dataset {
@Rdf(iri = "http://www.w3.org/ns/dcat#distribution")
val distribution: List<Distribution> // Is this correct?
@Rdf(iri = "http://purl.org/dc/terms/theme")
val theme: List<Concept> // Or is it List<String>?
// ... many more properties
// Risk: Wrong types, missing relationships
}
Kastor Gen (Automatic)
// Generated interface - relationships from SHACL
interface Dataset {
val distribution: List<Distribution> // ✅ Type from SHACL sh:class
val theme: List<Concept> // ✅ Correct type from ontology
// All relationships automatically inferred
}
Improvement: Zero relationship errors, automatic type inference
Maintaining Consistency
Manual (Drift Over Time)
// Month 1: Interface matches ontology
interface Catalog {
val title: String
val description: String?
}
// Month 3: Ontology updated, but interface not updated
// SHACL now requires description (minCount 1)
// Interface still has description: String? // ❌ Inconsistent!
// Month 6: More drift, properties added to ontology
// Interface missing new properties // ❌ Incomplete!
Problem: Code drifts from ontology over time
Kastor Gen (Always Consistent)
// Month 1: Generated from ontology
interface Catalog {
val title: String
val description: String?
}
// Month 3: Regenerate after ontology update
interface Catalog {
val title: String
val description: String // ✅ Automatically updated (now required)
}
// Month 6: Regenerate again
interface Catalog {
val title: String
val description: String
val language: List<String> // ✅ New property automatically added
}
Improvement: 100% consistency, zero manual sync needed
Working with Multiple Ontologies
Manual (Exponential Complexity)
// You must manually write interfaces for each ontology
@Rdf(iri = "http://www.w3.org/ns/dcat#Catalog")
interface Catalog { /* ... */ }
@Rdf(iri = "http://schema.org/DataCatalog")
interface DataCatalog { /* ... */ }
@Rdf(iri = "http://purl.org/dc/terms/Agent")
interface Agent { /* ... */ }
// ... 50+ more interfaces
// Time: Days or weeks
// Risk: High - many opportunities for errors
Kastor Gen (Linear Complexity)
// Configure once for each ontology
import com.geoknoesis.kastor.gen.annotations.Rdf
@Rdf(
shacl = "ontologies/dcat.shacl.ttl",
context = "ontologies/dcat.context.jsonld",
)
class DCATGenerator
@Rdf(
shacl = "ontologies/schema.shacl.ttl",
context = "ontologies/schema.context.jsonld",
)
class SchemaGenerator
// All interfaces generated automatically
// Time: Minutes
// Risk: Zero
Improvement: Scales linearly, not exponentially
Summary
| Aspect | Manual | Kastor Gen | Improvement |
|---|---|---|---|
| Initial setup | 30-60 min/class | 2 min/ontology | 90-95% |
| Type safety | Runtime | Compile-time | 100% |
| Consistency | 70-80% | 100% | 20-30% |
| Update time | 40-65 min | 17 min | 60-75% |
| Error rate | Baseline | -90% | Fewer bugs |
| Maintenance | Manual sync | Automatic | Zero effort |
Next Steps: