smart_message 0.0.5 → 0.0.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +122 -0
- data/Gemfile.lock +9 -1
- data/README.md +58 -4
- data/docs/README.md +1 -0
- data/docs/addressing.md +364 -0
- data/docs/getting-started.md +38 -0
- data/examples/01_point_to_point_orders.rb +4 -2
- data/examples/02_publish_subscribe_events.rb +2 -1
- data/examples/03_many_to_many_chat.rb +10 -5
- data/examples/04_redis_smart_home_iot.rb +32 -16
- data/examples/05_proc_handlers.rb +3 -2
- data/examples/06_custom_logger_example.rb +13 -7
- data/examples/07_error_handling_scenarios.rb +26 -15
- data/examples/08_entity_addressing_basic.rb +373 -0
- data/examples/08_entity_addressing_with_filtering.rb +430 -0
- data/examples/README.md +68 -0
- data/examples/tmux_chat/bot_agent.rb +2 -1
- data/examples/tmux_chat/shared_chat_system.rb +4 -2
- data/lib/smart_message/base.rb +204 -12
- data/lib/smart_message/circuit_breaker.rb +227 -0
- data/lib/smart_message/dispatcher.rb +189 -20
- data/lib/smart_message/header.rb +14 -0
- data/lib/smart_message/serializer/base.rb +77 -4
- data/lib/smart_message/serializer/json.rb +2 -2
- data/lib/smart_message/transport/base.rb +141 -4
- data/lib/smart_message/transport/memory_transport.rb +1 -1
- data/lib/smart_message/transport/redis_transport.rb +45 -15
- data/lib/smart_message/transport/stdout_transport.rb +1 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +1 -0
- data/smart_message.gemspec +1 -0
- metadata +19 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 118cf15fea99493c202bd383a547358a44a3873284cfe656665701213b40b51e
|
4
|
+
data.tar.gz: 7e542557b5b88f2963291232a875ba4dab44c682673251d7e976ac913ff41c14
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 637591235fcdf1feb8708f244d1f2f6b416d7330bca7f06c597581ea9ffcb3eb55e74610bcc045c7839e50304f978fd24092c870528a7f3277cd0d8be2620ae4
|
7
|
+
data.tar.gz: 35773299f97565981fc778bd76358c2560eef1d6c4885367ddb66d81448136c8a8a9f402d26477d2c7ac29c885f07906a7e3f0396fb827fdf15e5bbe3e5a43bc
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,128 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
|
+
## [0.0.7] 2025-08-19
|
10
|
+
### Added
|
11
|
+
- **Production-Grade Circuit Breaker Integration**: Comprehensive reliability patterns using BreakerMachines gem
|
12
|
+
- Circuit breaker protection for message processing operations with configurable failure thresholds
|
13
|
+
- Transport-level circuit breakers for publish/subscribe operations with automatic fallback
|
14
|
+
- Integrated circuit breaker DSL throughout SmartMessage components for production reliability
|
15
|
+
- Memory and Redis storage backends for circuit breaker state persistence
|
16
|
+
- Built-in fallback mechanisms including dead letter queue, retry with exponential backoff, and graceful degradation
|
17
|
+
- Circuit breaker statistics and introspection capabilities for monitoring and debugging
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
- **Critical: Redis Transport Header Preservation**: Fixed message header information loss during Redis pub/sub transport
|
21
|
+
- **Root Cause**: Redis transport was only sending message payload through channels, reconstructing generic headers on receive
|
22
|
+
- **Impact**: Message addressing information (`from`, `to`, `reply_to`) was lost, breaking entity-aware filtering and routing
|
23
|
+
- **Solution**: Modified Redis transport to combine header and payload into JSON structure during publish/subscribe
|
24
|
+
- **Result**: Full header information now preserved across Redis transport with backward compatibility fallback
|
25
|
+
- Redis IoT example now displays all expected sensor data, alerts, and real-time device monitoring output
|
26
|
+
- **Performance: Dispatcher Shutdown Optimization**: Dramatically improved dispatcher shutdown speed
|
27
|
+
- **Previous Issue**: Slow 1-second polling with no timeout mechanism caused delays up to several minutes
|
28
|
+
- **Optimization**: Replaced with native `wait_for_termination(3)` using Concurrent::ThreadPool built-in timeout
|
29
|
+
- **Performance Gain**: Shutdown now completes in milliseconds instead of seconds
|
30
|
+
- Removed unnecessary circuit breaker wrapper - thread pool handles timeout and fallback natively
|
31
|
+
- **Code Quality: Transport Method Signature Consistency**: Ensured all transport `do_publish` methods use consistent parameters
|
32
|
+
- Standardized `do_publish(message_header, message_payload)` signature across all transport implementations
|
33
|
+
- Updated Redis, STDOUT, and Memory transports for consistent interface
|
34
|
+
|
35
|
+
### Changed
|
36
|
+
- **Dispatcher Architecture**: Simplified shutdown mechanism leveraging Concurrent::ThreadPool native capabilities
|
37
|
+
- Removed custom timeout polling logic in favor of built-in `wait_for_termination` method
|
38
|
+
- Eliminated redundant circuit breaker for shutdown operations - thread pool provides timeout and fallback
|
39
|
+
- **BREAKING**: Removed `router_pool_shutdown` circuit breaker configuration (no longer needed)
|
40
|
+
- **Redis Transport Protocol**: Enhanced message format to preserve complete header information
|
41
|
+
- Redis messages now contain both header and payload: `{header: {...}, payload: "..."}`
|
42
|
+
- Automatic fallback to legacy behavior for malformed or old-format messages
|
43
|
+
- Maintains full backward compatibility with existing Redis deployments
|
44
|
+
|
45
|
+
### Enhanced
|
46
|
+
- **Circuit Breaker Integration**: Strategic application of reliability patterns where most beneficial
|
47
|
+
- Message processing operations protected with configurable failure thresholds and fallback behavior
|
48
|
+
- Transport publish/subscribe operations wrapped with circuit breakers for external service protection
|
49
|
+
- Clean separation between internal operations (thread pools) and external dependencies (Redis, etc.)
|
50
|
+
- **Redis Transport Reliability**: Production-ready Redis pub/sub with complete message fidelity
|
51
|
+
- Full message header preservation enables proper entity-aware filtering and routing
|
52
|
+
- IoT examples now demonstrate complete real-time messaging with sensor data, alerts, and device commands
|
53
|
+
- Enhanced error handling with JSON parsing fallbacks for malformed messages
|
54
|
+
- **Developer Experience**: Cleaner, more maintainable codebase with proper separation of concerns
|
55
|
+
- Circuit breakers applied strategically for external dependencies, not internal thread management
|
56
|
+
- Simplified shutdown logic leverages proven Concurrent::ThreadPool patterns
|
57
|
+
- Clear distinction between reliability mechanisms and business logic
|
58
|
+
|
59
|
+
### Documentation
|
60
|
+
- **Circuit Breaker Patterns**: Examples of proper BreakerMachines DSL usage throughout codebase
|
61
|
+
- Strategic application for external service protection vs internal thread management
|
62
|
+
- Configuration examples for different failure scenarios and recovery patterns
|
63
|
+
- Best practices for production-grade messaging reliability
|
64
|
+
|
65
|
+
## [0.0.6] 2025-08-19
|
66
|
+
|
67
|
+
### Added
|
68
|
+
- **Entity-Aware Message Filtering**: Advanced subscription filtering system enabling precise message routing
|
69
|
+
- New subscription filter parameters: `broadcast:`, `to:`, `from:` for `subscribe` method
|
70
|
+
- Filter by broadcast messages only: `MyMessage.subscribe(broadcast: true)`
|
71
|
+
- Filter by destination entity: `MyMessage.subscribe(to: 'service-name')`
|
72
|
+
- Filter by sender entity: `MyMessage.subscribe(from: 'sender-service')`
|
73
|
+
- Support for array-based filters: `subscribe(from: ['admin', 'system'])`
|
74
|
+
- Combined filters with OR logic: `subscribe(broadcast: true, to: 'my-service')`
|
75
|
+
- String-to-array automatic conversion for convenience: `subscribe(to: 'service')` becomes `['service']`
|
76
|
+
- Full backward compatibility - existing subscriptions work unchanged
|
77
|
+
- **Real-Time Header Synchronization**: Message headers now update automatically when addressing fields change
|
78
|
+
- Instance-level `from()`, `to()`, `reply_to()` methods now update both instance variables and message headers
|
79
|
+
- Enables filtering to work correctly when addressing is changed after message creation
|
80
|
+
- Reset methods (`reset_from`, `reset_to`, `reset_reply_to`) also update headers
|
81
|
+
- Critical for entity-aware filtering functionality
|
82
|
+
- **Enhanced Example Programs**
|
83
|
+
- New comprehensive filtering example: `examples/08_entity_addressing_with_filtering.rb`
|
84
|
+
- Demonstrates all filtering capabilities with real-world microservice scenarios
|
85
|
+
- Improved timing with sleep statements for better output visualization
|
86
|
+
- Updated basic addressing example: `examples/08_entity_addressing_basic.rb`
|
87
|
+
- Both examples now show clear interleaved publish/receive output flow
|
88
|
+
|
89
|
+
### Changed
|
90
|
+
- **Message Subscription Architecture**: Subscription storage updated to support filtering
|
91
|
+
- Dispatcher now stores subscription objects with filter criteria instead of simple method strings
|
92
|
+
- Subscription objects contain `{process_method: string, filters: hash}` structure
|
93
|
+
- All transport implementations updated to pass filter options through subscription chain
|
94
|
+
- RedisTransport updated to accept new filter_options parameter
|
95
|
+
- **Header Addressing Behavior**: Message headers now reflect real-time addressing changes
|
96
|
+
- **BREAKING**: Headers are no longer immutable after creation - they update when addressing methods are called
|
97
|
+
- This change enables entity-aware filtering to work correctly with dynamic addressing
|
98
|
+
- Previous behavior kept original addressing in headers while instance methods showed new values
|
99
|
+
|
100
|
+
### Fixed
|
101
|
+
- **Message Filtering Logic**: Resolved critical filtering implementation issues
|
102
|
+
- Fixed message_matches_filters? method to properly evaluate filter criteria
|
103
|
+
- Corrected AND/OR logic for combined broadcast and entity-specific filters
|
104
|
+
- Ensured filtering works with both class-level and instance-level addressing
|
105
|
+
- **Test Suite Compatibility**: Updated all tests to work with new subscription structure
|
106
|
+
- Fixed ProcHandlerTest to expect subscription objects instead of string arrays
|
107
|
+
- Updated TransportTest to handle new subscription object format
|
108
|
+
- Fixed AddressingTest to reflect new real-time header update behavior
|
109
|
+
- All existing functionality preserved with full backward compatibility
|
110
|
+
|
111
|
+
### Enhanced
|
112
|
+
- **Message Processing Performance**: Filtering happens at subscription level, reducing processing overhead
|
113
|
+
- Messages not matching filter criteria are ignored by handlers, improving efficiency
|
114
|
+
- Enables microservices to process only relevant messages
|
115
|
+
- Reduces unnecessary handler invocations and processing cycles
|
116
|
+
- **Developer Experience**: Clear examples demonstrate filtering benefits and usage patterns
|
117
|
+
- Examples show both what DOES and DOESN'T get processed
|
118
|
+
- Demonstrates gateway patterns, admin message handling, and microservice communication
|
119
|
+
- Educational value enhanced with comprehensive console output and timing
|
120
|
+
|
121
|
+
### Documentation
|
122
|
+
- **Entity-Aware Filtering Guide**: Comprehensive examples showing all filtering capabilities
|
123
|
+
- Broadcast vs directed message handling patterns
|
124
|
+
- Admin/monitoring system integration examples
|
125
|
+
- Request-reply routing with filters
|
126
|
+
- Gateway pattern implementations with dynamic routing
|
127
|
+
- Best practices for microservice message filtering
|
128
|
+
- **Breaking Change Documentation**: Clear explanation of header behavior changes
|
129
|
+
- Migration guide for applications depending on immutable headers
|
130
|
+
- Benefits of real-time header updates for filtering functionality
|
9
131
|
|
10
132
|
## [0.0.5] 2025-08-18
|
11
133
|
|
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
smart_message (0.0.
|
4
|
+
smart_message (0.0.7)
|
5
5
|
activesupport
|
6
|
+
breaker_machines
|
6
7
|
concurrent-ruby
|
7
8
|
hashie
|
8
9
|
redis
|
@@ -27,6 +28,11 @@ GEM
|
|
27
28
|
base64 (0.3.0)
|
28
29
|
benchmark (0.4.1)
|
29
30
|
bigdecimal (3.2.2)
|
31
|
+
breaker_machines (0.4.0)
|
32
|
+
activesupport (>= 7.2)
|
33
|
+
concurrent-ruby (~> 1.3)
|
34
|
+
state_machines (>= 0.50.0)
|
35
|
+
zeitwerk (~> 2.7)
|
30
36
|
concurrent-ruby (1.3.5)
|
31
37
|
connection_pool (2.5.3)
|
32
38
|
debug_me (1.1.1)
|
@@ -53,9 +59,11 @@ GEM
|
|
53
59
|
shoulda-context (2.0.0)
|
54
60
|
shoulda-matchers (4.5.1)
|
55
61
|
activesupport (>= 4.2.0)
|
62
|
+
state_machines (0.100.1)
|
56
63
|
tzinfo (2.0.6)
|
57
64
|
concurrent-ruby (~> 1.0)
|
58
65
|
uri (1.0.3)
|
66
|
+
zeitwerk (2.7.3)
|
59
67
|
|
60
68
|
PLATFORMS
|
61
69
|
arm64-darwin-24
|
data/README.md
CHANGED
@@ -9,6 +9,7 @@ SmartMessage is a message abstraction framework that decouples business logic fr
|
|
9
9
|
|
10
10
|
- **Transport Abstraction**: Plugin architecture supporting multiple message transports (Redis, RabbitMQ, Kafka, etc.)
|
11
11
|
- **Serialization Flexibility**: Pluggable serialization formats (JSON, MessagePack, etc.)
|
12
|
+
- **Entity-to-Entity Addressing**: Built-in FROM/TO/REPLY_TO addressing for point-to-point and broadcast messaging patterns
|
12
13
|
- **Schema Versioning**: Built-in version management with automatic compatibility validation
|
13
14
|
- **Comprehensive Validation**: Property validation with custom error messages and automatic validation before publishing
|
14
15
|
- **Message Documentation**: Built-in documentation support for message classes and properties with automatic defaults
|
@@ -47,6 +48,11 @@ class OrderMessage < SmartMessage::Base
|
|
47
48
|
# Add a description for the message class
|
48
49
|
description "Represents customer order data for processing and fulfillment"
|
49
50
|
|
51
|
+
# Configure entity addressing
|
52
|
+
from 'order-service'
|
53
|
+
to 'fulfillment-service' # Point-to-point message
|
54
|
+
reply_to 'order-service' # Responses come back here
|
55
|
+
|
50
56
|
# Required properties with validation
|
51
57
|
property :order_id,
|
52
58
|
required: true,
|
@@ -153,6 +159,50 @@ end
|
|
153
159
|
OrderMessage.subscribe(audit_handler)
|
154
160
|
```
|
155
161
|
|
162
|
+
### 4. Entity Addressing
|
163
|
+
|
164
|
+
SmartMessage supports entity-to-entity addressing with FROM/TO/REPLY_TO fields for advanced message routing:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
# Point-to-point messaging
|
168
|
+
class PaymentMessage < SmartMessage::Base
|
169
|
+
version 1
|
170
|
+
from 'payment-service' # Required: sender identity
|
171
|
+
to 'bank-gateway' # Optional: specific recipient
|
172
|
+
reply_to 'payment-service' # Optional: where responses go
|
173
|
+
|
174
|
+
property :amount, required: true
|
175
|
+
property :account_id, required: true
|
176
|
+
end
|
177
|
+
|
178
|
+
# Broadcast messaging (no 'to' specified)
|
179
|
+
class SystemAnnouncementMessage < SmartMessage::Base
|
180
|
+
version 1
|
181
|
+
from 'admin-service' # Required: sender identity
|
182
|
+
# No 'to' field = broadcast to all subscribers
|
183
|
+
|
184
|
+
property :message, required: true
|
185
|
+
property :priority, default: 'normal'
|
186
|
+
end
|
187
|
+
|
188
|
+
# Instance-level addressing override
|
189
|
+
payment = PaymentMessage.new(amount: 100.00, account_id: "ACCT-123")
|
190
|
+
payment.to('backup-gateway') # Override destination for this instance
|
191
|
+
payment.publish
|
192
|
+
|
193
|
+
# Access addressing information
|
194
|
+
puts payment._sm_header.from # => 'payment-service'
|
195
|
+
puts payment._sm_header.to # => 'backup-gateway'
|
196
|
+
puts payment._sm_header.reply_to # => 'payment-service'
|
197
|
+
```
|
198
|
+
|
199
|
+
#### Messaging Patterns Supported
|
200
|
+
|
201
|
+
- **Point-to-Point**: Set `to` field for direct entity targeting
|
202
|
+
- **Broadcast**: Omit `to` field (nil) for message broadcast to all subscribers
|
203
|
+
- **Request-Reply**: Use `reply_to` field to specify response routing
|
204
|
+
- **Gateway Patterns**: Override addressing at instance level for message forwarding
|
205
|
+
|
156
206
|
## Architecture
|
157
207
|
|
158
208
|
### Core Components
|
@@ -323,15 +373,16 @@ end
|
|
323
373
|
## Message Lifecycle
|
324
374
|
|
325
375
|
1. **Definition**: Create message class inheriting from `SmartMessage::Base`
|
326
|
-
2. **Configuration**: Set transport, serializer, and
|
327
|
-
3. **Validation**: Messages are automatically validated before publishing (properties, header, version compatibility)
|
328
|
-
4. **Publishing**: Message instance is encoded and sent through transport
|
376
|
+
2. **Configuration**: Set transport, serializer, logger plugins, and entity addressing (from/to/reply_to)
|
377
|
+
3. **Validation**: Messages are automatically validated before publishing (properties, header, addressing, version compatibility)
|
378
|
+
4. **Publishing**: Message instance is encoded with addressing metadata and sent through transport
|
329
379
|
5. **Subscription**: Message classes register handlers with dispatcher for processing
|
330
380
|
- Default handlers (`self.process` method)
|
331
381
|
- Custom method handlers (`"ClassName.method_name"`)
|
332
382
|
- Block handlers (`subscribe do |h,p|...end`)
|
333
383
|
- Proc/Lambda handlers (`subscribe(proc {...})`)
|
334
|
-
6. **
|
384
|
+
6. **Routing**: Dispatcher uses addressing metadata to route messages (point-to-point vs broadcast)
|
385
|
+
7. **Processing**: Received messages are decoded and routed to registered handlers
|
335
386
|
|
336
387
|
## Schema Versioning and Validation
|
337
388
|
|
@@ -588,6 +639,9 @@ puts message._sm_header.uuid
|
|
588
639
|
puts message._sm_header.message_class
|
589
640
|
puts message._sm_header.published_at
|
590
641
|
puts message._sm_header.publisher_pid
|
642
|
+
puts message._sm_header.from
|
643
|
+
puts message._sm_header.to
|
644
|
+
puts message._sm_header.reply_to
|
591
645
|
```
|
592
646
|
|
593
647
|
## Development
|
data/docs/README.md
CHANGED
@@ -16,6 +16,7 @@ Welcome to the comprehensive documentation for SmartMessage, a Ruby gem that abs
|
|
16
16
|
### Components
|
17
17
|
- [SmartMessage::Base](base.md)
|
18
18
|
- [Property System](properties.md)
|
19
|
+
- [Entity Addressing](addressing.md)
|
19
20
|
- [Transport Layer](transports.md)
|
20
21
|
- [Serializers](serializers.md)
|
21
22
|
- [Logging System](logging.md)
|
data/docs/addressing.md
ADDED
@@ -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.
|