Skip to content

Commit ce8167a

Browse files
author
Daniel Bustamante Ospina
committed
Merge remote-tracking branch 'origin/master'
# Conflicts: # .gitignore
2 parents 3b69781 + 1ce598e commit ce8167a

File tree

8 files changed

+3101
-0
lines changed

8 files changed

+3101
-0
lines changed

.gitignore

Lines changed: 640 additions & 0 deletions
Large diffs are not rendered by default.

docs/asciidoc/api-guide.adoc

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
== Reactive Commons API
2+
3+
[[api-guide-overview]]
4+
=== Overview
5+
6+
This section describes the reactive API for producing and consuming messages using Reactive Commons.
7+
There are three main classes in Reactive Commons:
8+
9+
. `HandlerRegistry` for listening to events and commands messages and for registering their respective handlers.
10+
. `DomainEventBus` for emiting events to an event bus
11+
. `DirectAsyncGateway` for emiting commands to an event bus
12+
13+
The project uses https://github.com/reactor/reactor-core[Reactor Core] to expose a https://github.com/reactive-streams/reactive-streams-jvm["Reactive Streams"] API.
14+
15+
=== Semantic Messages Classes
16+
17+
Reactice Commons has 3 classes that represent events, commands or queries, giving a semantic meaning for a message. So, let's talk about DomainEvent and Command classes .
18+
19+
==== DomainEvent<T>
20+
21+
:This class lets you represent a Event in the system. It accepts a generic class that will be the information to transport for that event, a eventId and a name for the event. The structure for a DomainEvent is
22+
23+
[source,java]
24+
--------
25+
package org.reactivecommons.api.domain;
26+
27+
public class DomainEvent<T> {
28+
private final String name;
29+
private final String eventId;
30+
private final T data;
31+
32+
public DomainEvent(String name, String eventId, T data) {
33+
this.name = name;
34+
this.eventId = eventId;
35+
this.data = data;
36+
}
37+
38+
//... getters, equals, hascode, toString impl..
39+
40+
}
41+
--------
42+
43+
==== Command<T>
44+
45+
An other basic structure is the Command class. This class lets you represent a Command in the system. It accepts a generic class that will be the information for that command, a commandId and a name for that event. The structure for a Command is:
46+
47+
[source,java]
48+
--------
49+
package org.reactivecommons.api.domain;
50+
51+
52+
import lombok.AllArgsConstructor;
53+
import lombok.Data;
54+
55+
@Data
56+
@AllArgsConstructor
57+
public class Command<T> {
58+
private final String name;
59+
private final String commandId;
60+
private final T data;
61+
}
62+
--------
63+
64+
==== AsyncQuery<T>
65+
66+
An other basic structure is the AsyncQuery class. This class lets you represent a Query in the system. It accepts a generic class that will be the information for that query and a name for that resoruce. The structure is:
67+
68+
69+
[source,java]
70+
--------
71+
@Data
72+
public class AsyncQuery<T> {
73+
private final String resource;
74+
private final T queryData;
75+
}
76+
--------
77+
78+
=== Reactive Commons - Sending Events, Commands and Request/Reply messages
79+
80+
Outbound messages are sent to an event bus using `DomainEventBus` or `DirectAsyncGateway` classes. If you are using Spring Boot, you can have a Main class like this:
81+
82+
[source,java]
83+
--------
84+
import org.reactivecommons.async.impl.config.annotations.EnableDomainEventBus;
85+
import org.springframework.boot.SpringApplication;
86+
import org.springframework.boot.autoconfigure.SpringBootApplication;
87+
88+
@SpringBootApplication
89+
@EnableDomainEventBus
90+
@EnableDirectAsyncGateway
91+
public class MainApplication {
92+
public static void main(String[] args) {
93+
SpringApplication.run(MainApplication.class, args);
94+
}
95+
96+
@Bean
97+
public ManageTasksUseCase manageTasksUseCase(TaskToDoRepository tasks, DomainEventBus eventBus) {
98+
return new ManageTasksUseCase(tasks, eventBus);
99+
}
100+
101+
}
102+
--------
103+
104+
==== DomainEventBus
105+
106+
The @EnableDomainEventBus annotation enable to the application to emit Events to the System. This annotation create a EnableDomainEventBus bean, so you can use it for emitting events. This interface looks like:
107+
108+
[source,java]
109+
--------
110+
package org.reactivecommons.api.domain;
111+
112+
import org.reactivestreams.Publisher;
113+
114+
public interface DomainEventBus {
115+
<T> Publisher<Void> emit(DomainEvent<T> event);
116+
}
117+
--------
118+
119+
The emit method recive a DomainEvent<T> class where you can publish information to the system. The method will respond you in a reacive way with a Publisher, like a mono object. So, for example, if you want to send a UserRegistered event to the system ,you can do this:
120+
121+
[source,java]
122+
--------
123+
124+
public class AnyUseCase {
125+
126+
private DomainEventBus eventBus; // Injected
127+
128+
private Mono<Void> emitCreatedEvent(UserRegistered event) {
129+
return Mono.from(eventBus.emit(new DomainEvent<>("user.registered", uuid(), event)));
130+
}
131+
//...
132+
}
133+
--------
134+
135+
==== DirectAsyncGateway - Commands
136+
137+
If you want to send Commands to the system, the @EnableDirectAsyncGateway annotation enable to the application to emit Commands to the System. This annotation create a DirectAsyncGateway bean, so you can use it for emitting commands. This interface looks like:
138+
139+
[source,java]
140+
--------
141+
public interface DirectAsyncGateway {
142+
<T> Mono<Void> sendCommand(Command<T> command, String targetName);
143+
<T, R> Mono<R> requestReply(AsyncQuery<T> query, String targetName, Class<R> type);
144+
}
145+
--------
146+
147+
The sendCommand method recive a Command<T> class where you can publish information to the system. The method will respond you in a reacive way with a Publisher, like a mono object. So, for example, if you want to send a UserRegister command to the "target.name" component ,you can do this:
148+
149+
[source,java]
150+
--------
151+
152+
public class AnyUseCase {
153+
154+
private DirectAsyncGateway gateway; // Injected
155+
156+
private Mono<Void> emitCreatedEvent(UserRegister command) {
157+
return gateway.sendCommand(new Command<>("user.register", uuid(), command), "target.name") // Continue reactive flow
158+
}
159+
//...
160+
}
161+
--------
162+
163+
The second parameter for sendCommand method is the name of the target component for that command, It's the name stablished in the properties file of Spring "application.properties" in the "spring.application.name" field.
164+
165+
[NOTE]
166+
you don't need this parameter in the emit method of DomainEventBus class, because an event is a fact for cero or more subscribers.
167+
168+
==== DirectAsyncGateway - Request/Reply
169+
170+
The DirectAsyncGateway class has another method called "requestReply", this method lets you to send a query and wait for an answer for that query. The method will respond you in a reacive way with a Publisher, like a mono object with the generic data. So, for example, if you want to send a query with QueryUser data, to the "target.name" component and recive an User object response, you can do this:
171+
172+
[source,java]
173+
--------
174+
175+
public class AnyUseCase {
176+
177+
private DirectAsyncGateway gateway; // Injected
178+
179+
private Mono<User> query(QueryUser query) {
180+
return gateway.requestReply(new AsyncQuery<>("query.name", query), "target.name", User.class);
181+
}
182+
//...
183+
}
184+
--------
185+
186+
=== Reactive Commons - Listening for Events, Commands and Query messages
187+
188+
==== HandlerRegistry
189+
190+
Inbound messages are listened from an event bus using `HandlerRegistry` class. The @EnableMessageListeners annotation enable you to listen messages like Events, Commands or Queries. You have to create a HandlerRegistry object, so you can register handlers for specifc messages.
191+
192+
[source,java]
193+
--------
194+
@SpringBootApplication
195+
@EnableMessageListeners
196+
public class MainApplication {
197+
...
198+
}
199+
--------
200+
201+
The HandlerRegistry implements builder patter, so each time you use some method, it will return a HanlderRegistry object. HanlderRegistry has the following methods:
202+
203+
* listenEvent: It lets listen for an event
204+
* serveQuery: It lets listen for a query
205+
* handleCommand: It lets listen for a command
206+
207+
===== HandlerRegistry - listenEvent
208+
209+
listenEvent method lets you register a handler for a specific event. It has the next signature:
210+
211+
[source,java]
212+
--------
213+
HandlerRegistry listenEvent(String eventName, EventHandler<T> fn, Class<T> eventClass)
214+
--------
215+
216+
Where the EventHandler interface signature is:
217+
218+
[source,java]
219+
--------
220+
public interface EventHandler<T> extends GenericHandler<Void, DomainEvent<T>> {
221+
}
222+
--------
223+
224+
[NOTE]
225+
The return type of the EventHandler is Void
226+
227+
So, for example, if your application want react to user.registered event, you can define a handler for that event like this:
228+
229+
[source,java]
230+
--------
231+
@Configuration
232+
public class SomeConfigurationClass {
233+
234+
@Autowired
235+
private ManageTasksUseCase someBusinessDependency;
236+
237+
@Bean
238+
public HandlerRegistry eventMessages() {
239+
return HandlerRegistry.register()
240+
.listenEvent("user.registered", event -> someBusinessDependency.functionReturningMonoVoid(event), UserRegistered.class)
241+
}
242+
}
243+
--------
244+
245+
===== HandlerRegistry - handleCommand
246+
247+
handleCommand method lets you register a handler for a specific command. It has the next signature:
248+
249+
[source,java]
250+
--------
251+
HandlerRegistry handleCommand(String commandName, CommandHandler<T> fn, Class<T> commandClass)
252+
--------
253+
254+
Where the CommandHandler interface signature is:
255+
256+
[source,java]
257+
--------
258+
public interface CommandHandler<T> extends GenericHandler<Void, Command<T>> {
259+
}
260+
--------
261+
[NOTE]
262+
The return type of the CommandHandler is Void
263+
264+
So, for example, if your application want react to user.register command, you can define a handler for that command like this:
265+
266+
[source,java]
267+
--------
268+
@Bean
269+
public HandlerRegistry commandMessages() {
270+
return HandlerRegistry.register()
271+
.handleCommand("user.register", cmd -> someBusinessDependency.handleCommand(cmd), UserRegister.class);
272+
}
273+
--------
274+
275+
===== HandlerRegistry - serveQuery
276+
277+
serveQuery method lets you register a handler for a specific query. It has the next signature:
278+
279+
[source,java]
280+
--------
281+
HandlerRegistry serveQuery(String resource, QueryHandler<T, R> handler, Class<R> queryClass)
282+
--------
283+
284+
Where the QueryHandler interface signature is:
285+
286+
[source,java]
287+
--------
288+
public interface QueryHandler<T, C> extends GenericHandler<T, C> {
289+
}
290+
--------
291+
[NOTE]
292+
The return type of the QueryHandler is a generic C
293+
294+
For example, if your application want react to user.information query, you can define a handler for that query like this:
295+
296+
[source,java]
297+
--------
298+
@Bean
299+
public HandlerRegistry queryMessages() {
300+
return HandlerRegistry.register()
301+
.serveQuery("user.information", query -> someBusinessDependency.findSomething(query), SomeQuery.class);
302+
}
303+
--------
304+
305+
TIP: Remember HandlerRegistry use builder pattern, So, you can build a chain of listener for each message:
306+
307+
[source,java]
308+
--------
309+
@Configuration
310+
public class SomeConfigurationClass {
311+
312+
@Autowired
313+
private ManageTasksUseCase someBusinessDependency;
314+
315+
@Bean
316+
public HandlerRegistry notificationEvents() {
317+
return HandlerRegistry.register()
318+
.listenNotificationEvent("some.event.name", event -> someBusinessDependency.someFunctionReturningMonoVoid(event), SomeClass.class)
319+
.listenEvent("some.event.name2", event -> someBusinessDependency.functionReturningMonoVoid(event), Some.class)
320+
.serveQuery("query.name", query -> someBusinessDependency.findSomething(query), SomeQuery.class)
321+
.handleCommand("command.name", cmd -> someBusinessDependency.handleCommand(cmd), CmdClass.class);
322+
}
323+
}
324+
--------

0 commit comments

Comments
 (0)