Skip to content

Commit ba2802e

Browse files
committed
Add English README for Mediator library with detailed usage instructions and examples
1 parent 53469c5 commit ba2802e

2 files changed

Lines changed: 314 additions & 0 deletions

File tree

README.en.md

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
# Mediator
2+
3+
A lightweight Java Mediator library for CQRS scenarios, supporting unified handling of `Command`, `Query`, and `Event` with middleware pipelines, message validation, and event parallel dispatch strategies.
4+
5+
[![Maven Central](https://img.shields.io/maven-central/v/com.neroyun/mediator)](https://central.sonatype.com/artifact/com.neroyun/mediator)
6+
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/NerosoftDev/Mediator/blob/master/LICENSE)
7+
8+
## Overview
9+
10+
`Mediator` decouples message senders from their handlers using a message-driven approach:
11+
12+
- **`Command`** — Triggers an action (typically no return value)
13+
- **`Query<R>`** — Requests data and returns a result of type `R`
14+
- **`Event`** — Publishes a notification that can be handled by multiple subscribers
15+
16+
The default implementation is `PipelinedMediator`, which provides:
17+
18+
- Automatic handler resolution by message type (`Handler<T, R>`)
19+
- Middleware pipeline support (`Middleware`)
20+
- Message validation (`Validator<T>`), throwing `ValidationException` on failure
21+
- Event parallel dispatch strategies (`HandlerParallelStrategy`)
22+
- Event exception handling strategies (`HandlerExceptionStrategy`)
23+
24+
## Requirements
25+
26+
- Java 17+
27+
- Maven
28+
29+
## Installation
30+
31+
Add the following dependency to your `pom.xml`:
32+
33+
```xml
34+
<dependency>
35+
<groupId>com.neroyun</groupId>
36+
<artifactId>mediator</artifactId>
37+
<version>${VERSION}</version>
38+
</dependency>
39+
```
40+
41+
## Quick Start
42+
43+
### 1. Define a Command and its Handler
44+
45+
```java
46+
public record UserCreateCommand(String name, String email) implements Command {}
47+
48+
public class UserCreateCommandHandler implements Handler<UserCreateCommand, Void> {
49+
@Override
50+
public Void handle(UserCreateCommand message) {
51+
System.out.println("Creating user: " + message.email());
52+
return null;
53+
}
54+
}
55+
```
56+
57+
### 2. (Optional) Define a Validator
58+
59+
```java
60+
public class UserCreateCommandValidator implements Validator<UserCreateCommand> {
61+
@Override
62+
public ValidationResult validate(UserCreateCommand message) {
63+
if (message.name() == null || message.name().isBlank()) {
64+
return ValidationResult.failure("Name is required");
65+
}
66+
if (message.email() == null || !message.email().contains("@")) {
67+
return ValidationResult.failure("Email is invalid");
68+
}
69+
return ValidationResult.success();
70+
}
71+
}
72+
```
73+
74+
### 3. Assemble the Mediator
75+
76+
```java
77+
Mediator mediator = new PipelinedMediator()
78+
.use(() -> Stream.of(new UserCreateCommandHandler()))
79+
.use(() -> Stream.of(new UserCreateCommandValidator()))
80+
.use(() -> Stream.of(
81+
(message, next) -> {
82+
long start = System.nanoTime();
83+
try {
84+
return next.invoke();
85+
} finally {
86+
long cost = System.nanoTime() - start;
87+
System.out.println("Handled " + message.getClass().getSimpleName() + " in " + cost + " ns");
88+
}
89+
}
90+
));
91+
```
92+
93+
### 4. Send a Message
94+
95+
```java
96+
mediator.send(new UserCreateCommand("Alice", "alice@example.com"));
97+
```
98+
99+
If validation fails, a `ValidationException` is thrown. Use `getErrors()` to retrieve the list of error messages.
100+
101+
---
102+
103+
## Middleware
104+
105+
`Middleware` intercepts messages before or after they reach a `Handler`. Typical use cases include logging, performance monitoring, authentication, auditing, and distributed tracing.
106+
107+
### Middleware Interface
108+
109+
`Middleware` is a `@FunctionalInterface`:
110+
111+
```java
112+
@FunctionalInterface
113+
public interface Middleware {
114+
Object handle(Message message, MiddlewareDelegate next);
115+
}
116+
```
117+
118+
- `message` — The message currently being processed
119+
- `next` — Invokes the next middleware or the final handler in the chain
120+
121+
### Registering Middleware
122+
123+
Pass middleware via `.use(() -> Stream.of(...))` when building `PipelinedMediator`:
124+
125+
```java
126+
Mediator mediator = new PipelinedMediator()
127+
.use(() -> Stream.of(new UserCreateCommandHandler()))
128+
.use(() -> Stream.of(new UserCreateCommandValidator()))
129+
.use(() -> Stream.of(
130+
(message, next) -> {
131+
System.out.println("Before: " + message.getClass().getSimpleName());
132+
try {
133+
return next.invoke();
134+
} finally {
135+
System.out.println("After: " + message.getClass().getSimpleName());
136+
}
137+
}
138+
));
139+
```
140+
141+
### Execution Order
142+
143+
Middleware forms a chain in registration order:
144+
145+
1. The first registered middleware executes first
146+
2. Calling `next.invoke()` passes control to the next middleware
147+
3. Finally, the matching `Handler` is invoked
148+
4. After the handler returns, each middleware continues its post-processing in reverse order
149+
150+
### Common Patterns
151+
152+
#### Logging and Timing
153+
154+
```java
155+
(message, next) -> {
156+
long start = System.nanoTime();
157+
try {
158+
return next.invoke();
159+
} finally {
160+
System.out.println("Elapsed (ns): " + (System.nanoTime() - start));
161+
}
162+
}
163+
```
164+
165+
#### Pre-condition / Authorization Check
166+
167+
```java
168+
(message, next) -> {
169+
if (message == null) {
170+
throw new IllegalArgumentException("Message must not be null");
171+
}
172+
return next.invoke();
173+
}
174+
```
175+
176+
---
177+
178+
## Event Parallel Dispatch Strategies
179+
180+
Annotate your event class to control how its handlers are dispatched:
181+
182+
```java
183+
@HandlerParallelStrategy(HandlerParallelStrategy.WHEN_ALL)
184+
@HandlerExceptionStrategy(HandlerExceptionStrategy.CONTINUE)
185+
public class UserCreatedEvent implements Event {}
186+
```
187+
188+
### `@HandlerParallelStrategy`
189+
190+
| Value | Description |
191+
|-------|-------------|
192+
| `NO_WAIT` *(default)* | Dispatches handlers asynchronously without waiting for completion (fire-and-forget) |
193+
| `WHEN_ALL` | Waits for all handlers to complete before returning |
194+
| `WHEN_ANY` | Waits until any one handler completes, then continues |
195+
196+
### `@HandlerExceptionStrategy`
197+
198+
| Value | Description |
199+
|-------|-------------|
200+
| `CONTINUE` *(default)* | Collects exceptions from all handlers and throws an `AggregateException` at the end |
201+
| `STOP` | Stops processing immediately when any handler throws an exception |
202+
203+
---
204+
205+
## Spring Boot Integration
206+
207+
This library has no dependency on Spring. To integrate it into a Spring Boot application, wire a `PipelinedMediator` bean in a `@Configuration` class.
208+
209+
### Register Handlers, Validators, and Middlewares as Spring Beans
210+
211+
```java
212+
@Component
213+
public class UserCreateCommandHandler implements Handler<UserCreateCommand, Void> {
214+
@Override
215+
public Void handle(UserCreateCommand message) {
216+
// business logic
217+
return null;
218+
}
219+
}
220+
221+
@Component
222+
public class UserCreateCommandValidator implements Validator<UserCreateCommand> {
223+
@Override
224+
public ValidationResult validate(UserCreateCommand message) {
225+
if (message.name() == null || message.name().isBlank()) {
226+
return ValidationResult.failure("Name is required");
227+
}
228+
return ValidationResult.success();
229+
}
230+
}
231+
```
232+
233+
### Assemble the Mediator Bean
234+
235+
```java
236+
import com.neroyun.mediator.*;
237+
import org.springframework.context.ApplicationContext;
238+
import org.springframework.context.annotation.Bean;
239+
import org.springframework.context.annotation.Configuration;
240+
import java.util.concurrent.Executors;
241+
242+
@Configuration
243+
public class MediatorConfiguration {
244+
245+
@Bean
246+
public Mediator mediator(ApplicationContext applicationContext) {
247+
return new PipelinedMediator()
248+
.use(() -> applicationContext.getBeansOfType(Handler.class).values().stream())
249+
.use(() -> applicationContext.getBeansOfType(Validator.class).values().stream())
250+
.use(() -> applicationContext.getBeansOfType(Middleware.class).values().stream())
251+
.use(() -> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()));
252+
}
253+
}
254+
```
255+
256+
### Inject into a Service
257+
258+
```java
259+
import com.neroyun.mediator.Mediator;
260+
import org.springframework.stereotype.Service;
261+
262+
@Service
263+
public class UserApplicationService {
264+
private final Mediator mediator;
265+
266+
public UserApplicationService(Mediator mediator) {
267+
this.mediator = mediator;
268+
}
269+
270+
public void createUser(String name, String email) {
271+
mediator.send(new UserCreateCommand(name, email));
272+
}
273+
}
274+
```
275+
276+
> **Notes:**
277+
> - Handlers are matched automatically by their generic message type
278+
> - Multiple middlewares form a chain in stream order
279+
> - When a `Validator` returns a failure, a `ValidationException` is thrown — catch it in a global exception handler (e.g., `@ControllerAdvice`) to return a proper HTTP error response
280+
281+
---
282+
283+
## Package Structure
284+
285+
| Package | Contents |
286+
|---------|----------|
287+
| `com.neroyun.mediator` | Core abstractions: `Mediator`, `Command`, `Query`, `Event`; extension points: `Handler`, `Middleware`, `Validator`; default implementation: `PipelinedMediator` |
288+
| `com.neroyun.mediator.strategy` | Event parallel and exception strategy annotations |
289+
| `com.neroyun.mediator.validation` | `ValidationResult`, `ValidationException` |
290+
| `com.neroyun.mediator.internal` | Internal support types (message base, stream suppliers, exception aggregation, etc.) |
291+
292+
---
293+
294+
## Building
295+
296+
```bash
297+
mvn clean test
298+
```
299+
300+
Ensure your local JDK version matches the `maven.compiler.release` setting in `pom.xml` (currently Java 17).
301+
302+
---
303+
304+
## License
305+
306+
This project is licensed under the [GNU General Public License v3.0](https://github.com/NerosoftDev/Mediator/blob/master/LICENSE).

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
一个轻量级的 Java Mediator 组件,用于在 CQRS 场景下统一处理 `Command``Query``Event`,并支持中间件管道、消息验证和事件并行策略。
44

5+
[![Maven Central](https://img.shields.io/maven-central/v/com.neroyun/mediator)](https://central.sonatype.com/artifact/com.neroyun/mediator)
6+
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://github.com/NerosoftDev/Mediator/blob/master/LICENSE)
7+
58
## 项目简介
69

710
`Mediator` 通过“消息 + 处理器”的模式解耦业务调用方与实现方:
@@ -18,6 +21,11 @@
1821
- 支持事件并行分发策略(`HandlerParallelStrategy`
1922
- 支持事件异常处理策略(`HandlerExceptionStrategy`
2023

24+
## 环境要求
25+
26+
- Java 17+
27+
- Maven
28+
2129
## 依赖与环境
2230

2331
项目为 Maven 工程(见 `pom.xml`):

0 commit comments

Comments
 (0)