- Kotlin 100%
LT :: CRUDular
CRUDular is an easy, modular way to add CRUD with HATEOAS HAL to your Kotlin/Java application.
It's created mainly to ease use cases such as Master Data, or user created content. There is no silver bullet for all your requirements, so please think about your usage profile before trying to do everything with the same approach.
See sample/spring-simple for a minimal example using Spring-Boot with WebMVC and a postgres database.
Usage
Add dependencies
- Import the BOM which contains version for all CRUD modules.
- Choose the implementations for
restandrepoyou need. For the spring framework there are starters, with default settings.
For details of the modules take a look at their README.md
Maven
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.loosetie.crudular</groupId>
<artifactId>lt-crudular-bom</artifactId>
<version>...</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>de.loosetie.crudular</groupId>
<artifactId>lt-crudular-repo-jooq</artifactId><!-- Choose your flavor -->
</dependency>
<dependency>
<groupId>de.loosetie.crudular</groupId>
<artifactId>lt-crudular-rest-webmvc</artifactId><!-- Choose your flavor -->
</dependency>
</dependencies>
</project>
Gradle
plugins {
id 'io.spring.dependency-management' version '...' // We suggest using this plugin, even if you do not use spring
}
dependencyManagement {
imports {
mavenBom "de.loosetie.crudular:lt-crudular-bom:..."
}
dependencies {
mavenBom "de.loosetie.crudular:lt-crudular-repo-jooq"
mavenBom "de.loosetie.crudular:lt-crudular-rest-webmvc"
}
Create CRUD entities
A CRUD entity is defined in 3 parts: Entity, Key and Search.
When using kotlin it's recommended to keep those classes in the same file :)
Entity
To create a CRUD entity create a class which implements HasBK and annotate it with @CrudEntity.
It's recommended that such classes are immutable.
import de.loosetie.crudular.api.entity.CrudEntity
import de.loosetie.crudular.api.HasBK
@CrudEntity
data class Sample(
var name: String,
var something: String
) : HasBK<SampleKey> {
override fun key() = SampleKey(author, title)
override fun equals(other: Any?) = keyEquals(other) # generic equals using the key
override fun hashCode() = keyHashCode()
}
Hint: If more performance id needed consider implementing hashCode and equals individually.
Key
Every entity needs a key.
A key has a subset of fields, implements BK and is immutable.
It represents the natural identifier of an entity.
In some cases a key may be composed of another key or even multiple.
Keep in mind that this may lead to very large keys.
Consider synthetic keys, like a UUID in this case.
import de.loosetie.crudular.api.BK
data class SampleKey(
val name: String,
) : BK<Sample>
Search
Optionally you can specify a Search for your entity.
This class is named <Entity name>Search and implements the Search interface.
This will enable filtering for your repository.
Annotate a property with CrudSearchField to configure conditions.
import de.loosetie.crudular.api.data.Search
import de.loosetie.crudular.api.entity.CrudSearchField
class SampleSearch(
@CrudSearchField("name", CrudSearchType.STARTS_WITH)
val nameStartsWith: String? = null,
override val pageSize: Int,
override val jitter: Int,
override val pivotKey: SampleKey?,
override val orderBy: List<Search.Order>?
) : Search<Sample>
Customize behavior
CRUDular allows the customization of many aspects. This section describes the main entry points for lightweight customization. If you feel like the only thing you do is customizing, please reconsider your use case and the tooling.
Annotations
The simplest approach and also the most limited approach is to use the following annotations:
CrudEntityCrudFieldCrudSearchField
See the kdoc for more.
Implementations
Supply implementations to the following interfaces, to customize CRUDular in depth:
REST
CrudHalCreateCrudHalReadCrudHalSearchCrudHalUpdateCrudHalDelete
Service
CrudServiceCreateCrudServiceReadCrudServiceSearchCrudServiceUpdateCrudServiceDelete
Repo
RepoValueMapperto customize data conversion.CrudRepoAfterCreate,CrudRepoAfterUpdateorCrudRepoBeforeDeleteto add behavior at the lowest level.
Writeable views
To make a view entity writeable, first make sure all the non required values have defaults.
Then implement CrudServiceCreate and/or CrudServiceUpdate to handle those methods properly.
Extend CRUDular
REST
To support all features make use of the following:
de.loosetie.crudular.api.meta.MetaModelde.loosetie.crudular.api.rest.hal.HalDocumentde.loosetie.crudular.api.rest.hal.HalLinkde.loosetie.crudular.rest.ProblemDetailsde.loosetie.crudular.api.service.CrudService
Process:
-
Parse key:
part of the URL,
de.loosetie.crudular.api.rest.FromUriUtilcan help -
Parse Payload:
Keep in mind to only respect enabled fields. Check the MetaModel: type -> entity ->
fieldsRestCreate,fieldsRestUpdate -
Validate the key/payload:
Support
jakarta.validation.Validator -
Call the service
-
Create HalDocument:
Add links to self, fields which contain a key and back links: type ->
referencedBySeede.loosetie.crudular.api.rest.ToUriUtil
Repo
Implement the following interfaces to support more frameworks, databases or other infrastructure.
RepoSpecificRepoBindingRepositoryConfigurer