Skip to content

Commit

Permalink
Merge pull request iluwatar#2 from genericmethod/master
Browse files Browse the repository at this point in the history
Pull
  • Loading branch information
mikulucky committed Jan 2, 2016
2 parents e25ef1b + 7176b86 commit 39b183d
Show file tree
Hide file tree
Showing 16 changed files with 377 additions and 3 deletions.
Binary file added event-driven-architecture/etc/class_diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions event-driven-architecture/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
layout: pattern
title: Event Driven Architecture
folder: event-driven-architecture
permalink: /patterns/event-driven-architecture


**Intent:** Send and notify state changes of your objects to other applications using an Event-driven Architecture.

![alt text](./etc/class_diagram.png "Event Driven Architecture")

**Applicability:** Use an Event-driven architecture when

* you want to create a loosely coupled system
* you want to build a more responsive system
* you want a system that is easier to extend

**Real world examples:**

* SendGrid, an email API, sends events whenever an email is processed, delivered, opened etc... (https://sendgrid.com/docs/API_Reference/Webhooks/event.html)
* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events)
* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda)
* MySQL runs triggers based on events such as inserts and update events happening on database tables.

**Credits:**

* [Event-driven architecture - Wikipedia](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
* [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)
28 changes: 28 additions & 0 deletions event-driven-architecture/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.10.0-SNAPSHOT</version>
</parent>

<artifactId>event-driven-architecture</artifactId>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
42 changes: 42 additions & 0 deletions event-driven-architecture/src/main/java/com/iluwatar/eda/App.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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;

/**
* An event-driven architecture (EDA) is a framework that orchestrates behavior around the
* production, detection and consumption of events as well as the responses they evoke. An event is
* any identifiable occurrence that has significance for system hardware or software. <p/> The
* example below uses an {@link EventDispatcher} to link/register {@link Event} objects to their
* respective handlers once an {@link Event} is dispatched, it's respective handler is invoked and
* the {@link Event} is handled accordingly.
*
*/
public class App {

/**
* 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.
* When a user is saved, the {@link UserCreatedEvent} can be dispatched.
* On the other hand, when a user is updated, {@link UserUpdatedEvent} can be dispatched.
*
*/
public static void main(String[] args) {

EventDispatcher dispatcher = new EventDispatcher();
dispatcher.registerChannel(UserCreatedEvent.class, new UserCreatedEventHandler());
dispatcher.registerChannel(UserUpdatedEvent.class, new UserUpdatedEventHandler());

User user = new User("iluwatar");
dispatcher.onEvent(new UserCreatedEvent(user));
dispatcher.onEvent(new UserUpdatedEvent(user));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.iluwatar.eda.event;

import com.iluwatar.eda.framework.EventDispatcher;
import com.iluwatar.eda.framework.Message;

/**
* The {@link Event} class serves as a base class for defining custom events happening with your
* system. In this example we have two types of events defined.
* <ul>
* <li>{@link UserCreatedEvent} - used when a user is created</li>
* <li>{@link UserUpdatedEvent} - used when a user is updated</li>
* </ul>
* Events can be distinguished using the {@link #getType() getType} method.
*/
public class Event implements Message {

/**
* Returns the event type as a {@link Class} object
* In this example, this method is used by the {@link EventDispatcher} to
* dispatch events depending on their type.
*
* @return the Event type as a {@link Class}.
*/
public Class<? extends Message> getType() {
return getClass();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.iluwatar.eda.event;

import com.iluwatar.eda.model.User;

/**
* 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.
*/
public class UserCreatedEvent extends Event {

private User user;

public UserCreatedEvent(User user) {
this.user = user;
}

public User getUser() {
return user;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.iluwatar.eda.event;

import com.iluwatar.eda.model.User;

/**
* 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.
*/
public class UserUpdatedEvent extends Event {

private User user;

public UserUpdatedEvent(User user) {
this.user = user;
}

public User getUser() {
return user;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.iluwatar.eda.framework;

import com.iluwatar.eda.event.Event;

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);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.iluwatar.eda.framework;

/**
* A {@link Message} is an object with a specific type that is associated
* 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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.iluwatar.eda.handler;

import com.iluwatar.eda.event.Event;
import com.iluwatar.eda.event.UserCreatedEvent;
import com.iluwatar.eda.framework.Handler;

/**
* Handles the {@link UserCreatedEvent} message.
*/
public class UserCreatedEventHandler implements Handler<UserCreatedEvent> {

@Override
public void onEvent(Event message) {

UserCreatedEvent userCreatedEvent = (UserCreatedEvent) message;
System.out.printf("User with %s has been Created!", userCreatedEvent.getUser().getUsername());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.iluwatar.eda.handler;

import com.iluwatar.eda.event.Event;
import com.iluwatar.eda.event.UserUpdatedEvent;
import com.iluwatar.eda.framework.Handler;

/**
* Handles the {@link UserUpdatedEvent} message.
*/
public class UserUpdatedEventHandler implements Handler<UserUpdatedEvent> {

@Override
public void onEvent(Event message) {

UserUpdatedEvent userUpdatedEvent = (UserUpdatedEvent) message;
System.out.printf("User with %s has been Updated!", userUpdatedEvent.getUser().getUsername());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.iluwatar.eda.model;

import com.iluwatar.eda.event.UserCreatedEvent;
import com.iluwatar.eda.event.UserUpdatedEvent;

/**
* This {@link User} class is a basic pojo used to demonstrate user data sent along with
* the {@link UserCreatedEvent} and {@link UserUpdatedEvent} events.
*/
public class User {

private String username;

public User(String username) {
this.username = username;
}

public String getUsername() {
return username;
}
}
Original file line number Diff line number Diff line change
@@ -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());
}
}
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 39b183d

Please sign in to comment.