#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:
		| @@ -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) | ||||
| * [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) | ||||
| * | ||||
| @@ -3,6 +3,7 @@ package com.iluwatar.eda; | ||||
| import com.iluwatar.eda.event.Event; | ||||
| import com.iluwatar.eda.event.UserCreatedEvent; | ||||
| import com.iluwatar.eda.event.UserUpdatedEvent; | ||||
| import com.iluwatar.eda.framework.EventDispatcher; | ||||
| import com.iluwatar.eda.handler.UserCreatedEventHandler; | ||||
| import com.iluwatar.eda.handler.UserUpdatedEventHandler; | ||||
| import com.iluwatar.eda.model.User; | ||||
| @@ -19,7 +20,7 @@ import com.iluwatar.eda.model.User; | ||||
| 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 | ||||
|    * bound to the UserCreatedEventHandler, whilst the {@link UserUpdatedEvent} is bound to the | ||||
|    * {@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()); | ||||
|  | ||||
|     User user = new User("iluwatar"); | ||||
|     dispatcher.dispatch(new UserCreatedEvent(user)); | ||||
|     dispatcher.dispatch(new UserUpdatedEvent(user)); | ||||
|     dispatcher.onEvent(new UserCreatedEvent(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; | ||||
|  | ||||
| import com.iluwatar.eda.framework.EventDispatcher; | ||||
| import com.iluwatar.eda.framework.Message; | ||||
|  | ||||
| /** | ||||
| @@ -15,8 +16,8 @@ public class Event implements Message { | ||||
|  | ||||
|   /** | ||||
|    * Returns the event type as a {@link Class} object | ||||
|    * In this example, this method is used by the {@link com.iluwatar.eda.EventDispatcher} to | ||||
|    * dispatch events depending on their type. | ||||
|    * In this example, this method is used by the {@link EventDispatcher} to | ||||
|    * onEvent events depending on their type. | ||||
|    * | ||||
|    * @return the Event type as a {@link Class}. | ||||
|    */ | ||||
|   | ||||
| @@ -3,7 +3,7 @@ package com.iluwatar.eda.event; | ||||
| 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, | ||||
|  * 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; | ||||
|  | ||||
| /** | ||||
|  * 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, | ||||
|  * 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 | ||||
|  * to a specific {@link Channel}. | ||||
|  * to a specific {@link Handler}. | ||||
|  */ | ||||
| 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(); | ||||
| } | ||||
|   | ||||
| @@ -2,16 +2,15 @@ package com.iluwatar.eda.handler; | ||||
|  | ||||
| import com.iluwatar.eda.event.Event; | ||||
| import com.iluwatar.eda.event.UserCreatedEvent; | ||||
| import com.iluwatar.eda.framework.Channel; | ||||
| import com.iluwatar.eda.model.User; | ||||
| import com.iluwatar.eda.framework.Handler; | ||||
|  | ||||
| /** | ||||
|  * Handles the {@link UserCreatedEvent} message. | ||||
|  */ | ||||
| public class UserCreatedEventHandler implements Channel<UserCreatedEvent> { | ||||
| public class UserCreatedEventHandler implements Handler<UserCreatedEvent> { | ||||
|  | ||||
|   @Override | ||||
|   public void dispatch(Event message) { | ||||
|   public void onEvent(Event message) { | ||||
|  | ||||
|     UserCreatedEvent userCreatedEvent = (UserCreatedEvent) message; | ||||
|     System.out.printf("User with %s has been Created!", userCreatedEvent.getUser().getUsername()); | ||||
|   | ||||
| @@ -1,17 +1,16 @@ | ||||
| package com.iluwatar.eda.handler; | ||||
|  | ||||
| import com.iluwatar.eda.event.Event; | ||||
| import com.iluwatar.eda.event.UserCreatedEvent; | ||||
| import com.iluwatar.eda.event.UserUpdatedEvent; | ||||
| import com.iluwatar.eda.framework.Channel; | ||||
| import com.iluwatar.eda.framework.Handler; | ||||
|  | ||||
| /** | ||||
|  * Handles the {@link UserUpdatedEvent} message. | ||||
|  */ | ||||
| public class UserUpdatedEventHandler implements Channel<UserUpdatedEvent> { | ||||
| public class UserUpdatedEventHandler implements Handler<UserUpdatedEvent> { | ||||
|  | ||||
|   @Override | ||||
|   public void dispatch(Event message) { | ||||
|   public void onEvent(Event message) { | ||||
|  | ||||
|     UserUpdatedEvent userUpdatedEvent = (UserUpdatedEvent) message; | ||||
|     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); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
		Reference in New Issue
	
	Block a user