smart_message 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,364 @@
1
+ # Entity Addressing
2
+
3
+ SmartMessage supports entity-to-entity addressing through built-in FROM/TO/REPLY_TO fields in message headers. This enables sophisticated messaging patterns including point-to-point communication, broadcast messaging, and request-reply workflows.
4
+
5
+ ## Overview
6
+
7
+ Entity addressing in SmartMessage provides:
8
+
9
+ - **Sender Identification**: Required `from` field identifies the sending entity
10
+ - **Recipient Targeting**: Optional `to` field for point-to-point messaging
11
+ - **Response Routing**: Optional `reply_to` field for request-reply patterns
12
+ - **Broadcast Support**: Omitting `to` field enables broadcast to all subscribers
13
+ - **Dual-Level Configuration**: Class and instance-level addressing configuration
14
+
15
+ ## Address Fields
16
+
17
+ ### FROM (Required)
18
+ The `from` field identifies the entity sending the message. This is required for all messages.
19
+
20
+ ```ruby
21
+ class MyMessage < SmartMessage::Base
22
+ from 'order-service' # Required sender identity
23
+
24
+ property :data
25
+ end
26
+ ```
27
+
28
+ ### TO (Optional)
29
+ The `to` field specifies the intended recipient entity. When present, creates point-to-point messaging. When `nil`, the message is broadcast to all subscribers.
30
+
31
+ ```ruby
32
+ # Point-to-point messaging
33
+ class DirectMessage < SmartMessage::Base
34
+ from 'sender-service'
35
+ to 'recipient-service' # Specific target
36
+
37
+ property :content
38
+ end
39
+
40
+ # Broadcast messaging
41
+ class AnnouncementMessage < SmartMessage::Base
42
+ from 'admin-service'
43
+ # No 'to' field = broadcast to all subscribers
44
+
45
+ property :announcement
46
+ end
47
+ ```
48
+
49
+ ### REPLY_TO (Optional)
50
+ The `reply_to` field specifies where responses should be sent. Defaults to the `from` entity if not specified.
51
+
52
+ ```ruby
53
+ class RequestMessage < SmartMessage::Base
54
+ from 'client-service'
55
+ to 'api-service'
56
+ reply_to 'client-callback-service' # Responses go here
57
+
58
+ property :request_data
59
+ end
60
+ ```
61
+
62
+ ## Configuration Patterns
63
+
64
+ ### Class-Level Configuration
65
+
66
+ Set default addressing for all instances of a message class:
67
+
68
+ ```ruby
69
+ class PaymentMessage < SmartMessage::Base
70
+ version 1
71
+
72
+ # Class-level addressing
73
+ from 'payment-service'
74
+ to 'bank-gateway'
75
+ reply_to 'payment-service'
76
+
77
+ property :amount, required: true
78
+ property :account_id, required: true
79
+ end
80
+
81
+ # All instances inherit class addressing
82
+ payment = PaymentMessage.new(amount: 100.00, account_id: "ACCT-123")
83
+ puts payment._sm_header.from # => 'payment-service'
84
+ puts payment._sm_header.to # => 'bank-gateway'
85
+ puts payment._sm_header.reply_to # => 'payment-service'
86
+ ```
87
+
88
+ ### Instance-Level Overrides
89
+
90
+ Override addressing for specific message instances:
91
+
92
+ ```ruby
93
+ class FlexibleMessage < SmartMessage::Base
94
+ from 'service-a'
95
+ to 'service-b'
96
+
97
+ property :data
98
+ end
99
+
100
+ # Override addressing for this instance
101
+ message = FlexibleMessage.new(data: "test")
102
+ message.from('different-sender')
103
+ message.to('different-recipient')
104
+ message.reply_to('different-reply-service')
105
+
106
+ puts message.from # => 'different-sender'
107
+ puts message.to # => 'different-recipient'
108
+ puts message.reply_to # => 'different-reply-service'
109
+
110
+ # Class defaults remain unchanged
111
+ puts FlexibleMessage.from # => 'service-a'
112
+ puts FlexibleMessage.to # => 'service-b'
113
+ ```
114
+
115
+ ## Messaging Patterns
116
+
117
+ ### Point-to-Point Messaging
118
+
119
+ Direct communication between two specific entities:
120
+
121
+ ```ruby
122
+ class OrderProcessingMessage < SmartMessage::Base
123
+ from 'order-service'
124
+ to 'inventory-service' # Direct target
125
+ reply_to 'order-service'
126
+
127
+ property :order_id, required: true
128
+ property :items, required: true
129
+ end
130
+
131
+ # Message goes directly to inventory-service
132
+ order = OrderProcessingMessage.new(
133
+ order_id: "ORD-123",
134
+ items: ["widget-1", "widget-2"]
135
+ )
136
+ order.publish # Only inventory-service receives this
137
+ ```
138
+
139
+ ### Broadcast Messaging
140
+
141
+ Send message to all subscribers by omitting the `to` field:
142
+
143
+ ```ruby
144
+ class SystemMaintenanceMessage < SmartMessage::Base
145
+ from 'admin-service'
146
+ # No 'to' field = broadcast
147
+
148
+ property :message, required: true
149
+ property :scheduled_time, required: true
150
+ end
151
+
152
+ # Message goes to all subscribers
153
+ maintenance = SystemMaintenanceMessage.new(
154
+ message: "System maintenance tonight at 2 AM",
155
+ scheduled_time: Time.parse("2024-01-15 02:00:00")
156
+ )
157
+ maintenance.publish # All subscribers receive this
158
+ ```
159
+
160
+ ### Request-Reply Pattern
161
+
162
+ Structured request-response communication:
163
+
164
+ ```ruby
165
+ # Request message
166
+ class UserLookupRequest < SmartMessage::Base
167
+ from 'web-service'
168
+ to 'user-service'
169
+ reply_to 'web-service' # Responses come back here
170
+
171
+ property :user_id, required: true
172
+ property :request_id, required: true # For correlation
173
+ end
174
+
175
+ # Response message
176
+ class UserLookupResponse < SmartMessage::Base
177
+ from 'user-service'
178
+ # 'to' will be set to the original 'reply_to' value
179
+
180
+ property :user_id, required: true
181
+ property :request_id, required: true # Correlation ID
182
+ property :user_data
183
+ property :success, default: true
184
+ end
185
+
186
+ # Send request
187
+ request = UserLookupRequest.new(
188
+ user_id: "USER-123",
189
+ request_id: SecureRandom.uuid
190
+ )
191
+ request.publish
192
+
193
+ # In user-service handler:
194
+ class UserLookupRequest
195
+ def self.process(header, payload)
196
+ request_data = JSON.parse(payload)
197
+
198
+ # Process lookup...
199
+ user_data = UserService.find(request_data['user_id'])
200
+
201
+ # Send response back to reply_to address
202
+ response = UserLookupResponse.new(
203
+ user_id: request_data['user_id'],
204
+ request_id: request_data['request_id'],
205
+ user_data: user_data
206
+ )
207
+ response.to(header.reply_to) # Send to original reply_to
208
+ response.publish
209
+ end
210
+ end
211
+ ```
212
+
213
+ ### Gateway Pattern
214
+
215
+ Forward messages between different transports/formats:
216
+
217
+ ```ruby
218
+ class GatewayMessage < SmartMessage::Base
219
+ from 'gateway-service'
220
+
221
+ property :original_message
222
+ property :source_format
223
+ property :target_format
224
+ end
225
+
226
+ # Receive from one transport/format
227
+ incoming = SomeMessage.new(data: "from system A")
228
+
229
+ # Forward to different system with different addressing
230
+ gateway_msg = GatewayMessage.new(
231
+ original_message: incoming.to_h,
232
+ source_format: 'json',
233
+ target_format: 'xml'
234
+ )
235
+
236
+ # Override transport and addressing for forwarding
237
+ gateway_msg.config do
238
+ transport DifferentTransport.new
239
+ serializer DifferentSerializer.new
240
+ end
241
+ gateway_msg.to('system-b')
242
+ gateway_msg.publish
243
+ ```
244
+
245
+ ## Address Validation
246
+
247
+ The `from` field is required and validated automatically:
248
+
249
+ ```ruby
250
+ class InvalidMessage < SmartMessage::Base
251
+ # No 'from' specified - will fail validation
252
+ property :data
253
+ end
254
+
255
+ message = InvalidMessage.new(data: "test")
256
+ message.publish # Raises SmartMessage::Errors::ValidationError
257
+ # => "The property 'from' From entity ID is required for message routing and replies"
258
+ ```
259
+
260
+ ## Header Access
261
+
262
+ Access addressing information from message headers:
263
+
264
+ ```ruby
265
+ class SampleMessage < SmartMessage::Base
266
+ from 'sample-service'
267
+ to 'target-service'
268
+ reply_to 'callback-service'
269
+
270
+ property :content
271
+ end
272
+
273
+ message = SampleMessage.new(content: "Hello")
274
+ header = message._sm_header
275
+
276
+ puts header.from # => 'sample-service'
277
+ puts header.to # => 'target-service'
278
+ puts header.reply_to # => 'callback-service'
279
+ puts header.uuid # => Generated UUID
280
+ puts header.message_class # => 'SampleMessage'
281
+ ```
282
+
283
+ ## Integration with Dispatcher
284
+
285
+ The dispatcher can use addressing metadata for advanced routing logic:
286
+
287
+ ```ruby
288
+ # Future enhancement: Dispatcher filtering by recipient
289
+ # dispatcher.route(message_header, payload) do |header|
290
+ # if header.to.nil?
291
+ # # Broadcast to all subscribers
292
+ # route_to_all_subscribers(header.message_class)
293
+ # else
294
+ # # Route only to specific recipient
295
+ # route_to_entity(header.to, header.message_class)
296
+ # end
297
+ # end
298
+ ```
299
+
300
+ ## Best Practices
301
+
302
+ ### Entity Naming
303
+ Use consistent, descriptive entity identifiers:
304
+
305
+ ```ruby
306
+ # Good: Descriptive service names
307
+ from 'order-management-service'
308
+ to 'inventory-tracking-service'
309
+ reply_to 'order-status-service'
310
+
311
+ # Avoid: Generic or unclear names
312
+ from 'service1'
313
+ to 'app'
314
+ ```
315
+
316
+ ### Address Consistency
317
+ Maintain consistent addressing patterns across your application:
318
+
319
+ ```ruby
320
+ # Consistent pattern for microservices
321
+ class OrderMessage < SmartMessage::Base
322
+ from 'order-service'
323
+ to 'fulfillment-service'
324
+ reply_to 'order-service'
325
+ end
326
+
327
+ class PaymentMessage < SmartMessage::Base
328
+ from 'payment-service'
329
+ to 'billing-service'
330
+ reply_to 'payment-service'
331
+ end
332
+ ```
333
+
334
+ ### Gateway Configuration
335
+ For gateway patterns, use instance-level overrides:
336
+
337
+ ```ruby
338
+ # Class defines default routing
339
+ class APIMessage < SmartMessage::Base
340
+ from 'api-gateway'
341
+ to 'internal-service'
342
+ end
343
+
344
+ # Override for external routing
345
+ message = APIMessage.new(data: "external request")
346
+ message.to('external-partner-service')
347
+ message.config do
348
+ transport ExternalTransport.new
349
+ serializer SecureSerializer.new
350
+ end
351
+ message.publish
352
+ ```
353
+
354
+ ## Future Enhancements
355
+
356
+ The addressing system provides the foundation for advanced features:
357
+
358
+ - **Dispatcher Filtering**: Route messages based on recipient targeting
359
+ - **Security Integration**: Entity-based authentication and authorization
360
+ - **Audit Trails**: Track message flow between entities
361
+ - **Load Balancing**: Distribute messages across entity instances
362
+ - **Circuit Breakers**: Per-entity failure handling
363
+
364
+ Entity addressing enables sophisticated messaging architectures while maintaining the simplicity and flexibility that makes SmartMessage powerful.
data/docs/architecture.md CHANGED
@@ -65,6 +65,8 @@ The foundation class that all messages inherit from, built on `Hashie::Dash`.
65
65
 
66
66
  ```ruby
67
67
  class MyMessage < SmartMessage::Base
68
+ description "Handles custom message processing for my application"
69
+
68
70
  property :data
69
71
 
70
72
  config do
data/docs/examples.md CHANGED
@@ -10,6 +10,8 @@ This document provides practical examples of using SmartMessage in real-world sc
10
10
  require 'smart_message'
11
11
 
12
12
  class NotificationMessage < SmartMessage::Base
13
+ description "Sends notifications to users via multiple channels"
14
+
13
15
  property :recipient
14
16
  property :subject
15
17
  property :body
@@ -37,6 +37,14 @@ Let's create a simple message class and see it in action:
37
37
  require 'smart_message'
38
38
 
39
39
  class WelcomeMessage < SmartMessage::Base
40
+ # Add a description for the message class
41
+ description "Welcomes new users after successful signup"
42
+
43
+ # Configure entity addressing
44
+ from 'user-service' # Required: identifies sender
45
+ to 'notification-service' # Optional: specific recipient
46
+ reply_to 'user-service' # Optional: where responses go
47
+
40
48
  # Define message properties
41
49
  property :user_name
42
50
  property :email
@@ -132,6 +140,8 @@ Messages use Hashie::Dash properties for type-safe attributes:
132
140
 
133
141
  ```ruby
134
142
  class OrderMessage < SmartMessage::Base
143
+ description "Represents customer orders for processing and fulfillment"
144
+
135
145
  property :order_id, required: true
136
146
  property :amount, transform_with: ->(v) { BigDecimal(v.to_s) }
137
147
  property :items, default: []
@@ -148,6 +158,9 @@ puts message._sm_header.uuid # Unique identifier
148
158
  puts message._sm_header.message_class # "WelcomeMessage"
149
159
  puts message._sm_header.published_at # Timestamp when published
150
160
  puts message._sm_header.publisher_pid # Process ID of publisher
161
+ puts message._sm_header.from # 'user-service'
162
+ puts message._sm_header.to # 'notification-service'
163
+ puts message._sm_header.reply_to # 'user-service'
151
164
  ```
152
165
 
153
166
  ### Transports
@@ -175,6 +188,8 @@ SmartMessage supports four types of message handlers to give you flexibility in
175
188
 
176
189
  ```ruby
177
190
  class OrderMessage < SmartMessage::Base
191
+ description "Handles customer order processing and fulfillment"
192
+
178
193
  # Define your message properties
179
194
  property :order_id
180
195
  property :amount
@@ -215,6 +230,36 @@ OrderMessage.subscribe("OrderService.process_order")
215
230
  - **Proc**: Reusable handlers that work across multiple message types
216
231
  - **Method**: Complex business logic organized in service classes
217
232
 
233
+ ### Entity Addressing
234
+
235
+ SmartMessage supports entity-to-entity addressing for sophisticated messaging patterns:
236
+
237
+ ```ruby
238
+ # Point-to-point messaging
239
+ class PaymentMessage < SmartMessage::Base
240
+ from 'payment-service' # Required: sender identity
241
+ to 'bank-gateway' # Optional: specific recipient
242
+ reply_to 'payment-service' # Optional: where responses go
243
+
244
+ property :amount, required: true
245
+ end
246
+
247
+ # Broadcast messaging (no 'to' field)
248
+ class AnnouncementMessage < SmartMessage::Base
249
+ from 'admin-service' # Required sender
250
+ # No 'to' = broadcast to all subscribers
251
+
252
+ property :message, required: true
253
+ end
254
+
255
+ # Instance-level addressing override
256
+ payment = PaymentMessage.new(amount: 100.00)
257
+ payment.to('backup-gateway') # Override destination
258
+ payment.publish
259
+ ```
260
+
261
+ For more details, see [Entity Addressing](addressing.md).
262
+
218
263
  ## Next Steps
219
264
 
220
265
  Now that you have the basics working, explore:
@@ -230,6 +275,8 @@ Now that you have the basics working, explore:
230
275
 
231
276
  ```ruby
232
277
  class NotificationMessage < SmartMessage::Base
278
+ description "Sends notifications to users via email, SMS, or push"
279
+
233
280
  property :recipient
234
281
  property :subject
235
282
  property :body
@@ -271,6 +318,8 @@ NotificationMessage.new(
271
318
 
272
319
  ```ruby
273
320
  class EventMessage < SmartMessage::Base
321
+ description "Logs application events for monitoring and analytics"
322
+
274
323
  property :event_type
275
324
  property :user_id
276
325
  property :data