#113 Event Driven Architecture
Adds various changes including : - Fixes to Javadoc - Test refactoring and improvements - Refactored EventDispatcher to be immutable - Removed DynamicRouter interface since it not needed - Renamed Channel to a more appropriate name - Handler
This commit is contained in:
parent
9e857d7dd6
commit
cfb0fafc7d
@ -30,4 +30,3 @@ permalink: /patterns/event-driven-architecture
|
|||||||
* [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html)
|
* [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html)
|
||||||
* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications)
|
* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications)
|
||||||
* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture)
|
* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture)
|
||||||
*
|
|
@ -3,6 +3,7 @@ package com.iluwatar.eda;
|
|||||||
import com.iluwatar.eda.event.Event;
|
import com.iluwatar.eda.event.Event;
|
||||||
import com.iluwatar.eda.event.UserCreatedEvent;
|
import com.iluwatar.eda.event.UserCreatedEvent;
|
||||||
import com.iluwatar.eda.event.UserUpdatedEvent;
|
import com.iluwatar.eda.event.UserUpdatedEvent;
|
||||||
|
import com.iluwatar.eda.framework.EventDispatcher;
|
||||||
import com.iluwatar.eda.handler.UserCreatedEventHandler;
|
import com.iluwatar.eda.handler.UserCreatedEventHandler;
|
||||||
import com.iluwatar.eda.handler.UserUpdatedEventHandler;
|
import com.iluwatar.eda.handler.UserUpdatedEventHandler;
|
||||||
import com.iluwatar.eda.model.User;
|
import com.iluwatar.eda.model.User;
|
||||||
@ -19,7 +20,7 @@ import com.iluwatar.eda.model.User;
|
|||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Once the {@link EventDispatcher} is initialised, channels related to specific events have to be
|
* Once the {@link EventDispatcher} is initialised, handlers related to specific events have to be
|
||||||
* made known to the dispatcher by registering them. In this case the {@link UserCreatedEvent} is
|
* made known to the dispatcher by registering them. In this case the {@link UserCreatedEvent} is
|
||||||
* bound to the UserCreatedEventHandler, whilst the {@link UserUpdatedEvent} is bound to the
|
* bound to the UserCreatedEventHandler, whilst the {@link UserUpdatedEvent} is bound to the
|
||||||
* {@link UserUpdatedEventHandler}. The dispatcher can now be called to dispatch specific events.
|
* {@link UserUpdatedEventHandler}. The dispatcher can now be called to dispatch specific events.
|
||||||
@ -34,8 +35,8 @@ public class App {
|
|||||||
dispatcher.registerChannel(UserUpdatedEvent.class, new UserUpdatedEventHandler());
|
dispatcher.registerChannel(UserUpdatedEvent.class, new UserUpdatedEventHandler());
|
||||||
|
|
||||||
User user = new User("iluwatar");
|
User user = new User("iluwatar");
|
||||||
dispatcher.dispatch(new UserCreatedEvent(user));
|
dispatcher.onEvent(new UserCreatedEvent(user));
|
||||||
dispatcher.dispatch(new UserUpdatedEvent(user));
|
dispatcher.onEvent(new UserUpdatedEvent(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
package com.iluwatar.eda;
|
|
||||||
|
|
||||||
import com.iluwatar.eda.event.Event;
|
|
||||||
import com.iluwatar.eda.framework.Channel;
|
|
||||||
import com.iluwatar.eda.framework.DynamicRouter;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the routing of {@link Event} messages to associated channels.
|
|
||||||
* A {@link HashMap} is used to store the association between events and their respective handlers.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class EventDispatcher implements DynamicRouter<Event> {
|
|
||||||
|
|
||||||
private Map<Class<? extends Event>, Channel<?>> handlers;
|
|
||||||
|
|
||||||
public EventDispatcher() {
|
|
||||||
handlers = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Links an {@link Event} to a specific {@link Channel}.
|
|
||||||
*
|
|
||||||
* @param contentType The {@link Event} to be registered
|
|
||||||
* @param channel The {@link Channel} that will be handling the {@link Event}
|
|
||||||
*/
|
|
||||||
public void registerChannel(Class<? extends Event> contentType,
|
|
||||||
Channel<?> channel) {
|
|
||||||
handlers.put(contentType, channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dispatches an {@link Event} depending on it's type.
|
|
||||||
*
|
|
||||||
* @param content The {@link Event} to be dispatched
|
|
||||||
*/
|
|
||||||
public void dispatch(Event content) {
|
|
||||||
handlers.get(content.getClass()).dispatch(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a map of registered event handlers.
|
|
||||||
* @return {@link Map} of registered event handlers.
|
|
||||||
*/
|
|
||||||
public Map<Class<? extends Event>, Channel<?>> getHandlers() {
|
|
||||||
return handlers;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
package com.iluwatar.eda.event;
|
package com.iluwatar.eda.event;
|
||||||
|
|
||||||
|
import com.iluwatar.eda.framework.EventDispatcher;
|
||||||
import com.iluwatar.eda.framework.Message;
|
import com.iluwatar.eda.framework.Message;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,8 +16,8 @@ public class Event implements Message {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the event type as a {@link Class} object
|
* Returns the event type as a {@link Class} object
|
||||||
* In this example, this method is used by the {@link com.iluwatar.eda.EventDispatcher} to
|
* In this example, this method is used by the {@link EventDispatcher} to
|
||||||
* dispatch events depending on their type.
|
* onEvent events depending on their type.
|
||||||
*
|
*
|
||||||
* @return the Event type as a {@link Class}.
|
* @return the Event type as a {@link Class}.
|
||||||
*/
|
*/
|
||||||
|
@ -3,7 +3,7 @@ package com.iluwatar.eda.event;
|
|||||||
import com.iluwatar.eda.model.User;
|
import com.iluwatar.eda.model.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link UserCreatedEvent} class should should be dispatched whenever a user has been created.
|
* The {@link UserCreatedEvent} should should be dispatched whenever a user has been created.
|
||||||
* This class can be extended to contain details about the user has been created. In this example,
|
* This class can be extended to contain details about the user has been created. In this example,
|
||||||
* the entire {@link User} object is passed on as data with the event.
|
* the entire {@link User} object is passed on as data with the event.
|
||||||
*/
|
*/
|
||||||
|
@ -3,7 +3,7 @@ package com.iluwatar.eda.event;
|
|||||||
import com.iluwatar.eda.model.User;
|
import com.iluwatar.eda.model.User;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link UserUpdatedEvent} class should should be dispatched whenever a user has been updated.
|
* The {@link UserUpdatedEvent} should should be dispatched whenever a user has been updated.
|
||||||
* This class can be extended to contain details about the user has been updated. In this example,
|
* This class can be extended to contain details about the user has been updated. In this example,
|
||||||
* the entire {@link User} object is passed on as data with the event.
|
* the entire {@link User} object is passed on as data with the event.
|
||||||
*/
|
*/
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package com.iluwatar.eda.framework;
|
|
||||||
|
|
||||||
import com.iluwatar.eda.event.Event;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Channels are delivery points for messages. Every {@link Channel} is responsible for a single type
|
|
||||||
* of message
|
|
||||||
*/
|
|
||||||
public interface Channel<E extends Message> {
|
|
||||||
void dispatch(Event message);
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package com.iluwatar.eda.framework;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link DynamicRouter} is responsible for selecting the proper path of a {@link Message}
|
|
||||||
* Messages can be associated to Channels through the registerChannel method and dispatched by
|
|
||||||
* calling the dispatch method.
|
|
||||||
*/
|
|
||||||
public interface DynamicRouter<E extends Message> {
|
|
||||||
|
|
||||||
void registerChannel(Class<? extends E> contentType, Channel<?> channel);
|
|
||||||
|
|
||||||
void dispatch(E content);
|
|
||||||
}
|
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.iluwatar.eda.framework;
|
||||||
|
|
||||||
|
import com.iluwatar.eda.event.Event;
|
||||||
|
import com.iluwatar.eda.framework.Handler;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the routing of {@link Event} messages to associated handlers.
|
||||||
|
* A {@link HashMap} is used to store the association between events and their respective handlers.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class EventDispatcher {
|
||||||
|
|
||||||
|
private Map<Class<? extends Event>, Handler<?>> handlers;
|
||||||
|
|
||||||
|
public EventDispatcher() {
|
||||||
|
handlers = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Links an {@link Event} to a specific {@link Handler}.
|
||||||
|
*
|
||||||
|
* @param eventType The {@link Event} to be registered
|
||||||
|
* @param handler The {@link Handler} that will be handling the {@link Event}
|
||||||
|
*/
|
||||||
|
public void registerChannel(Class<? extends Event> eventType,
|
||||||
|
Handler<?> handler) {
|
||||||
|
handlers.put(eventType, handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches an {@link Event} depending on it's type.
|
||||||
|
*
|
||||||
|
* @param event The {@link Event} to be dispatched
|
||||||
|
*/
|
||||||
|
public void onEvent(Event event) {
|
||||||
|
handlers.get(event.getClass()).onEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.iluwatar.eda.framework;
|
||||||
|
|
||||||
|
import com.iluwatar.eda.event.Event;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface can be implemented to handle different types of messages.
|
||||||
|
* Every handler is responsible for a single of type message
|
||||||
|
*/
|
||||||
|
public interface Handler<E extends Message> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The onEvent method should implement and handle behavior related to the event.
|
||||||
|
* This can be as simple as calling another service to handle the event on publishing the event on
|
||||||
|
* a queue to be consumed by other sub systems.
|
||||||
|
* @param event the {@link Event} object to be handled.
|
||||||
|
*/
|
||||||
|
void onEvent(Event event);
|
||||||
|
}
|
@ -2,8 +2,14 @@ package com.iluwatar.eda.framework;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Message} is an object with a specific type that is associated
|
* A {@link Message} is an object with a specific type that is associated
|
||||||
* to a specific {@link Channel}.
|
* to a specific {@link Handler}.
|
||||||
*/
|
*/
|
||||||
public interface Message {
|
public interface Message {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the message type as a {@link Class} object. In this example the message type is
|
||||||
|
* used to handle events by their type.
|
||||||
|
* @return the message type as a {@link Class}.
|
||||||
|
*/
|
||||||
Class<? extends Message> getType();
|
Class<? extends Message> getType();
|
||||||
}
|
}
|
||||||
|
@ -2,16 +2,15 @@ package com.iluwatar.eda.handler;
|
|||||||
|
|
||||||
import com.iluwatar.eda.event.Event;
|
import com.iluwatar.eda.event.Event;
|
||||||
import com.iluwatar.eda.event.UserCreatedEvent;
|
import com.iluwatar.eda.event.UserCreatedEvent;
|
||||||
import com.iluwatar.eda.framework.Channel;
|
import com.iluwatar.eda.framework.Handler;
|
||||||
import com.iluwatar.eda.model.User;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the {@link UserCreatedEvent} message.
|
* Handles the {@link UserCreatedEvent} message.
|
||||||
*/
|
*/
|
||||||
public class UserCreatedEventHandler implements Channel<UserCreatedEvent> {
|
public class UserCreatedEventHandler implements Handler<UserCreatedEvent> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispatch(Event message) {
|
public void onEvent(Event message) {
|
||||||
|
|
||||||
UserCreatedEvent userCreatedEvent = (UserCreatedEvent) message;
|
UserCreatedEvent userCreatedEvent = (UserCreatedEvent) message;
|
||||||
System.out.printf("User with %s has been Created!", userCreatedEvent.getUser().getUsername());
|
System.out.printf("User with %s has been Created!", userCreatedEvent.getUser().getUsername());
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
package com.iluwatar.eda.handler;
|
package com.iluwatar.eda.handler;
|
||||||
|
|
||||||
import com.iluwatar.eda.event.Event;
|
import com.iluwatar.eda.event.Event;
|
||||||
import com.iluwatar.eda.event.UserCreatedEvent;
|
|
||||||
import com.iluwatar.eda.event.UserUpdatedEvent;
|
import com.iluwatar.eda.event.UserUpdatedEvent;
|
||||||
import com.iluwatar.eda.framework.Channel;
|
import com.iluwatar.eda.framework.Handler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the {@link UserUpdatedEvent} message.
|
* Handles the {@link UserUpdatedEvent} message.
|
||||||
*/
|
*/
|
||||||
public class UserUpdatedEventHandler implements Channel<UserUpdatedEvent> {
|
public class UserUpdatedEventHandler implements Handler<UserUpdatedEvent> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispatch(Event message) {
|
public void onEvent(Event message) {
|
||||||
|
|
||||||
UserUpdatedEvent userUpdatedEvent = (UserUpdatedEvent) message;
|
UserUpdatedEvent userUpdatedEvent = (UserUpdatedEvent) message;
|
||||||
System.out.printf("User with %s has been Updated!", userUpdatedEvent.getUser().getUsername());
|
System.out.printf("User with %s has been Updated!", userUpdatedEvent.getUser().getUsername());
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
import com.iluwatar.eda.EventDispatcher;
|
|
||||||
import com.iluwatar.eda.event.Event;
|
|
||||||
import com.iluwatar.eda.event.UserCreatedEvent;
|
|
||||||
import com.iluwatar.eda.event.UserUpdatedEvent;
|
|
||||||
import com.iluwatar.eda.handler.UserCreatedEventHandler;
|
|
||||||
import com.iluwatar.eda.handler.UserUpdatedEventHandler;
|
|
||||||
import com.iluwatar.eda.model.User;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import static org.mockito.Mockito.spy;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Event Driven Pattern unit tests to assert and verify correct pattern behaviour
|
|
||||||
*/
|
|
||||||
public class EventDrivenTest {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This unit test should register events and event handlers correctly with the event dispatcher
|
|
||||||
* and events should be dispatched accordingly.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testEventDriverPattern() {
|
|
||||||
|
|
||||||
EventDispatcher dispatcher = spy(new EventDispatcher());
|
|
||||||
UserCreatedEventHandler userCreatedEventHandler = new UserCreatedEventHandler();
|
|
||||||
UserUpdatedEventHandler userUpdatedEventHandler = new UserUpdatedEventHandler();
|
|
||||||
dispatcher.registerChannel(UserCreatedEvent.class, userCreatedEventHandler);
|
|
||||||
dispatcher.registerChannel(UserUpdatedEvent.class, userUpdatedEventHandler);
|
|
||||||
|
|
||||||
assertEquals("Two handlers must be registered", 2, dispatcher.getHandlers().size());
|
|
||||||
assertEquals("UserCreatedEvent must return the UserCreatedEventHandler",
|
|
||||||
userCreatedEventHandler,
|
|
||||||
(UserCreatedEventHandler) dispatcher.getHandlers().get(UserCreatedEvent.class));
|
|
||||||
assertEquals("UserUpdatedEvent must be registered to the UserUpdatedEventHandler",
|
|
||||||
userUpdatedEventHandler,
|
|
||||||
(UserUpdatedEventHandler) dispatcher.getHandlers().get(UserUpdatedEvent.class));
|
|
||||||
|
|
||||||
User user = new User("iluwatar");
|
|
||||||
|
|
||||||
UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user);
|
|
||||||
UserUpdatedEvent userUpdatedEvent = new UserUpdatedEvent(user);
|
|
||||||
dispatcher.dispatch(userCreatedEvent);
|
|
||||||
dispatcher.dispatch(userUpdatedEvent);
|
|
||||||
|
|
||||||
//verify that the events have been dispatched
|
|
||||||
verify(dispatcher).dispatch(userCreatedEvent);
|
|
||||||
verify(dispatcher).dispatch(userUpdatedEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This unit test should correctly return the {@link Event} class type when calling the
|
|
||||||
* {@link Event#getType() getType} method.
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testGetEventType() {
|
|
||||||
User user = new User("iluwatar");
|
|
||||||
UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user);
|
|
||||||
assertEquals(UserCreatedEvent.class, userCreatedEvent.getType());
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.iluwatar.eda.event;
|
||||||
|
|
||||||
|
import com.iluwatar.eda.model.User;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link UserCreatedEventTest} tests and verifies {@link Event} behaviour.
|
||||||
|
*/
|
||||||
|
public class UserCreatedEventTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This unit test should correctly return the {@link Event} class type when calling the
|
||||||
|
* {@link Event#getType() getType} method.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetEventType() {
|
||||||
|
User user = new User("iluwatar");
|
||||||
|
UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user);
|
||||||
|
assertEquals(UserCreatedEvent.class, userCreatedEvent.getType());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package com.iluwatar.eda.framework;
|
||||||
|
|
||||||
|
import com.iluwatar.eda.framework.EventDispatcher;
|
||||||
|
import com.iluwatar.eda.event.UserCreatedEvent;
|
||||||
|
import com.iluwatar.eda.event.UserUpdatedEvent;
|
||||||
|
import com.iluwatar.eda.handler.UserCreatedEventHandler;
|
||||||
|
import com.iluwatar.eda.handler.UserUpdatedEventHandler;
|
||||||
|
import com.iluwatar.eda.model.User;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event Dispatcher unit tests to assert and verify correct event dispatcher behaviour
|
||||||
|
*/
|
||||||
|
public class EventDispatcherTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This unit test should register events and event handlers correctly with the event dispatcher
|
||||||
|
* and events should be dispatched accordingly.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testEventDriverPattern() {
|
||||||
|
|
||||||
|
EventDispatcher dispatcher = spy(new EventDispatcher());
|
||||||
|
UserCreatedEventHandler userCreatedEventHandler = spy(new UserCreatedEventHandler());
|
||||||
|
UserUpdatedEventHandler userUpdatedEventHandler = spy(new UserUpdatedEventHandler());
|
||||||
|
dispatcher.registerChannel(UserCreatedEvent.class, userCreatedEventHandler);
|
||||||
|
dispatcher.registerChannel(UserUpdatedEvent.class, userUpdatedEventHandler);
|
||||||
|
|
||||||
|
User user = new User("iluwatar");
|
||||||
|
|
||||||
|
UserCreatedEvent userCreatedEvent = new UserCreatedEvent(user);
|
||||||
|
UserUpdatedEvent userUpdatedEvent = new UserUpdatedEvent(user);
|
||||||
|
|
||||||
|
//fire a userCreatedEvent and verify that userCreatedEventHandler has been invoked.
|
||||||
|
dispatcher.onEvent(userCreatedEvent);
|
||||||
|
verify(userCreatedEventHandler).onEvent(userCreatedEvent);
|
||||||
|
verify(dispatcher).onEvent(userCreatedEvent);
|
||||||
|
|
||||||
|
//fire a userCreatedEvent and verify that userUpdatedEventHandler has been invoked.
|
||||||
|
dispatcher.onEvent(userUpdatedEvent);
|
||||||
|
verify(userUpdatedEventHandler).onEvent(userUpdatedEvent);
|
||||||
|
verify(dispatcher).onEvent(userUpdatedEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user