mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-13 14:16:02 +00:00
docs: update data bus
This commit is contained in:
1 parent
b376f1de9a
commit
356ba27272
1 file changed
+122
-84
+122
-84
@@ -1,22 +1,28 @@
|
|||||||
---
|
---
|
||||||
title: Data Bus
|
title: Data Bus
|
||||||
category: Architectural
|
category: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tag:
|
tag:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
- Event-driven
|
||||||
|
- Messaging
|
||||||
|
- Publish/subscribe
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
|
||||||
|
* Event Bus
|
||||||
|
* Message Bus
|
||||||
|
|
||||||
## Intent
|
## Intent
|
||||||
|
|
||||||
Allows send of messages/events between components of an application
|
The Data Bus design pattern aims to provide a centralized communication channel through which various components of a system can exchange data without being directly connected, thus promoting loose coupling and enhancing scalability and maintainability.
|
||||||
without them needing to know about each other. They only need to know
|
|
||||||
about the type of the message/event being sent.
|
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real world example
|
Real world example
|
||||||
|
|
||||||
> Say you have an app that enables online bookings and participation of events. You want the app to send notifications such as event advertisements to everyone who is an ordinary member of the community or organisation holding the events. However, you do not want to send such notifications like advertisements to the event administrators or organisers but you desire to send them and them only the time whenever a new advertisement is sent to all members of the community. The Data Bus enables you to selectively notify people of a community by type, whether it be ordinary community members or event administrators, by making their classes or components only accept messages of a certain type. Ultimately, there is no need for the components or classes of ordinary community members nor administrators to know anything about you in terms of the classes or components you are using to notify the entire community except for the need to know the type of the messages you are sending.
|
> Say you have an app that enables online bookings and participation of events. You want the app to send notifications such as event advertisements to everyone who is an ordinary member of the community or organisation holding the events. However, you do not want to send such notifications like advertisements to the event administrators or organisers, but you desire to send them and them only the time whenever a new advertisement is sent to all members of the community. The Data Bus enables you to selectively notify people of a community by type, whether it be ordinary community members or event administrators, by making their classes or components only accept messages of a certain type. Ultimately, there is no need for the components or classes of ordinary community members nor administrators to know anything about you in terms of the classes or components you are using to notify the entire community except for the need to know the type of the messages you are sending.
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
|
|
||||||
@@ -29,77 +35,78 @@ Translating the online events app example above, we firstly have our Member inte
|
|||||||
```java
|
```java
|
||||||
public interface Member extends Consumer<DataType> {
|
public interface Member extends Consumer<DataType> {
|
||||||
|
|
||||||
void accept(DataType event);
|
void accept(DataType event);
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Then we have a databus to subscribe someone to be a member or unsubcribe, and also to publish an event so to notify every member in the community.
|
Then we have a data bus to subscribe someone to be a member or unsubscribe, and also to publish an event so to notify every member in the community.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class DataBus {
|
public class DataBus {
|
||||||
|
|
||||||
private static final DataBus INSTANCE = new DataBus();
|
private static final DataBus INSTANCE = new DataBus();
|
||||||
|
|
||||||
private final Set<Member> listeners = new HashSet<>();
|
private final Set<Member> listeners = new HashSet<>();
|
||||||
|
|
||||||
public static DataBus getInstance() {
|
public static DataBus getInstance() {
|
||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a member with the data-bus to start receiving events.
|
* Register a member with the data-bus to start receiving events.
|
||||||
*
|
*
|
||||||
* @param member The member to register
|
* @param member The member to register
|
||||||
*/
|
*/
|
||||||
public void subscribe(final Member member) {
|
public void subscribe(final Member member) {
|
||||||
|
|
||||||
this.listeners.add(member);
|
this.listeners.add(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deregister a member to stop receiving events.
|
* Deregister a member to stop receiving events.
|
||||||
*
|
*
|
||||||
* @param member The member to deregister
|
* @param member The member to deregister
|
||||||
*/
|
*/
|
||||||
public void unsubscribe(final Member member) {
|
public void unsubscribe(final Member member) {
|
||||||
this.listeners.remove(member);
|
this.listeners.remove(member);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish and event to all members.
|
* Publish and event to all members.
|
||||||
*
|
*
|
||||||
* @param event The event
|
* @param event The event
|
||||||
*/
|
*/
|
||||||
public void publish(final DataType event) {
|
public void publish(final DataType event) {
|
||||||
event.setDataBus(this);
|
event.setDataBus(this);
|
||||||
|
|
||||||
listeners.forEach(
|
listeners.forEach(
|
||||||
listener -> listener.accept(event));
|
listener -> listener.accept(event));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
As you can see, the accept method is applied for each member under the publish method.
|
As you can see, the accept method is applied for each member under the publish method.
|
||||||
|
|
||||||
Hence, as shown below, the accept method can be used to check the type of message to be published and successfully send/handle that message if the accept method has an instance for that message. Otherwise, the accept method cannot as is for the case of the MessageCollectorMember (the ordinary community members) when the type of message being sent is StartingData or StoppingData (information on the time whenever a new advertisement is sent to all members).
|
Hence, as shown below, the accept method can be used to check the type of message to be published and successfully send/handle that message if the accept method has an instance for that message. Otherwise, the accept method cannot as is for the case of the MessageCollectorMember (the ordinary community members) when the type of message being sent is StartingData or StoppingData (information on the time whenever a new advertisement is sent to all members).
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class MessageCollectorMember implements Member {
|
public class MessageCollectorMember implements Member {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
|
|
||||||
private final List<String> messages = new ArrayList<>();
|
private final List<String> messages = new ArrayList<>();
|
||||||
|
|
||||||
public MessageCollectorMember(String name) {
|
public MessageCollectorMember(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void accept(final DataType data) {
|
|
||||||
if (data instanceof MessageData) {
|
|
||||||
handleEvent((MessageData) data);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Override
|
||||||
|
public void accept(final DataType data) {
|
||||||
|
if (data instanceof MessageData) {
|
||||||
|
handleEvent((MessageData) data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
However, the StatusMember(the event administrators or organisers) can accept such types of messages as
|
However, the StatusMember(the event administrators or organisers) can accept such types of messages as
|
||||||
@@ -107,58 +114,89 @@ However, the StatusMember(the event administrators or organisers) can accept suc
|
|||||||
```java
|
```java
|
||||||
public class StatusMember implements Member {
|
public class StatusMember implements Member {
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
||||||
private LocalDateTime started;
|
private LocalDateTime started;
|
||||||
|
|
||||||
private LocalDateTime stopped;
|
private LocalDateTime stopped;
|
||||||
@Override
|
|
||||||
public void accept(final DataType data) {
|
@Override
|
||||||
if (data instanceof StartingData) {
|
public void accept(final DataType data) {
|
||||||
handleEvent((StartingData) data);
|
if (data instanceof StartingData) {
|
||||||
} else if (data instanceof StoppingData) {
|
handleEvent((StartingData) data);
|
||||||
handleEvent((StoppingData) data);
|
} else if (data instanceof StoppingData) {
|
||||||
|
handleEvent((StoppingData) data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Here is the App class to demonstrate the Data Bus pattern in action:
|
||||||
|
|
||||||
Thus, the data bus outputs as follows:
|
|
||||||
|
|
||||||
```java
|
```java
|
||||||
class App {
|
class App {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
final var bus = DataBus.getInstance();
|
final var bus = DataBus.getInstance();
|
||||||
bus.subscribe(new StatusMember(1));
|
bus.subscribe(new StatusMember(1));
|
||||||
bus.subscribe(new StatusMember(2));
|
bus.subscribe(new StatusMember(2));
|
||||||
final var foo = new MessageCollectorMember("Foo");
|
final var foo = new MessageCollectorMember("Foo");
|
||||||
final var bar = new MessageCollectorMember("Bar");
|
final var bar = new MessageCollectorMember("Bar");
|
||||||
bus.subscribe(foo);
|
bus.subscribe(foo);
|
||||||
bus.publish(StartingData.of(LocalDateTime.now()));
|
bus.publish(StartingData.of(LocalDateTime.now()));
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
//OUTPUT:
|
|
||||||
|
|
||||||
|
Thus, the data bus outputs as follows:
|
||||||
|
|
||||||
|
```
|
||||||
//02:33:57.627 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 2 sees application started at 2022-10-26T02:33:57.613529100
|
//02:33:57.627 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 2 sees application started at 2022-10-26T02:33:57.613529100
|
||||||
|
|
||||||
//02:33:57.633 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 1 sees application started at 2022-10-26T02:33:57.613529100
|
//02:33:57.633 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 1 sees application started at 2022-10-26T02:33:57.613529100
|
||||||
|
```
|
||||||
|
|
||||||
Evidently, due to MessageCollectorMembers only accepting messages of type MessageData and none of either StartingData nor StoppingData, the MessageCollectorMembers are prevented from seeing what the StatusMembers (the event administrators or organisers) are shown: information on the time whenever a new advertisement is sent to all members.
|
Evidently, due to MessageCollectorMembers only accepting messages of type MessageData and none of either StartingData nor StoppingData, the MessageCollectorMembers are prevented from seeing what the StatusMembers (the event administrators or organisers) are shown: information on the time whenever a new advertisement is sent to all members.
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Applicability
|
## Applicability
|
||||||
Use Data Bus pattern when
|
|
||||||
|
|
||||||
* you want your components to decide themselves which messages/events they want to receive
|
* When multiple components need to share data or events but direct coupling is undesirable.
|
||||||
* you want to have many-to-many communication
|
* In complex systems where the flow of information varies dynamically.
|
||||||
* you want your components to know nothing about each other
|
* In distributed systems where components might be deployed across different environments.
|
||||||
|
|
||||||
|
## Known Uses
|
||||||
|
|
||||||
|
* Event handling systems in large-scale applications.
|
||||||
|
* Microservices architectures for inter-service communication.
|
||||||
|
* Real-time data processing systems, such as stock trading platforms.
|
||||||
|
* In frameworks like Spring, particularly with its application event mechanism.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
|
||||||
|
* Loose Coupling: Components can interact without having direct dependencies on each other.
|
||||||
|
* Flexibility: New subscribers or publishers can be added without impacting existing components.
|
||||||
|
* Scalability: The pattern supports scaling components independently.
|
||||||
|
* Reusability: The bus and components can be reused across different systems.
|
||||||
|
|
||||||
|
Trade-offs:
|
||||||
|
|
||||||
|
* Complexity: Introducing a data bus can add complexity to the system architecture.
|
||||||
|
* Performance Overhead: The additional layer of communication may introduce latency.
|
||||||
|
* Debugging Difficulty: Tracing data flow through the bus can be challenging, especially in systems with many events.
|
||||||
|
|
||||||
## Related Patterns
|
## Related Patterns
|
||||||
Data Bus is similar to
|
|
||||||
|
|
||||||
* Mediator pattern with Data Bus Members deciding for themselves if they want to accept any given message
|
* [Mediator](https://java-design-patterns.com/patterns/mediator/): Facilitates communication between components, but unlike Data Bus, it centralizes control.
|
||||||
* Observer pattern but supporting many-to-many communication
|
* [Observer](https://java-design-patterns.com/patterns/observer/): Similar in nature to the publish-subscribe mechanism used in Data Bus for notifying changes to multiple objects.
|
||||||
* Publish/Subscribe pattern with the Data Bus decoupling the publisher and the subscriber
|
* Publish/Subscribe: The Data Bus pattern is often implemented using the publish-subscribe mechanism, where publishers post messages to the bus without knowledge of the subscribers.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
* [Enterprise Integration Patterns](https://amzn.to/3J6WoYS)
|
||||||
|
* [Pattern-Oriented Software Architecture, Volume 4: A Pattern Language for Distributed Computing](https://amzn.to/3PTRGBM)
|
||||||
Reference in new issue
Block a user