Skip to content

Commit 049b7f0

Browse files
authored
Rt message handler decorator (#2)
* adding handler * added decorators * refactory * refactory pubsub * update readme * for feature added * refact: fixing * updated code * updated angular * updated readme * updated readme * updated readme * fixing feature module * refact: fixing code * updated example app * added examples * fix: test * doc: fixing readme * doc: updated readme
1 parent f064060 commit 049b7f0

File tree

4 files changed

+257
-23
lines changed

4 files changed

+257
-23
lines changed

README.md

+235-16
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,84 @@
11

2-
32
# Angular PubSub
43

54
Angular 11.x implementation of the [publish subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) Pattern.
65

76
![GitHub package.json dependency version (subfolder of monorepo)](https://img.shields.io/github/package-json/dependency-version/fullstackmaster1/fsms-angular-pubsub/@angular/core) ![GitHub Workflow Status (branch)](https://img.shields.io/github/workflow/status/fullstackmaster1/fsms-angular-pubsub/CI%20and%20CD/main?style=flat) ![npm](https://img.shields.io/npm/dw/@fsms/angular-pubsub?style=flat) [![npm version](https://badge.fury.io/js/%40fsms%2Fangular-pubsub.svg)](https://badge.fury.io/js/%40fsms%2Fangular-pubsub) ![GitHub repo size](https://img.shields.io/github/repo-size/fullstackmaster1/FSMS-ANGULAR-PUBSUB) ![GitHub pull requests](https://img.shields.io/github/issues-pr/fullstackmaster1/fsms-angular-pubsub) ![GitHub last commit](https://img.shields.io/github/last-commit/fullstackmaster1/fsms-angular-pubsub) [![dependencies Status](https://status.david-dm.org/gh/FullStackMaster1/fsms-angular-pubsub.svg)](https://david-dm.org/FullStackMaster1/fsms-angular-pubsub) ![GitHub User's stars](https://img.shields.io/github/stars/fullstackmaster1?style=social) ![GitHub Sponsors](https://img.shields.io/github/sponsors/fullstackmaster1?style=social)
87

9-
## Installing
8+
> By [Rupesh Tiwari](https://rupeshtiwari.com)
9+
10+
**If you enjoy @fsms/angular-pubsub, please consider [supporting me](https://github.com/sponsors/rupeshtiwari) for years of development (and to unlock rewards!) ❤**
11+
12+
## Table of Contents
13+
14+
- [Angular PubSub](#angular-pubsub)
15+
- [Table of Contents](#table-of-contents)
16+
- [Installing @fsms/angular-pubsub](#installing-fsmsangular-pubsub)
17+
- [Definitions](#definitions)
18+
- [Message](#message)
19+
- [Pub sub Service (Angular Service)](#pub-sub-service-angular-service)
20+
- [Using PubSub Service](#using-pubsub-service)
21+
- [Using Angular Service As Message Handler](#using-angular-service-as-message-handler)
22+
- [Contributions](#contributions)
23+
24+
25+
## Installing @fsms/angular-pubsub
1026

1127
**npm installation**
1228

1329
```shell
1430
npm i -S @fsms/angular-pubsub
1531
```
16-
## Using PubSub For Inline Style
32+
33+
## Definitions
34+
35+
You need `Message` class to create your messages and you need `PubsubService` to publish or subscribe messages.
36+
### Message
37+
38+
`Message` holds `messageType` and optional payload
39+
40+
```ts
41+
export interface IMessage {
42+
messageType: string;
43+
payload?: any;
44+
}
45+
```
46+
Example of one message:
47+
48+
```ts
49+
import { DefineMessage, IMessageSchema, IMessage } from '@fsms/angular-pubsub';
50+
51+
@DefineMessage<IMessageSchema>()
52+
export class PlaceOrder implements IMessage {
53+
static messageType = '[Sells] Place Order';
54+
messageType = PlaceOrder.messageType;
55+
constructor(public payload?: string) {}
56+
}
57+
```
58+
59+
### Pub sub Service (Angular Service)
60+
61+
`pubsubService` is used to publish and subscribe messages.
62+
63+
```ts
64+
publish<V extends IMessage = IMessage>(message: V): void;
65+
subscribe({
66+
messageType,
67+
callback,
68+
error,
69+
complete,
70+
}: SubscribeOptions): PubsubSubscription;
71+
```
72+
73+
## Using PubSub Service
1774
1875
1. **Importing PubsubModule in application module**.
1976
2077
Initialize module for root in your angular root module
2178
2279
```ts
2380

24-
import { PubSubModule } from '@fsms/angular-pubsub'; // <= HERE
81+
import { PubSubModule } from '@fsms/angular-pubsub'; 👈 // Importing Angular Pubsub module
2582

2683
@NgModule({
2784
declarations: [
@@ -33,7 +90,7 @@ imports: [
3390
BrowserModule,
3491
FormsModule,
3592
HttpModule,
36-
PubSubModule.forRoot() // <= AND HERE
93+
PubSubModule.forRoot() 👈 // Initiate Pubsub module
3794
],
3895
providers: [],
3996
bootstrap: [RootComponent]
@@ -47,7 +104,7 @@ Go to desired component and subscribe to a message.
47104

48105
```ts
49106
import { Component } from '@angular/core';
50-
import { PubsubService } from '@fsms/angular-pubsub';// <= HERE
107+
import { PubsubService } from '@fsms/angular-pubsub';👈 // Importing Angular Pubsub module
51108

52109
@Component({
53110
selector: 'app-root',
@@ -57,22 +114,40 @@ import { PubsubService } from '@fsms/angular-pubsub';// <= HERE
57114
export class AppComponent {
58115
constructor(
59116
private pubsubService: PubsubService/* <= HERE */) {}
117+
👆// Injecting Angular Pubsub Service
60118
}
61119
```
62120
3. **Subscribing to message**
63121

64122
In `ngOnInit` method of angular, you can subscribe to the events that you want to react upon.
65123

66124
```ts
67-
ngOnInit(): void {
68-
this.pubsubService.subscribe({ // <= HERE
69-
messageType: PlaceOrderType,
125+
import { PubsubService, PubsubSubscription } from '@fsms/angular-pubsub';
126+
import { PlaceOrder } from './orders/messages/place-order-message';
127+
128+
@Component({
129+
selector: 'app-root',
130+
templateUrl: './app.component.html',
131+
styleUrls: ['./app.component.css'],
132+
})
133+
export class AppComponent implements OnInit {
134+
subscriptions: PubsubSubscription[] = [];
135+
136+
constructor(private pubsubService: PubsubService) {}
137+
138+
ngOnInit(): void {
139+
140+
this.subscriptions.push(
141+
this.pubsubService.subscribe({ 👈// Subscribing to a message
142+
messageType: PlaceOrder.messageType,
70143
callback: (msg) => console.log('received', msg),
71-
});
144+
})
145+
);
146+
}
72147
}
73148
```
74-
4. **Publishing Message**
75-
The `publish` method takes one argument where it expect the `message` object.
149+
4. **Publishing a Message**
150+
The `publish` method takes one argument where it expect the `Message` object.
76151

77152
Example: Now on a button click, I want to publish a message with some payload.
78153

@@ -93,20 +168,164 @@ export class AppComponent {
93168
orderPlaced($event: KeyboardEvent) {
94169
$event.preventDefault();
95170

96-
this.pubsubService.publish(new OrderPlaced('20 Apples'));// <= HERE
171+
this.pubsubService.publish( 👈// Publishing a message
172+
new OrderCreated({
173+
orderId: new Date().getTime().toString(36),
174+
item: '20 Apples',
175+
})
176+
);
97177
}
98178
}
99179
```
100180
5. **Unsubscribing Messages**
101181

182+
Keep all subscriptions per component in an array. And On component you must unsubscribe your subscriptions on `ngOnDestroy` event.
183+
184+
```ts
185+
ngOnDestroy(): void {
186+
this.subscriptions.forEach((s) => s.unsubscribe());👈// Unsubscribing a message
187+
}
188+
```
189+
190+
## Using Angular Service As Message Handler
191+
192+
Convert Angular service to a Message Handler. If to organize your angular code base as Service Oriented Architecture (SOA) way. And you want to create an Angular service that can listen to a Message and react on them just like a N-ServiceBus Message Handlers?
193+
194+
Then you must use `@RegisterHandler({})` decorator on any Angular Service then it will automatically be registered as message subscriber. This helps us to organize your business logic in services rather in angular components.
195+
196+
**Message Handler**
197+
198+
Message handler is a service that can listen to one message or more messages and perform business logic. Message Handler can also publish after handling incoming messages.
199+
200+
Diagram of a Angular Service as Message Handler called as `ShipOrderService` which listens to `OrderReady` message and process shipping then publishes `OrderShipped` message.
201+
202+
![](https://i.imgur.com/r60vyT4.png)
203+
204+
205+
**Creating Message Handler at Root Module**
206+
207+
1. First create your message handler at Root (App) Module.
208+
209+
Example: When Order is Ready Shipping service is starting the shipment process.
210+
211+
```ts
212+
import { Injectable } from '@angular/core';
213+
import {
214+
CallbackOptions,
215+
IHandleMessage,
216+
RegisterHandler,
217+
} from '@fsms/angular-pubsub';
218+
import { OrderReady } from '../messages/order-ready-message';
219+
import { OrderShipped } from '../messages/order-shipped-message';
220+
221+
@Injectable({ providedIn: 'root' }) // Angular Service
222+
@RegisterHandler({ 👈
223+
messages: [OrderReady],👈 // You can listen to many messages
224+
})
225+
export class ShipOrderService implements IHandleMessage<OrderReady> {
226+
handle({ message, context }: CallbackOptions<OrderReady>): void {
227+
console.log('[Shipping] Order Shipped', message);
228+
229+
context.publish(new OrderShipped(message.payload));
230+
👆 // context will have publish method to publish any message from message handler.
231+
}
232+
}
233+
```
234+
235+
2. Register your message handler in Root (App) Module.
236+
237+
Use `PubsubModule.forRoot([])` to register your app message handlers.
238+
239+
Example: Registering `ShipOrderService`
240+
```ts
241+
import { NgModule } from '@angular/core';
242+
import { FormsModule } from '@angular/forms';
243+
import { BrowserModule } from '@angular/platform-browser';
244+
import { PubsubModule } from '@fsms/angular-pubsub';
245+
import { AppComponent } from './app.component';
246+
import { ShipOrderService } from './services/ship-order.service';
247+
248+
@NgModule({
249+
declarations: [AppComponent],
250+
imports: [
251+
BrowserModule,
252+
FormsModule,
253+
PubsubModule.forRoot([ // Register App Module level Message Handlers
254+
ShipOrderService, 👈
255+
]),
256+
],
257+
providers: [],
258+
bootstrap: [AppComponent],
259+
})
260+
export class AppModule {}
261+
```
262+
263+
**Creating Message Handler at Feature Module Level**
264+
265+
In order to achieve true service oriented architecture. You must create independent isolated feature modules. The message handlers gives you the power to register your message handlers at feature module level.
266+
267+
1. First create your message handler at Feature Module.
268+
269+
Example: `Create Order` Message handler in `Orders` module.
270+
271+
```ts
272+
import { CallbackOptions, IHandleMessage } from '@fsms/angular-pubsub';
273+
import { RegisterHandler } from 'projects/fsms-angular-pubsub/src/lib/pubsub-decorator';
274+
import { OrderCreated } from 'src/app/messages/order-created-message';
275+
import { PlaceOrder } from '../messages/place-order-message';
276+
277+
@RegisterHandler({👈 // Create as Message Handler
278+
messages: [PlaceOrder],
279+
})
280+
export class CreateOrderService implements IHandleMessage<PlaceOrder> {
281+
constructor() {}
282+
283+
handle({ message, context }: CallbackOptions<PlaceOrder>): void {
284+
console.log(`[Sales] Order Created`, message);
285+
286+
context.publish(
287+
new OrderCreated({
288+
orderId: new Date().getTime().toString(36),
289+
item: message.payload,
290+
})
291+
);
292+
}
293+
}
294+
295+
```
296+
297+
2. Register your message handler in Feature Module.
298+
299+
Use `PubsubModule.forFeature([])` to register your feature message handlers.
300+
301+
Example: Registering `CreateOrderService` at orders module.
302+
303+
```ts
304+
import { NgModule } from '@angular/core';
305+
import { CommonModule } from '@angular/common';
306+
import { SubmitOrderComponent } from './submit-order/submit-order.component';
307+
import { CreateOrderService } from './services/create-order.service';
308+
import { PubsubModule } from '@fsms/angular-pubsub';
309+
310+
@NgModule({
311+
declarations: [SubmitOrderComponent],
312+
imports: [CommonModule,
313+
PubsubModule.forFeature([CreateOrderService]) 👈 // Registering as feature message handler
314+
],
315+
exports: [SubmitOrderComponent],
316+
})
317+
export class OrdersModule {}
318+
```
319+
320+
## Contributions
102321

103-
---
322+
Contributions are welcome!🙂 If you find any problems or would like to contribute in any way, feel free to create a pull request/open an issue/send me a message.
104323

105-
**Thank You!**
324+
You can also contribute by becoming an [official sponsor](https://github.com/sponsors/rupeshtiwari) to help keep Angular Pub-Sub well-maintained.
106325

107326
💖 Say 👋 to me!
108327
Rupesh Tiwari
109328
<a href="https://www.rupeshtiwari.com"> www.rupeshtiwari.com</a>
110-
✉️ <a href="mailto:[email protected]?subject=Hi"> Email Rupesh</a>
329+
✉️ <a href="mailto:[email protected]?subject=Hi"> Email Rupesh Tiwari</a>
111330
Founder of <a href="https://www.fullstackmaster.net"> Fullstack Master</a>
112331

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@fsms/angular-pubsub-app",
3-
"version": "1.0.19",
3+
"version": "2.0.0",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",

projects/fsms-angular-pubsub/src/lib/contracts/definitions.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ export abstract class Logger {
2323
}
2424

2525
export interface PubsubSubscription {
26-
unsubscribe();
26+
unsubscribe(): void;
2727
}

src/app/app.component.ts

+20-5
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,33 @@
1-
import { Component, OnInit } from '@angular/core';
2-
import { PubsubService } from '@fsms/angular-pubsub';
1+
import { Component, OnDestroy, OnInit } from '@angular/core';
2+
import { PubsubService, PubsubSubscription } from '@fsms/angular-pubsub';
3+
import { OrderCreated } from './messages/order-created-message';
34
import { OrderReady } from './messages/order-ready-message';
45
import { OrderShipped } from './messages/order-shipped-message';
5-
import { OrderCreated } from './messages/order-created-message';
6+
import { PlaceOrder } from './orders/messages/place-order-message';
67

78
@Component({
89
selector: 'app-root',
910
templateUrl: './app.component.html',
1011
styleUrls: ['./app.component.css'],
1112
})
12-
export class AppComponent implements OnInit {
13+
export class AppComponent implements OnInit, OnDestroy {
14+
subscriptions: PubsubSubscription[] = [];
15+
1316
constructor(private pubsubService: PubsubService) {}
1417

15-
ngOnInit(): void {}
18+
ngOnInit(): void {
19+
// HERE >=
20+
this.subscriptions.push(
21+
this.pubsubService.subscribe({
22+
messageType: PlaceOrder.messageType,
23+
callback: (msg) => console.log('received', msg),
24+
})
25+
);
26+
}
27+
28+
ngOnDestroy(): void {
29+
this.subscriptions.forEach((s) => s.unsubscribe());
30+
}
1631

1732
orderPlaced($event: KeyboardEvent) {
1833
$event.preventDefault();

0 commit comments

Comments
 (0)