Skip to content

Commit c25ca3d

Browse files
authored
unify query and command handling under requestHandling (#452)
1 parent 51a8e67 commit c25ca3d

File tree

29 files changed

+671
-709
lines changed

29 files changed

+671
-709
lines changed

MIGRATION_GUIDE.md

Lines changed: 243 additions & 68 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,16 @@ Documentation is available at [https://trendyol.github.io/kediatR/](https://tren
1515
## Show me the code
1616

1717
```kotlin
18-
class PingCommand : Command.Unit // or
19-
class PingQuery : Query<String> // or
18+
class PingCommand : Request.Unit // or
19+
class PingQuery : Request<String> // or
2020
class PingNotification : Notification
21-
class PingCommandHandler : CommandHandler.Unit<PingCommand> {
22-
override suspend fun handle(command: PingCommand) {
21+
22+
class PingCommandHandler : RequestHandler.Unit<PingCommand> {
23+
override suspend fun handle(command: PingCommand) : Unit {
2324
println("Pong!")
2425
}
2526
}
26-
class PingQueryHandler : QueryHandler<PingQuery, String> {
27+
class PingQueryHandler : RequestHandler<PingQuery, String> {
2728
override suspend fun handle(query: PingQuery): String {
2829
return "Pong!"
2930
}

projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/CommandHandler.kt

Lines changed: 0 additions & 77 deletions
This file was deleted.

projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Container.kt

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ package com.trendyol.kediatr
66
* This class extends Registrar and acts as a central registry that discovers and organizes
77
* all handlers and behaviors from the dependency provider. It maintains separate collections
88
* for different types of handlers:
9-
* - Query handlers (one-to-one mapping)
10-
* - Command handlers (one-to-one mapping)
9+
* - Request handlers (one-to-one mapping for both queries and commands)
1110
* - Notification handlers (one-to-many mapping)
1211
* - Pipeline behaviors (set of unique behaviors)
1312
*
@@ -16,8 +15,7 @@ package com.trendyol.kediatr
1615
*
1716
* @param dependencyProvider The dependency provider used to discover and resolve handlers
1817
* @see Registrar
19-
* @see QueryProvider
20-
* @see CommandProvider
18+
* @see RequestProvider
2119
* @see NotificationProvider
2220
* @see PipelineProvider
2321
*/
@@ -26,10 +24,10 @@ internal class Container(
2624
dependencyProvider: DependencyProvider
2725
) : Registrar() {
2826
/**
29-
* Map storing query handlers where the key is the query class and the value is the query provider.
30-
* Each query type should have exactly one handler.
27+
* Map storing request handlers where the key is the request class and the value is the request provider.
28+
* Each request type should have exactly one handler.
3129
*/
32-
val queryMap = HashMap<Class<*>, QueryProvider<QueryHandler<*, *>>>()
30+
val requestHandlerMap = HashMap<Class<*>, RequestProvider<RequestHandler<Request<*>, *>>>()
3331

3432
/**
3533
* Map storing notification handlers where the key is the notification class and the value is a list of providers.
@@ -42,25 +40,9 @@ internal class Container(
4240
*/
4341
val pipelineSet = HashSet<PipelineProvider<*>>()
4442

45-
/**
46-
* Map storing command handlers where the key is the command class and the value is the command provider.
47-
* Each command type should have exactly one handler.
48-
*/
49-
val commandMap = HashMap<Class<*>, CommandProvider<*>>()
50-
5143
init {
52-
// Register query handlers - one handler per query type
53-
registerFor<QueryHandler<Query<*>, *>, Query<*>>(dependencyProvider) { key, value ->
54-
queryMap[key] = QueryProvider(dependencyProvider, value as Class<QueryHandler<*, *>>)
55-
}
56-
57-
// Register command handlers - one handler per command type
58-
registerFor<CommandHandler<Command<*>, *>, Command<*>>(dependencyProvider) { key, value ->
59-
commandMap[key] =
60-
CommandProvider(
61-
dependencyProvider,
62-
value as Class<CommandHandler<*, *>>
63-
)
44+
registerFor<RequestHandler<Request<*>, *>, Request<*>>(dependencyProvider) { key, value ->
45+
requestHandlerMap[key] = RequestProvider(dependencyProvider, value)
6446
}
6547

6648
// Register notification handlers - multiple handlers per notification type allowed

projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/DependencyProvider.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,18 @@ package com.trendyol.kediatr
44
* Dependency provider interface for resolving handler instances and discovering subtypes.
55
*
66
* This interface abstracts the dependency injection mechanism used by the mediator
7-
* to resolve handlers for queries, commands, and notifications. Implementations
7+
* to resolve handlers for requests and notifications. Implementations
88
* should integrate with specific DI frameworks (Spring, Koin, etc.).
99
*
1010
* @see Mediator
11-
* @see QueryHandler
12-
* @see CommandHandler
11+
* @see RequestHandler
1312
* @see NotificationHandler
1413
*/
1514
interface DependencyProvider {
1615
/**
1716
* Gets a single instance of the specified class.
1817
*
19-
* This method is used to resolve handler instances for queries and commands,
18+
* This method is used to resolve handler instances for requests,
2019
* which should have exactly one handler per type.
2120
*
2221
* @param T The type of instance to resolve

projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/HandlerRegistryProvider.kt

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ import java.lang.reflect.ParameterizedType
1515
* Example usage:
1616
* ```kotlin
1717
* val handlers = listOf(
18-
* MyQueryHandler(),
19-
* MyCommandHandler(),
18+
* MyRequestHandler(),
2019
* MyNotificationHandler(),
2120
* LoggingPipelineBehavior()
2221
* )
@@ -62,7 +61,7 @@ class HandlerRegistryProvider(
6261
*
6362
* This method performs sophisticated type checking that handles:
6463
* - Direct class inheritance and interface implementation
65-
* - Generic interface implementations (e.g., QueryHandler<MyQuery, String>)
64+
* - Generic interface implementations (e.g., RequestHandler<MyRequest, String>)
6665
* - Inheritance chains with generic interfaces
6766
* - Complex type hierarchies
6867
*
@@ -106,16 +105,15 @@ class HandlerRegistryProvider(
106105
* a full dependency injection framework.
107106
*
108107
* The handlers list can contain:
109-
* - QueryHandler implementations
110-
* - CommandHandler implementations
108+
* - RequestHandler implementations for queries and commands
111109
* - NotificationHandler implementations
112110
* - PipelineBehavior implementations
113111
*
114112
* Example:
115113
* ```kotlin
116114
* val handlers = listOf(
117-
* GetUserQueryHandler(),
118-
* CreateUserCommandHandler(),
115+
* GetUserRequestHandler(),
116+
* CreateUserRequestHandler(),
119117
* UserCreatedNotificationHandler(),
120118
* LoggingPipelineBehavior()
121119
* )
@@ -128,10 +126,9 @@ class HandlerRegistryProvider(
128126
* ```
129127
*
130128
* @param handlers The handlers to be used by the mediator. Can include any combination of
131-
* QueryHandler, CommandHandler, NotificationHandler, and PipelineBehavior instances
129+
* RequestHandler, NotificationHandler, and PipelineBehavior instances
132130
* @return A configured mediator instance ready for use
133-
* @see QueryHandler
134-
* @see CommandHandler
131+
* @see RequestHandler
135132
* @see NotificationHandler
136133
* @see PipelineBehavior
137134
*/

projects/kediatr-core/src/main/kotlin/com/trendyol/kediatr/Mediator.kt

Lines changed: 20 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,47 @@
11
package com.trendyol.kediatr
22

33
/**
4-
* Mediator interface for sending queries, commands, and notifications.
4+
* Mediator interface for sending requests and publishing notifications.
55
*
66
* The Mediator pattern encapsulates how objects interact with each other, promoting loose coupling
77
* by preventing objects from referring to each other explicitly. This implementation provides
88
* a centralized way to handle:
9-
* - Queries: Request-response operations that return data
10-
* - Commands: Operations that modify state and may return results
9+
* - Requests: Unified interface for both queries and commands that return responses
1110
* - Notifications: Fire-and-forget messages that can have multiple handlers
1211
*
13-
* @see Query
14-
* @see Command
12+
* @see Request
1513
* @see Notification
1614
* @see PublishStrategy
1715
*/
1816
interface Mediator {
1917
/**
20-
* Sends a query and returns the response.
18+
* Sends a request and returns the response.
2119
*
22-
* Queries are typically used for read operations that return data without side effects.
23-
* Each query type should have exactly one handler that processes it.
20+
* Requests represent both queries and commands in a unified way. Each request type
21+
* should have exactly one handler that processes it. Requests are typically used for:
22+
* - Queries: Read operations that return data without side effects
23+
* - Commands: Operations that modify state and may return results
2424
*
25-
* @param TQuery The type of query that extends Query<TResponse>
26-
* @param TResponse The type of response that the query handler will return
27-
* @param query The query instance to send
28-
* @return The response of the query as returned by its handler
29-
* @throws IllegalStateException if no handler is found for the query type
30-
* @throws Exception any exception thrown by the query handler
25+
* @param TResponse The type of response that the request handler will return
26+
* @param TRequest The type of request that extends Request<TResponse>
27+
* @return The response of the request as returned by its handler
28+
* @throws HandlerNotFoundException if no handler is found for the request type
29+
* @throws Exception any exception thrown by the request handler
3130
*/
32-
suspend fun <TQuery : Query<TResponse>, TResponse> send(query: TQuery): TResponse
33-
34-
/**
35-
* Sends a command and returns the result.
36-
*
37-
* Commands are typically used for write operations that modify state.
38-
* Each command type should have exactly one handler that processes it.
39-
*
40-
* @param TCommand The type of command that extends Command<TResult>
41-
* @param TResult The type of result that the command handler will return
42-
* @param command The command instance to send
43-
* @return The result of the command as returned by its handler
44-
* @throws IllegalStateException if no handler is found for the command type
45-
* @throws Exception any exception thrown by the command handler
46-
*/
47-
suspend fun <TCommand : Command<TResult>, TResult> send(command: TCommand): TResult
31+
suspend fun <TRequest : Request<TResponse>, TResponse> send(request: TRequest): TResponse
4832

4933
/**
5034
* Publishes a notification using the specified publish strategy.
5135
*
5236
* Notifications are fire-and-forget messages that can have zero, one, or multiple handlers.
53-
* The publish strategy determines how multiple handlers are executed (sequentially, in parallel, etc.)
37+
* The publishing strategy determines how multiple handlers are executed (sequentially, in parallel, etc.)
5438
* and how exceptions are handled.
5539
*
5640
* @param T The type of notification that extends Notification
5741
* @param notification The notification instance to publish
5842
* @param publishStrategy The strategy to use for publishing the notification.
5943
* Defaults to [PublishStrategy.DEFAULT] which stops on first exception.
60-
* @throws Exception depending on the publish strategy:
44+
* @throws Exception depending on the publishing strategy:
6145
* - DEFAULT/StopOnException: throws the first exception encountered
6246
* - ContinueOnException: throws AggregateException if any handlers failed
6347
* - Parallel strategies: may throw exceptions from handlers
@@ -73,18 +57,17 @@ interface Mediator {
7357
/**
7458
* Creates a new Mediator instance with the provided dependency provider.
7559
*
76-
* The dependency provider is used to resolve handlers for queries, commands, and notifications.
60+
* The dependency provider is used to resolve handlers for requests and notifications.
7761
* It should be configured to provide instances of:
78-
* - QueryHandler implementations for each query type
79-
* - CommandHandler implementations for each command type
62+
* - RequestHandler implementations for each request type (queries and commands)
8063
* - NotificationHandler implementations for each notification type
64+
* - PipelineBehavior implementations for cross-cutting concerns
8165
*
8266
* @param dependencyProvider The dependency provider that will resolve handler instances
8367
* @return A configured Mediator instance ready for use
8468
*
8569
* @see DependencyProvider
86-
* @see QueryHandler
87-
* @see CommandHandler
70+
* @see RequestHandler
8871
* @see NotificationHandler
8972
*/
9073
fun build(dependencyProvider: DependencyProvider): Mediator = MediatorImpl(RegistryImpl(dependencyProvider))

0 commit comments

Comments
 (0)