smart_message 0.0.10 → 0.0.12
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/.github/workflows/deploy-github-pages.yml +38 -0
- data/.gitignore +5 -0
- data/CHANGELOG.md +30 -0
- data/Gemfile.lock +35 -4
- data/README.md +169 -71
- data/Rakefile +29 -4
- data/docs/assets/images/ddq_architecture.svg +130 -0
- data/docs/assets/images/dlq_architecture.svg +115 -0
- data/docs/assets/images/enhanced-dual-publishing.svg +136 -0
- data/docs/assets/images/enhanced-fluent-api.svg +149 -0
- data/docs/assets/images/enhanced-microservices-routing.svg +115 -0
- data/docs/assets/images/enhanced-pattern-matching.svg +107 -0
- data/docs/assets/images/fluent-api-demo.svg +59 -0
- data/docs/assets/images/performance-comparison.svg +161 -0
- data/docs/assets/images/redis-basic-architecture.svg +53 -0
- data/docs/assets/images/redis-enhanced-architecture.svg +88 -0
- data/docs/assets/images/redis-queue-architecture.svg +101 -0
- data/docs/assets/images/smart_message.jpg +0 -0
- data/docs/assets/images/smart_message_walking.jpg +0 -0
- data/docs/assets/images/smartmessage_architecture_overview.svg +173 -0
- data/docs/assets/images/transport-comparison-matrix.svg +171 -0
- data/docs/assets/javascripts/mathjax.js +17 -0
- data/docs/assets/stylesheets/extra.css +51 -0
- data/docs/{addressing.md → core-concepts/addressing.md} +5 -7
- data/docs/{architecture.md → core-concepts/architecture.md} +78 -138
- data/docs/{dispatcher.md → core-concepts/dispatcher.md} +21 -21
- data/docs/{message_filtering.md → core-concepts/message-filtering.md} +2 -3
- data/docs/{message_processing.md → core-concepts/message-processing.md} +17 -17
- data/docs/{troubleshooting.md → development/troubleshooting.md} +7 -7
- data/docs/{examples.md → getting-started/examples.md} +115 -89
- data/docs/{getting-started.md → getting-started/quick-start.md} +47 -18
- data/docs/guides/redis-queue-getting-started.md +697 -0
- data/docs/guides/redis-queue-patterns.md +889 -0
- data/docs/guides/redis-queue-production.md +1091 -0
- data/docs/index.md +64 -0
- data/docs/{dead_letter_queue.md → reference/dead-letter-queue.md} +2 -3
- data/docs/{logging.md → reference/logging.md} +1 -1
- data/docs/{message_deduplication.md → reference/message-deduplication.md} +1 -0
- data/docs/{proc_handlers_summary.md → reference/proc-handlers.md} +7 -6
- data/docs/{serializers.md → reference/serializers.md} +3 -5
- data/docs/{transports.md → reference/transports.md} +133 -11
- data/docs/transports/memory-transport.md +374 -0
- data/docs/transports/redis-enhanced-transport.md +524 -0
- data/docs/transports/redis-queue-transport.md +1304 -0
- data/docs/transports/redis-transport-comparison.md +496 -0
- data/docs/transports/redis-transport.md +509 -0
- data/examples/README.md +98 -5
- data/examples/city_scenario/911_emergency_call_flow.svg +99 -0
- data/examples/city_scenario/README.md +515 -0
- data/examples/city_scenario/ai_visitor_intelligence_flow.svg +108 -0
- data/examples/city_scenario/citizen.rb +195 -0
- data/examples/city_scenario/city_diagram.svg +125 -0
- data/examples/city_scenario/common/health_monitor.rb +80 -0
- data/examples/city_scenario/common/logger.rb +30 -0
- data/examples/city_scenario/emergency_dispatch_center.rb +270 -0
- data/examples/city_scenario/fire_department.rb +446 -0
- data/examples/city_scenario/fire_emergency_flow.svg +95 -0
- data/examples/city_scenario/health_department.rb +100 -0
- data/examples/city_scenario/health_monitoring_system.svg +130 -0
- data/examples/city_scenario/house.rb +244 -0
- data/examples/city_scenario/local_bank.rb +217 -0
- data/examples/city_scenario/messages/emergency_911_message.rb +81 -0
- data/examples/city_scenario/messages/emergency_resolved_message.rb +43 -0
- data/examples/city_scenario/messages/fire_dispatch_message.rb +43 -0
- data/examples/city_scenario/messages/fire_emergency_message.rb +45 -0
- data/examples/city_scenario/messages/health_check_message.rb +22 -0
- data/examples/city_scenario/messages/health_status_message.rb +35 -0
- data/examples/city_scenario/messages/police_dispatch_message.rb +46 -0
- data/examples/city_scenario/messages/silent_alarm_message.rb +38 -0
- data/examples/city_scenario/police_department.rb +316 -0
- data/examples/city_scenario/redis_monitor.rb +129 -0
- data/examples/city_scenario/redis_stats.rb +743 -0
- data/examples/city_scenario/room_for_improvement.md +240 -0
- data/examples/city_scenario/security_emergency_flow.svg +95 -0
- data/examples/city_scenario/service_internal_architecture.svg +154 -0
- data/examples/city_scenario/smart_message_ai_agent.rb +364 -0
- data/examples/city_scenario/start_demo.sh +236 -0
- data/examples/city_scenario/stop_demo.sh +106 -0
- data/examples/city_scenario/visitor.rb +631 -0
- data/examples/{10_message_deduplication.rb → memory/01_message_deduplication_demo.rb} +1 -1
- data/examples/{09_dead_letter_queue_demo.rb → memory/02_dead_letter_queue_demo.rb} +13 -40
- data/examples/{01_point_to_point_orders.rb → memory/03_point_to_point_orders.rb} +1 -1
- data/examples/{02_publish_subscribe_events.rb → memory/04_publish_subscribe_events.rb} +2 -2
- data/examples/{03_many_to_many_chat.rb → memory/05_many_to_many_chat.rb} +4 -4
- data/examples/{show_me.rb → memory/06_pretty_print_demo.rb} +1 -1
- data/examples/{05_proc_handlers.rb → memory/07_proc_handlers_demo.rb} +2 -2
- data/examples/{06_custom_logger_example.rb → memory/08_custom_logger_demo.rb} +17 -14
- data/examples/{07_error_handling_scenarios.rb → memory/09_error_handling_demo.rb} +4 -4
- data/examples/{08_entity_addressing_basic.rb → memory/10_entity_addressing_basic.rb} +8 -8
- data/examples/{08_entity_addressing_with_filtering.rb → memory/11_entity_addressing_with_filtering.rb} +6 -6
- data/examples/{09_regex_filtering_microservices.rb → memory/12_regex_filtering_microservices.rb} +2 -2
- data/examples/{10_header_block_configuration.rb → memory/13_header_block_configuration.rb} +6 -6
- data/examples/{11_global_configuration_example.rb → memory/14_global_configuration_demo.rb} +19 -8
- data/examples/{show_logger.rb → memory/15_logger_demo.rb} +1 -1
- data/examples/memory/README.md +163 -0
- data/examples/memory/memory_transport_architecture.svg +90 -0
- data/examples/memory/point_to_point_pattern.svg +94 -0
- data/examples/memory/publish_subscribe_pattern.svg +125 -0
- data/examples/{04_redis_smart_home_iot.rb → redis/01_smart_home_iot_demo.rb} +5 -5
- data/examples/redis/README.md +230 -0
- data/examples/redis/alert_system_flow.svg +127 -0
- data/examples/redis/dashboard_status_flow.svg +107 -0
- data/examples/redis/device_command_flow.svg +113 -0
- data/examples/redis/redis_transport_architecture.svg +115 -0
- data/examples/{smart_home_iot_dataflow.md → redis/smart_home_iot_dataflow.md} +4 -116
- data/examples/redis/smart_home_system_architecture.svg +133 -0
- data/examples/redis_enhanced/README.md +319 -0
- data/examples/redis_enhanced/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_enhanced/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_enhanced/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_enhanced/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/01_basic_messaging.rb +221 -0
- data/examples/redis_queue/01_comprehensive_examples.rb +508 -0
- data/examples/redis_queue/02_pattern_routing.rb +405 -0
- data/examples/redis_queue/03_fluent_api.rb +422 -0
- data/examples/redis_queue/04_load_balancing.rb +486 -0
- data/examples/redis_queue/05_microservices.rb +735 -0
- data/examples/redis_queue/06_emergency_alerts.rb +777 -0
- data/examples/redis_queue/07_queue_management.rb +587 -0
- data/examples/redis_queue/README.md +366 -0
- data/examples/redis_queue/enhanced_01_basic_patterns.rb +233 -0
- data/examples/redis_queue/enhanced_02_fluent_api.rb +331 -0
- data/examples/redis_queue/enhanced_03_dual_publishing.rb +281 -0
- data/examples/redis_queue/enhanced_04_advanced_routing.rb +419 -0
- data/examples/redis_queue/redis_queue_architecture.svg +148 -0
- data/ideas/README.md +41 -0
- data/ideas/agents.md +1001 -0
- data/ideas/database_transport.md +980 -0
- data/ideas/improvement.md +359 -0
- data/ideas/meshage.md +1788 -0
- data/ideas/message_discovery.md +178 -0
- data/ideas/message_schema.md +1381 -0
- data/lib/smart_message/.idea/.gitignore +8 -0
- data/lib/smart_message/.idea/markdown.xml +6 -0
- data/lib/smart_message/.idea/misc.xml +4 -0
- data/lib/smart_message/.idea/modules.xml +8 -0
- data/lib/smart_message/.idea/smart_message.iml +16 -0
- data/lib/smart_message/.idea/vcs.xml +6 -0
- data/lib/smart_message/addressing.rb +15 -0
- data/lib/smart_message/base.rb +0 -2
- data/lib/smart_message/configuration.rb +1 -1
- data/lib/smart_message/logger.rb +15 -4
- data/lib/smart_message/plugins.rb +5 -2
- data/lib/smart_message/serializer.rb +14 -0
- data/lib/smart_message/transport/redis_enhanced_transport.rb +399 -0
- data/lib/smart_message/transport/redis_queue_transport.rb +555 -0
- data/lib/smart_message/transport/registry.rb +1 -0
- data/lib/smart_message/transport.rb +34 -1
- data/lib/smart_message/version.rb +1 -1
- data/lib/smart_message.rb +5 -52
- data/mkdocs.yml +184 -0
- data/p2p_plan.md +326 -0
- data/p2p_roadmap.md +287 -0
- data/smart_message.gemspec +2 -0
- data/smart_message.svg +51 -0
- metadata +170 -44
- data/docs/README.md +0 -57
- data/examples/dead_letters.jsonl +0 -12
- data/examples/temp.txt +0 -94
- data/examples/tmux_chat/README.md +0 -283
- data/examples/tmux_chat/bot_agent.rb +0 -278
- data/examples/tmux_chat/human_agent.rb +0 -199
- data/examples/tmux_chat/room_monitor.rb +0 -160
- data/examples/tmux_chat/shared_chat_system.rb +0 -328
- data/examples/tmux_chat/start_chat_demo.sh +0 -190
- data/examples/tmux_chat/stop_chat_demo.sh +0 -22
- /data/docs/{properties.md → core-concepts/properties.md} +0 -0
- /data/docs/{ideas_to_think_about.md → development/ideas.md} +0 -0
@@ -0,0 +1,735 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# examples/redis_queue/05_microservices.rb
|
3
|
+
# Microservices architecture with Redis Queue Transport
|
4
|
+
|
5
|
+
require_relative '../../lib/smart_message'
|
6
|
+
|
7
|
+
puts "🏗️ Redis Queue Transport - Microservices Architecture Demo"
|
8
|
+
puts "=" * 60
|
9
|
+
|
10
|
+
#==============================================================================
|
11
|
+
# Transport Configuration for Microservices
|
12
|
+
#==============================================================================
|
13
|
+
|
14
|
+
# Configure SmartMessage for microservices architecture
|
15
|
+
SmartMessage.configure do |config|
|
16
|
+
config.transport = :redis_queue
|
17
|
+
config.transport_options = {
|
18
|
+
url: 'redis://localhost:6379',
|
19
|
+
db: 5, # Use database 5 for microservices demo
|
20
|
+
queue_prefix: 'microservices',
|
21
|
+
consumer_group: 'service_workers'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
#==============================================================================
|
26
|
+
# Service Request/Response Message Classes
|
27
|
+
#==============================================================================
|
28
|
+
|
29
|
+
class ServiceRequest < SmartMessage::Base
|
30
|
+
transport :redis_queue
|
31
|
+
|
32
|
+
property :request_id, required: true
|
33
|
+
property :service, required: true
|
34
|
+
property :operation, required: true
|
35
|
+
property :payload, default: {}
|
36
|
+
property :correlation_id
|
37
|
+
property :timestamp, default: -> { Time.now }
|
38
|
+
|
39
|
+
def process
|
40
|
+
puts "📥 Service Request: #{service}.#{operation} [#{request_id}]"
|
41
|
+
puts " Payload: #{payload.inspect}" if payload.any?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ServiceResponse < SmartMessage::Base
|
46
|
+
transport :redis_queue
|
47
|
+
|
48
|
+
property :request_id, required: true
|
49
|
+
property :service, required: true
|
50
|
+
property :status, required: true # success, error, timeout
|
51
|
+
property :result, default: {}
|
52
|
+
property :error_message
|
53
|
+
property :processing_time_ms
|
54
|
+
property :timestamp, default: -> { Time.now }
|
55
|
+
|
56
|
+
def process
|
57
|
+
status_icon = case status
|
58
|
+
when 'success' then '✅'
|
59
|
+
when 'error' then '❌'
|
60
|
+
when 'timeout' then '⏰'
|
61
|
+
else '❓'
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "#{status_icon} Service Response: #{service} [#{request_id}] - #{status}"
|
65
|
+
puts " Result: #{result.inspect}" if result.any?
|
66
|
+
puts " Error: #{error_message}" if error_message
|
67
|
+
puts " Processing time: #{processing_time_ms}ms" if processing_time_ms
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class ServiceEvent < SmartMessage::Base
|
72
|
+
transport :redis_queue
|
73
|
+
|
74
|
+
property :event_id, required: true
|
75
|
+
property :event_type, required: true
|
76
|
+
property :service, required: true
|
77
|
+
property :entity_id
|
78
|
+
property :data, default: {}
|
79
|
+
property :timestamp, default: -> { Time.now }
|
80
|
+
|
81
|
+
def process
|
82
|
+
puts "📡 Service Event: #{service}.#{event_type} [#{event_id}]"
|
83
|
+
puts " Entity: #{entity_id}" if entity_id
|
84
|
+
puts " Data: #{data.inspect}" if data.any?
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#==============================================================================
|
89
|
+
# Microservice Implementations
|
90
|
+
#==============================================================================
|
91
|
+
|
92
|
+
class ApiGatewayService
|
93
|
+
def initialize(transport)
|
94
|
+
@transport = transport
|
95
|
+
@request_counter = 0
|
96
|
+
setup_subscriptions
|
97
|
+
end
|
98
|
+
|
99
|
+
def setup_subscriptions
|
100
|
+
# Listen for responses from downstream services
|
101
|
+
@transport.where
|
102
|
+
.to('api_gateway')
|
103
|
+
.subscribe do |message_class, message_data|
|
104
|
+
data = JSON.parse(message_data)
|
105
|
+
puts "🌐 API Gateway received response from #{data['service']}: #{data['status']}"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def handle_user_request(operation, payload = {})
|
110
|
+
@request_counter += 1
|
111
|
+
request_id = "REQ-#{sprintf('%06d', @request_counter)}"
|
112
|
+
|
113
|
+
puts "\n🌐 API Gateway: Processing #{operation} request [#{request_id}]"
|
114
|
+
|
115
|
+
# Route to appropriate service based on operation
|
116
|
+
service = case operation
|
117
|
+
when 'create_user', 'get_user', 'update_user' then 'user_service'
|
118
|
+
when 'create_order', 'get_order', 'cancel_order' then 'order_service'
|
119
|
+
when 'process_payment', 'refund_payment' then 'payment_service'
|
120
|
+
when 'send_email', 'send_sms' then 'notification_service'
|
121
|
+
else 'unknown_service'
|
122
|
+
end
|
123
|
+
|
124
|
+
ServiceRequest.new(
|
125
|
+
request_id: request_id,
|
126
|
+
service: service,
|
127
|
+
operation: operation,
|
128
|
+
payload: payload,
|
129
|
+
correlation_id: SecureRandom.uuid,
|
130
|
+
_sm_header: {
|
131
|
+
from: 'api_gateway',
|
132
|
+
to: service
|
133
|
+
}
|
134
|
+
).publish
|
135
|
+
|
136
|
+
request_id
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class UserService
|
141
|
+
def initialize(transport)
|
142
|
+
@transport = transport
|
143
|
+
@users = {} # In-memory user store for demo
|
144
|
+
setup_subscriptions
|
145
|
+
end
|
146
|
+
|
147
|
+
def setup_subscriptions
|
148
|
+
@transport.where
|
149
|
+
.to('user_service')
|
150
|
+
.subscribe do |message_class, message_data|
|
151
|
+
handle_request(JSON.parse(message_data))
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
private
|
156
|
+
|
157
|
+
def handle_request(data)
|
158
|
+
request_id = data['request_id']
|
159
|
+
operation = data['operation']
|
160
|
+
payload = data['payload'] || {}
|
161
|
+
|
162
|
+
puts "👤 User Service: Handling #{operation} [#{request_id}]"
|
163
|
+
|
164
|
+
start_time = Time.now
|
165
|
+
result, status, error = case operation
|
166
|
+
when 'create_user'
|
167
|
+
create_user(payload)
|
168
|
+
when 'get_user'
|
169
|
+
get_user(payload['user_id'])
|
170
|
+
when 'update_user'
|
171
|
+
update_user(payload['user_id'], payload)
|
172
|
+
else
|
173
|
+
[nil, 'error', 'Unknown operation']
|
174
|
+
end
|
175
|
+
end_time = Time.now
|
176
|
+
|
177
|
+
# Send response back to API Gateway
|
178
|
+
ServiceResponse.new(
|
179
|
+
request_id: request_id,
|
180
|
+
service: 'user_service',
|
181
|
+
status: status,
|
182
|
+
result: result || {},
|
183
|
+
error_message: error,
|
184
|
+
processing_time_ms: ((end_time - start_time) * 1000).round(2),
|
185
|
+
_sm_header: {
|
186
|
+
from: 'user_service',
|
187
|
+
to: 'api_gateway'
|
188
|
+
}
|
189
|
+
).publish
|
190
|
+
|
191
|
+
# Emit event if successful
|
192
|
+
if status == 'success' && operation == 'create_user'
|
193
|
+
ServiceEvent.new(
|
194
|
+
event_id: SecureRandom.uuid,
|
195
|
+
event_type: 'user_created',
|
196
|
+
service: 'user_service',
|
197
|
+
entity_id: result[:user_id],
|
198
|
+
data: { name: result[:name], email: result[:email] },
|
199
|
+
_sm_header: {
|
200
|
+
from: 'user_service',
|
201
|
+
to: 'event_bus'
|
202
|
+
}
|
203
|
+
).publish
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def create_user(payload)
|
208
|
+
user_id = "user_#{SecureRandom.hex(4)}"
|
209
|
+
user_data = {
|
210
|
+
user_id: user_id,
|
211
|
+
name: payload['name'],
|
212
|
+
email: payload['email'],
|
213
|
+
created_at: Time.now.iso8601
|
214
|
+
}
|
215
|
+
|
216
|
+
@users[user_id] = user_data
|
217
|
+
[user_data, 'success', nil]
|
218
|
+
rescue => e
|
219
|
+
[nil, 'error', e.message]
|
220
|
+
end
|
221
|
+
|
222
|
+
def get_user(user_id)
|
223
|
+
if user_id && @users[user_id]
|
224
|
+
[@users[user_id], 'success', nil]
|
225
|
+
else
|
226
|
+
[nil, 'error', 'User not found']
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
def update_user(user_id, updates)
|
231
|
+
if user_id && @users[user_id]
|
232
|
+
@users[user_id].merge!(updates)
|
233
|
+
[@users[user_id], 'success', nil]
|
234
|
+
else
|
235
|
+
[nil, 'error', 'User not found']
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
class OrderService
|
241
|
+
def initialize(transport)
|
242
|
+
@transport = transport
|
243
|
+
@orders = {}
|
244
|
+
setup_subscriptions
|
245
|
+
end
|
246
|
+
|
247
|
+
def setup_subscriptions
|
248
|
+
@transport.where
|
249
|
+
.to('order_service')
|
250
|
+
.subscribe do |message_class, message_data|
|
251
|
+
handle_request(JSON.parse(message_data))
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
private
|
256
|
+
|
257
|
+
def handle_request(data)
|
258
|
+
request_id = data['request_id']
|
259
|
+
operation = data['operation']
|
260
|
+
payload = data['payload'] || {}
|
261
|
+
|
262
|
+
puts "📦 Order Service: Handling #{operation} [#{request_id}]"
|
263
|
+
|
264
|
+
start_time = Time.now
|
265
|
+
result, status, error = case operation
|
266
|
+
when 'create_order'
|
267
|
+
create_order(payload)
|
268
|
+
when 'get_order'
|
269
|
+
get_order(payload['order_id'])
|
270
|
+
when 'cancel_order'
|
271
|
+
cancel_order(payload['order_id'])
|
272
|
+
else
|
273
|
+
[nil, 'error', 'Unknown operation']
|
274
|
+
end
|
275
|
+
end_time = Time.now
|
276
|
+
|
277
|
+
# Send response back
|
278
|
+
ServiceResponse.new(
|
279
|
+
request_id: request_id,
|
280
|
+
service: 'order_service',
|
281
|
+
status: status,
|
282
|
+
result: result || {},
|
283
|
+
error_message: error,
|
284
|
+
processing_time_ms: ((end_time - start_time) * 1000).round(2),
|
285
|
+
_sm_header: {
|
286
|
+
from: 'order_service',
|
287
|
+
to: 'api_gateway'
|
288
|
+
}
|
289
|
+
).publish
|
290
|
+
|
291
|
+
# Emit events for successful operations
|
292
|
+
if status == 'success'
|
293
|
+
emit_order_event(operation, result)
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
def create_order(payload)
|
298
|
+
order_id = "order_#{SecureRandom.hex(4)}"
|
299
|
+
order_data = {
|
300
|
+
order_id: order_id,
|
301
|
+
user_id: payload['user_id'],
|
302
|
+
items: payload['items'] || [],
|
303
|
+
total_amount: payload['total_amount'],
|
304
|
+
status: 'pending',
|
305
|
+
created_at: Time.now.iso8601
|
306
|
+
}
|
307
|
+
|
308
|
+
@orders[order_id] = order_data
|
309
|
+
[order_data, 'success', nil]
|
310
|
+
rescue => e
|
311
|
+
[nil, 'error', e.message]
|
312
|
+
end
|
313
|
+
|
314
|
+
def get_order(order_id)
|
315
|
+
if order_id && @orders[order_id]
|
316
|
+
[@orders[order_id], 'success', nil]
|
317
|
+
else
|
318
|
+
[nil, 'error', 'Order not found']
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
def cancel_order(order_id)
|
323
|
+
if order_id && @orders[order_id]
|
324
|
+
@orders[order_id][:status] = 'cancelled'
|
325
|
+
[@orders[order_id], 'success', nil]
|
326
|
+
else
|
327
|
+
[nil, 'error', 'Order not found']
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def emit_order_event(operation, order_data)
|
332
|
+
event_type = case operation
|
333
|
+
when 'create_order' then 'order_created'
|
334
|
+
when 'cancel_order' then 'order_cancelled'
|
335
|
+
else "order_#{operation}"
|
336
|
+
end
|
337
|
+
|
338
|
+
ServiceEvent.new(
|
339
|
+
event_id: SecureRandom.uuid,
|
340
|
+
event_type: event_type,
|
341
|
+
service: 'order_service',
|
342
|
+
entity_id: order_data[:order_id],
|
343
|
+
data: order_data,
|
344
|
+
_sm_header: {
|
345
|
+
from: 'order_service',
|
346
|
+
to: 'event_bus'
|
347
|
+
}
|
348
|
+
).publish
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
class PaymentService
|
353
|
+
def initialize(transport)
|
354
|
+
@transport = transport
|
355
|
+
@payments = {}
|
356
|
+
setup_subscriptions
|
357
|
+
end
|
358
|
+
|
359
|
+
def setup_subscriptions
|
360
|
+
@transport.where
|
361
|
+
.to('payment_service')
|
362
|
+
.subscribe do |message_class, message_data|
|
363
|
+
handle_request(JSON.parse(message_data))
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
private
|
368
|
+
|
369
|
+
def handle_request(data)
|
370
|
+
request_id = data['request_id']
|
371
|
+
operation = data['operation']
|
372
|
+
payload = data['payload'] || {}
|
373
|
+
|
374
|
+
puts "💳 Payment Service: Handling #{operation} [#{request_id}]"
|
375
|
+
|
376
|
+
start_time = Time.now
|
377
|
+
result, status, error = case operation
|
378
|
+
when 'process_payment'
|
379
|
+
process_payment(payload)
|
380
|
+
when 'refund_payment'
|
381
|
+
refund_payment(payload['payment_id'])
|
382
|
+
else
|
383
|
+
[nil, 'error', 'Unknown operation']
|
384
|
+
end
|
385
|
+
end_time = Time.now
|
386
|
+
|
387
|
+
ServiceResponse.new(
|
388
|
+
request_id: request_id,
|
389
|
+
service: 'payment_service',
|
390
|
+
status: status,
|
391
|
+
result: result || {},
|
392
|
+
error_message: error,
|
393
|
+
processing_time_ms: ((end_time - start_time) * 1000).round(2),
|
394
|
+
_sm_header: {
|
395
|
+
from: 'payment_service',
|
396
|
+
to: 'api_gateway'
|
397
|
+
}
|
398
|
+
).publish
|
399
|
+
|
400
|
+
if status == 'success'
|
401
|
+
emit_payment_event(operation, result)
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def process_payment(payload)
|
406
|
+
payment_id = "payment_#{SecureRandom.hex(4)}"
|
407
|
+
|
408
|
+
# Simulate payment processing
|
409
|
+
sleep(0.5) # Simulate external API call
|
410
|
+
|
411
|
+
# Random success/failure for demo
|
412
|
+
if rand < 0.9 # 90% success rate
|
413
|
+
payment_data = {
|
414
|
+
payment_id: payment_id,
|
415
|
+
order_id: payload['order_id'],
|
416
|
+
amount: payload['amount'],
|
417
|
+
currency: payload['currency'] || 'USD',
|
418
|
+
status: 'completed',
|
419
|
+
transaction_id: "txn_#{SecureRandom.hex(6)}",
|
420
|
+
processed_at: Time.now.iso8601
|
421
|
+
}
|
422
|
+
|
423
|
+
@payments[payment_id] = payment_data
|
424
|
+
[payment_data, 'success', nil]
|
425
|
+
else
|
426
|
+
[nil, 'error', 'Payment declined by bank']
|
427
|
+
end
|
428
|
+
rescue => e
|
429
|
+
[nil, 'error', e.message]
|
430
|
+
end
|
431
|
+
|
432
|
+
def refund_payment(payment_id)
|
433
|
+
if payment_id && @payments[payment_id]
|
434
|
+
@payments[payment_id][:status] = 'refunded'
|
435
|
+
@payments[payment_id][:refunded_at] = Time.now.iso8601
|
436
|
+
[@payments[payment_id], 'success', nil]
|
437
|
+
else
|
438
|
+
[nil, 'error', 'Payment not found']
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def emit_payment_event(operation, payment_data)
|
443
|
+
event_type = case operation
|
444
|
+
when 'process_payment' then 'payment_processed'
|
445
|
+
when 'refund_payment' then 'payment_refunded'
|
446
|
+
else "payment_#{operation}"
|
447
|
+
end
|
448
|
+
|
449
|
+
ServiceEvent.new(
|
450
|
+
event_id: SecureRandom.uuid,
|
451
|
+
event_type: event_type,
|
452
|
+
service: 'payment_service',
|
453
|
+
entity_id: payment_data[:payment_id],
|
454
|
+
data: payment_data,
|
455
|
+
_sm_header: {
|
456
|
+
from: 'payment_service',
|
457
|
+
to: 'event_bus'
|
458
|
+
}
|
459
|
+
).publish
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
class NotificationService
|
464
|
+
def initialize(transport)
|
465
|
+
@transport = transport
|
466
|
+
setup_subscriptions
|
467
|
+
end
|
468
|
+
|
469
|
+
def setup_subscriptions
|
470
|
+
# Subscribe to direct requests
|
471
|
+
@transport.where
|
472
|
+
.to('notification_service')
|
473
|
+
.subscribe do |message_class, message_data|
|
474
|
+
handle_request(JSON.parse(message_data))
|
475
|
+
end
|
476
|
+
|
477
|
+
# Subscribe to events for automatic notifications
|
478
|
+
@transport.where
|
479
|
+
.to('event_bus')
|
480
|
+
.subscribe do |message_class, message_data|
|
481
|
+
handle_event(JSON.parse(message_data)) if message_class == 'ServiceEvent'
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
private
|
486
|
+
|
487
|
+
def handle_request(data)
|
488
|
+
request_id = data['request_id']
|
489
|
+
operation = data['operation']
|
490
|
+
payload = data['payload'] || {}
|
491
|
+
|
492
|
+
puts "📧 Notification Service: Handling #{operation} [#{request_id}]"
|
493
|
+
|
494
|
+
result, status, error = case operation
|
495
|
+
when 'send_email'
|
496
|
+
send_email(payload)
|
497
|
+
when 'send_sms'
|
498
|
+
send_sms(payload)
|
499
|
+
else
|
500
|
+
[nil, 'error', 'Unknown operation']
|
501
|
+
end
|
502
|
+
|
503
|
+
ServiceResponse.new(
|
504
|
+
request_id: request_id,
|
505
|
+
service: 'notification_service',
|
506
|
+
status: status,
|
507
|
+
result: result || {},
|
508
|
+
error_message: error,
|
509
|
+
_sm_header: {
|
510
|
+
from: 'notification_service',
|
511
|
+
to: 'api_gateway'
|
512
|
+
}
|
513
|
+
).publish
|
514
|
+
end
|
515
|
+
|
516
|
+
def handle_event(event_data)
|
517
|
+
event_type = event_data['event_type']
|
518
|
+
|
519
|
+
puts "📧 Notification Service: Handling event #{event_type}"
|
520
|
+
|
521
|
+
case event_type
|
522
|
+
when 'user_created'
|
523
|
+
send_welcome_email(event_data['data'])
|
524
|
+
when 'order_created'
|
525
|
+
send_order_confirmation(event_data['data'])
|
526
|
+
when 'payment_processed'
|
527
|
+
send_payment_confirmation(event_data['data'])
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
def send_email(payload)
|
532
|
+
puts " ✉️ Sending email to #{payload['to']}: #{payload['subject']}"
|
533
|
+
{ message_id: SecureRandom.uuid, status: 'sent' }
|
534
|
+
rescue => e
|
535
|
+
[nil, 'error', e.message]
|
536
|
+
end
|
537
|
+
|
538
|
+
def send_sms(payload)
|
539
|
+
puts " 📱 Sending SMS to #{payload['phone']}: #{payload['message'][0..30]}..."
|
540
|
+
[{ message_id: SecureRandom.uuid, status: 'sent' }, 'success', nil]
|
541
|
+
rescue => e
|
542
|
+
[nil, 'error', e.message]
|
543
|
+
end
|
544
|
+
|
545
|
+
def send_welcome_email(user_data)
|
546
|
+
puts " 🎉 Sending welcome email to #{user_data['email']}"
|
547
|
+
end
|
548
|
+
|
549
|
+
def send_order_confirmation(order_data)
|
550
|
+
puts " 📦 Sending order confirmation for #{order_data['order_id']}"
|
551
|
+
end
|
552
|
+
|
553
|
+
def send_payment_confirmation(payment_data)
|
554
|
+
puts " 💳 Sending payment confirmation for #{payment_data['payment_id']}"
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
558
|
+
#==============================================================================
|
559
|
+
# Service Initialization
|
560
|
+
#==============================================================================
|
561
|
+
|
562
|
+
puts "\n🔧 Initializing microservices..."
|
563
|
+
|
564
|
+
transport = SmartMessage::Transport::RedisQueueTransport.new(
|
565
|
+
url: 'redis://localhost:6379',
|
566
|
+
db: 5,
|
567
|
+
queue_prefix: 'microservices',
|
568
|
+
consumer_group: 'service_workers',
|
569
|
+
block_time: 1000
|
570
|
+
)
|
571
|
+
|
572
|
+
# Initialize all services
|
573
|
+
api_gateway = ApiGatewayService.new(transport)
|
574
|
+
user_service = UserService.new(transport)
|
575
|
+
order_service = OrderService.new(transport)
|
576
|
+
payment_service = PaymentService.new(transport)
|
577
|
+
notification_service = NotificationService.new(transport)
|
578
|
+
|
579
|
+
# Wait for services to initialize
|
580
|
+
sleep 2
|
581
|
+
|
582
|
+
puts "✅ All microservices initialized and ready"
|
583
|
+
|
584
|
+
#==============================================================================
|
585
|
+
# End-to-End Workflow Demonstration
|
586
|
+
#==============================================================================
|
587
|
+
|
588
|
+
puts "\n🎭 Demonstrating end-to-end e-commerce workflow:"
|
589
|
+
|
590
|
+
# Workflow 1: User Registration and Welcome
|
591
|
+
puts "\n1️⃣ User Registration Workflow:"
|
592
|
+
user_req_id = api_gateway.handle_user_request('create_user', {
|
593
|
+
'name' => 'Alice Johnson',
|
594
|
+
'email' => 'alice@example.com'
|
595
|
+
})
|
596
|
+
|
597
|
+
sleep 2
|
598
|
+
|
599
|
+
# Workflow 2: Order Creation and Processing
|
600
|
+
puts "\n2️⃣ Order Creation Workflow:"
|
601
|
+
order_req_id = api_gateway.handle_user_request('create_order', {
|
602
|
+
'user_id' => 'user_1234',
|
603
|
+
'items' => [
|
604
|
+
{ 'sku' => 'BOOK-001', 'name' => 'Ruby Programming', 'price' => 29.99 },
|
605
|
+
{ 'sku' => 'BOOK-002', 'name' => 'Design Patterns', 'price' => 39.99 }
|
606
|
+
],
|
607
|
+
'total_amount' => 69.98
|
608
|
+
})
|
609
|
+
|
610
|
+
sleep 2
|
611
|
+
|
612
|
+
# Workflow 3: Payment Processing
|
613
|
+
puts "\n3️⃣ Payment Processing Workflow:"
|
614
|
+
payment_req_id = api_gateway.handle_user_request('process_payment', {
|
615
|
+
'order_id' => 'order_5678',
|
616
|
+
'amount' => 69.98,
|
617
|
+
'currency' => 'USD',
|
618
|
+
'payment_method' => 'credit_card'
|
619
|
+
})
|
620
|
+
|
621
|
+
sleep 3
|
622
|
+
|
623
|
+
# Workflow 4: Notification Sending
|
624
|
+
puts "\n4️⃣ Direct Notification Workflow:"
|
625
|
+
notification_req_id = api_gateway.handle_user_request('send_email', {
|
626
|
+
'to' => 'customer@example.com',
|
627
|
+
'subject' => 'Your order has been shipped!',
|
628
|
+
'template' => 'shipping_notification'
|
629
|
+
})
|
630
|
+
|
631
|
+
sleep 2
|
632
|
+
|
633
|
+
#==============================================================================
|
634
|
+
# Complex Multi-Service Scenarios
|
635
|
+
#==============================================================================
|
636
|
+
|
637
|
+
puts "\n🔄 Complex multi-service scenarios:"
|
638
|
+
|
639
|
+
# Scenario 1: Parallel service calls
|
640
|
+
puts "\n📡 Scenario 1: Parallel service operations"
|
641
|
+
requests = [
|
642
|
+
api_gateway.handle_user_request('get_user', { 'user_id' => 'user_1234' }),
|
643
|
+
api_gateway.handle_user_request('get_order', { 'order_id' => 'order_5678' }),
|
644
|
+
api_gateway.handle_user_request('send_sms', { 'phone' => '+1234567890', 'message' => 'Your order is ready!' })
|
645
|
+
]
|
646
|
+
|
647
|
+
puts " Parallel requests: #{requests.join(', ')}"
|
648
|
+
|
649
|
+
sleep 3
|
650
|
+
|
651
|
+
# Scenario 2: Service dependency chain
|
652
|
+
puts "\n🔗 Scenario 2: Service dependency chain"
|
653
|
+
puts " Creating user → Creating order → Processing payment → Sending confirmation"
|
654
|
+
|
655
|
+
# Step 1: Create user
|
656
|
+
user_req = api_gateway.handle_user_request('create_user', {
|
657
|
+
'name' => 'Bob Smith',
|
658
|
+
'email' => 'bob@example.com'
|
659
|
+
})
|
660
|
+
|
661
|
+
sleep 1
|
662
|
+
|
663
|
+
# Step 2: Create order for user
|
664
|
+
order_req = api_gateway.handle_user_request('create_order', {
|
665
|
+
'user_id' => 'user_' + SecureRandom.hex(4),
|
666
|
+
'items' => [{ 'sku' => 'GADGET-001', 'name' => 'Smart Watch', 'price' => 199.99 }],
|
667
|
+
'total_amount' => 199.99
|
668
|
+
})
|
669
|
+
|
670
|
+
sleep 1
|
671
|
+
|
672
|
+
# Step 3: Process payment
|
673
|
+
payment_req = api_gateway.handle_user_request('process_payment', {
|
674
|
+
'order_id' => 'order_' + SecureRandom.hex(4),
|
675
|
+
'amount' => 199.99,
|
676
|
+
'payment_method' => 'paypal'
|
677
|
+
})
|
678
|
+
|
679
|
+
sleep 2
|
680
|
+
|
681
|
+
# Scenario 3: Error handling and compensation
|
682
|
+
puts "\n⚠️ Scenario 3: Error handling demonstration"
|
683
|
+
error_req = api_gateway.handle_user_request('get_user', { 'user_id' => 'nonexistent_user' })
|
684
|
+
|
685
|
+
sleep 2
|
686
|
+
|
687
|
+
#==============================================================================
|
688
|
+
# Service Statistics and Monitoring
|
689
|
+
#==============================================================================
|
690
|
+
|
691
|
+
puts "\n📊 Microservices Architecture Statistics:"
|
692
|
+
|
693
|
+
# Show queue statistics
|
694
|
+
stats = transport.queue_stats
|
695
|
+
puts "\nService queue lengths:"
|
696
|
+
service_queues = stats.select { |name, _| name.include?('_service') }
|
697
|
+
service_queues.each do |queue_name, info|
|
698
|
+
service_name = queue_name.split('.').last.gsub('_', ' ').titleize
|
699
|
+
puts " #{service_name}: #{info[:length]} pending requests"
|
700
|
+
end
|
701
|
+
|
702
|
+
# Show routing patterns
|
703
|
+
routing_table = transport.routing_table
|
704
|
+
puts "\nActive service routing patterns:"
|
705
|
+
routing_table.each do |pattern, queues|
|
706
|
+
puts " Pattern: '#{pattern}' → #{queues.size} queue(s)"
|
707
|
+
end
|
708
|
+
|
709
|
+
puts "\nTotal active queues: #{stats.size}"
|
710
|
+
total_messages = stats.values.sum { |info| info[:length] }
|
711
|
+
puts "Total pending messages: #{total_messages}"
|
712
|
+
|
713
|
+
# Cleanup
|
714
|
+
transport.disconnect
|
715
|
+
|
716
|
+
puts "\n🏗️ Microservices architecture demonstration completed!"
|
717
|
+
|
718
|
+
puts "\n💡 Microservices Patterns Demonstrated:"
|
719
|
+
puts " ✓ Service-to-service communication"
|
720
|
+
puts " ✓ Request/response pattern"
|
721
|
+
puts " ✓ Event-driven architecture"
|
722
|
+
puts " ✓ Asynchronous message processing"
|
723
|
+
puts " ✓ Service isolation and independence"
|
724
|
+
puts " ✓ Centralized API Gateway pattern"
|
725
|
+
puts " ✓ Event bus for cross-service notifications"
|
726
|
+
puts " ✓ Error handling and fault tolerance"
|
727
|
+
|
728
|
+
puts "\n🚀 Key Architecture Benefits:"
|
729
|
+
puts " • Loose coupling between services"
|
730
|
+
puts " • High-performance async communication"
|
731
|
+
puts " • Scalable service-specific queues"
|
732
|
+
puts " • Event-driven reactive patterns"
|
733
|
+
puts " • Built-in message persistence"
|
734
|
+
puts " • Service discovery via routing patterns"
|
735
|
+
puts " • Comprehensive monitoring and observability"
|