Supports synchronous and async (using kotlin coroutines ) command and query handling, native kotlin implementation, spring-boot, quarkus and koin configurations.
After kediatr-core version 1.0.17 you can use any dependency injection framework by implementing DependencyProvider interface.
kediatR has multiple implementations: kediatR-core, kediatR-spring-starter, kediatR-koin-starter and kediatR-quarkus-starter.
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>kediatr-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>kediatr-spring-starter</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>kediatr-koin-starter</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>com.trendyol</groupId>
<artifactId>kediatr-quarkus-starter</artifactId>
<version>1.1.2</version>
</dependency>
- add kediatr-core dependency to your POM
class ManualDependencyProvider(
private val handlerMap: HashMap<Class<*>, Any>
) : DependencyProvider {
override fun <T> getSingleInstanceOf(clazz: Class<T>): T {
return handlerMap[clazz] as T
}
override fun <T> getSubTypesOf(clazz: Class<T>): Collection<Class<T>> {
return handlerMap
.filter { it.key.interfaces.contains(clazz) }
.map { it.key as Class<T> }
}
}
fun main() {
val handler = HelloCommandHandler()
val handlers: HashMap<Class<*>, Any> = hashMapOf(Pair(HelloCommandHandler::class.java, handler))
val provider = ManuelDependencyProvider(handlers)
val bus: CommandBus = CommandBusBuilder(provider).build()
bus.executeCommand(HelloCommand("hello"))
}
class HelloCommand(val message: String) : Command
class HelloCommandHandler : CommandHandler<HelloCommand> {
override fun handle(command: HelloCommand) {
println(command.message)
}
}
fun main() {
val handler = GetSomeDataQueryHandler()
val handlers: HashMap<Class<*>, Any> = hashMapOf(Pair(GetSomeDataQuery::class.java, handler))
val provider = ManuelDependencyProvider(handlers)
val bus: CommandBus = CommandBusBuilder(provider).build()
val result: String = bus.executeQuery(GetSomeDataQuery(1))
println(result)
}
class GetSomeDataQuery(val id: Int) : Query<String>
class GetSomeDataQueryHandler : QueryHandler<GetSomeDataQuery, String> {
override fun handle(query: GetSomeDataQuery): String {
// you can use properties in the query object to retrieve data from somewhere
// val result = getDataFromSomewhere(query.id)
// return result
return "hello"
}
}
class CommandProcessingPipeline : PipelineBehavior {
override fun <TRequest> preProcess(request: TRequest) {
println("Starting process.")
}
override fun <TRequest> postProcess(request: TRequest) {
println("Ending process.")
}
override fun <TRequest, TException : Exception> handleExceptionProcess(request: TRequest, exception: TException) {
println("Some exception occurred during process. Error: $exception")
}
}
- add kediatr-spring dependency to your POM and enjoy yourself
@Service
class UserService(private val commandBus: CommandBus) {
fun findUser(id: Long) {
return commandBus.executeQuery(GetUserByIdQuery(id))
}
}
class GetUserByIdQuery(private val id: Long) : Query<UserDto>
@Component
class GetUserByIdQueryHandler(private val userRepository: UserRepository) : QueryHandler<GetUserByIdQuery, UserDto> {
fun handle(query: GetUserByIdQuery): UserDto {
val user = userRepository.findById(query.id)
// do some operation on user
return UserDto(user.id, user.name, user.surname)
}
}
class UserService(private val commandBus: CommandBus) {
suspend fun findUser(id: Long) {
return commandBus.executeQueryAsync(GetUserByIdQuery(id))
}
}
class GetUserByIdQuery(private val id: Long) : Query<UserDto>
class GetUserByIdQueryHandler(private val userRepository: UserRepository) : AsyncQueryHandler<GetUserByIdQuery, UserDto> {
suspend fun handleAsync(query: GetUserByIdQuery): UserDto {
val user = userRepository.findByIdAsync(query.id)
// do some operation on user
return UserDto(user.id, user.name, user.surname)
}
}
class AsyncCommandProcessingPipeline : AsyncPipelineBehavior {
override suspend fun <TRequest> preProcess(request: TRequest) {
println("Starting process.")
}
override suspend fun <TRequest> postProcess(request: TRequest) {
println("Ending process.")
}
override suspend fun <TRequest, TException : Exception> handleException(request: TRequest, exception: TException) {
println("Some exception occurred during process. Error: $exception")
}
}
Simply inject kediatr as a singleton dependency with any module and inject handler instances. KediatrKoin.getCommandBus() must be in the same module with at least one Handler to get correct package name for reflection. Please note that this is an experimental release and reflection strategy with koin is a little wonky. Please open a pull request if you think there is a better implementation.
val kediatrModule = module {
single { KediatrKoin.getCommandBus() }
single { GetUserByIdQueryHandler(get()) }
}
class UserService(private val commandBus: CommandBus) {
fun findUser(id: Long) {
return commandBus.executeQuery(GetUserByIdQuery(id))
}
}
class GetUserByIdQuery(private val id: Long) : Query<UserDto>
class GetUserByIdQueryHandler(private val userRepository: UserRepository) : QueryHandler<GetUserByIdQuery, UserDto> {
fun handle(query: GetUserByIdQuery): UserDto {
val user = userRepository.findById(query.id)
// do some operation on user
return UserDto(user.id, user.name, user.surname)
}
}
- Add kediatr-quarkus-starter dependency to your POM
- Quarkus does not index 3rd party libraries unless you explicitly indicate. Add this configuration to application.properties file.
quarkus:
index-dependency:
kediatr:
group-id: com.trendyol
artifact-id: kediatr-quarkus-starter
- Add @Startup annotation for every handler so that kediatr can prepare queries and commands on beginning of the application.
class UserService(private val commandBus: CommandBus) {
fun findUser(id: Long) {
return commandBus.executeQuery(GetUserByIdQuery(id))
}
}
class GetUserByIdQuery(private val id: Long) : Query<UserDto>
@ApplicationScoped
@Startup
class GetUserByIdQueryHandler(private val userRepository: UserRepository) : QueryHandler<GetUserByIdQuery, UserDto> {
fun handle(query: GetUserByIdQuery): UserDto {
val user = userRepository.findById(query.id)
// do some operation on user
return UserDto(user.id, user.name, user.surname)
}
}