Service-to-Service Communication Patterns in the Macro Repository: HTTP, SQS, and Lambda
The Macro codebase uses three primary service-to-service communication patterns: synchronous HTTP APIs built with Axum, asynchronous message passing via Amazon SQS, and AWS Lambda functions triggered by SQS or EventBridge events.
This article examines how macro-inc/macro implements inter-service communication across its Rust microservices architecture. Understanding these patterns provides insight into building scalable, decoupled systems that balance immediate response requirements with asynchronous processing needs.
HTTP Communication with Axum
Macro uses Axum for synchronous, request/response communication between services. Each service exposing HTTP endpoints defines its router in a dedicated module, typically named axum_router, and mounts it in main.rs.
The router construction follows a consistent pattern. In services/unfurl_service/src/api/unfurl/mod.rs, the service creates an axum::Router and registers route handlers:
use axum::{Router, routing::post};
pub fn unfurl_router() -> Router {
Router::new()
.route("/unfurl", post(unfurl_handler))
}
Similar patterns appear in services/static_file_service/src/api/mod.rs for file upload/download endpoints and services/notification_service/src/api/user_notification.rs for notification CRUD operations. Each router is then served via axum::serve in the respective service's entry point.
Asynchronous Messaging with SQS
For decoupled, event-driven communication, Macro relies on Amazon SQS. The pattern separates producers from consumers through typed queue wrappers.
Publishing Events
Services create queue instances using SqsQueue::new or specialized wrappers like SqsNotificationIngress. The generic implementation in crates/webhook/src/outbound/sqs_queue.rs provides the foundation for SQS producers across the codebase.
In services/notification_service/src/main.rs, the service initializes its queue and publishes events:
use notification::outbound::queue::SqsQueue;
let notification_queue = SqsQueue::new(sqs_client.clone(), "notification-queue");
notification_queue.send_message(payload).await?;
Error handling is explicit: services/email_service/src/pubsub/util.rs records FailureReason::SqsEnqueueFailed when message enqueueing fails, enabling proper observability and retry logic.
Consuming Messages
Consumers implement polling logic through SqsQueue or process messages in Lambda functions (covered in the next section). This separation allows scaling producers and consumers independently based on workload characteristics.
Serverless Event Processing with AWS Lambda
Several Macro services run as AWS Lambda functions, triggered by SQS or EventBridge events. This pattern enables cost-effective, scalable processing of asynchronous workloads without maintaining persistent infrastructure.
SQS-Triggered Lambda
The entry point in services/upload_extractor_lambda_handler/src/main.rs demonstrates the standard pattern:
use aws_lambda_events::event::sqs::SqsEvent;
use lambda_runtime::{service_fn, LambdaEvent};
async fn handler(event: LambdaEvent<SqsEvent>) -> Result<(), anyhow::Error> {
// decode the SQS record and process
}
let func = service_fn(handler);
The LambdaEvent<SqsEvent> wrapper provides structured access to queue messages with AWS Lambda's execution context.
Multi-Event Lambda Handlers
services/document_text_extractor/src/handler/mod.rs shows a more flexible approach using an IncomingEvent enum that matches on both SqsEvent and EventBridgeEvent:
match incoming_event {
IncomingEvent::SqsEvent(sqs_event) => process_sqs(sqs_event).await,
IncomingEvent::EventBridge(eventbridge_event) => process_eventbridge(eventbridge_event).await,
}
services/organization_retention_handler/src/main.rs follows the same SQS-triggered pattern for data lifecycle operations.
Pattern Selection in Practice
| Pattern | Use Case | Implementation Location |
|---|---|---|
| Axum HTTP | Synchronous API requests, real-time responses | services/*/src/api/*/mod.rs |
| SQS Queues | Reliable asynchronous messaging, work decoupling | crates/webhook/src/outbound/sqs_queue.rs |
| Lambda + SQS | Scalable, event-driven processing, cost optimization | services/*_lambda_handler/src/main.rs |
The architectural choice depends on latency requirements, scaling needs, and operational complexity. HTTP serves immediate client requests, SQS enables reliable pipeline construction, and Lambda provides serverless elasticity for bursty or intermittent workloads.
Summary
- HTTP with Axum provides synchronous service-to-service communication through REST endpoints defined in router modules and served via
axum::serve. - SQS queues enable asynchronous, decoupled messaging with explicit error handling through typed queue wrappers like
SqsQueue. - AWS Lambda functions process events serverlessly, triggered by SQS or EventBridge events using
LambdaEvent<T>handlers. - The codebase locates routing logic in
api/modules, queue implementations in shared crates, and Lambda entry points in dedicated*_lambda_handlerservices.
Frequently Asked Questions
How does Macro handle HTTP request routing between services?
Each service defines its Axum router in an api module (commonly axum_router), composes routes with Router::new().route(), then mounts and serves the router in main.rs. This pattern appears in services/unfurl_service/src/api/unfurl/mod.rs and services/static_file_service/src/api/mod.rs.
What SQS implementation does Macro use for message queuing?
Macro implements a generic SqsQueue struct in crates/webhook/src/outbound/sqs_queue.rs that wraps the AWS SDK. Services instantiate this with SqsQueue::new(client, queue_name) and call send_message(payload).await to publish events.
When does Macro use Lambda instead of long-running services?
Lambda handles event-driven, bursty, or intermittent workloads—particularly SQS message processing and EventBridge-triggered operations. Services like upload_extractor_lambda_handler and organization_retention_handler use this pattern for cost-efficient scaling without persistent infrastructure.
How are Lambda events structured in Macro's Rust code?
Handlers use lambda_runtime::LambdaEvent<T> where T is SqsEvent or EventBridgeEvent from aws_lambda_events. The document_text_extractor service demonstrates matching on an IncomingEvent enum to handle multiple trigger types in a single function.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →