Integration Studies
REST, RabbitMQ, and Kafka — the three pillars of modern system integration.
What is REST?
REST (Representational State Transfer) is an architectural style for distributed hypermedia systems. It was defined by Roy Fielding in his 2000 doctoral dissertation.
Six Constraints of REST
- Client–Server: UI and data storage are separated, improving portability of the UI and scalability of the server.
- Stateless: Each request must contain all information needed to understand it. The server stores no client session state.
- Cacheable: Responses must define themselves as cacheable or non-cacheable. This improves scalability and performance.
- Uniform Interface: A consistent interface between components simplifies the architecture. Achieved via resource identification, manipulation through representations, self-descriptive messages, and HATEOAS.
- Layered System: A client cannot tell whether it connects directly to the end server or to an intermediary. Allows load balancers and gateways.
- Code on Demand (optional): Servers can extend client functionality by transferring executable code (e.g., JavaScript).
HTTP Methods
- GET – Retrieve a resource. Safe and idempotent.
- POST – Create a new resource or trigger an action. Not idempotent.
- PUT – Replace a resource entirely. Idempotent.
- PATCH – Partially update a resource. Idempotent.
- DELETE – Remove a resource. Idempotent.
Common HTTP Status Codes
- 200 OK – Success
- 201 Created – Resource was created (used with POST/PUT)
- 204 No Content – Success but no response body (used with DELETE)
- 400 Bad Request – Malformed request syntax
- 401 Unauthorized – Authentication required
- 403 Forbidden – Authenticated but not permitted
- 404 Not Found – Resource does not exist
- 409 Conflict – State conflict (e.g., duplicate)
- 422 Unprocessable Entity – Validation error
- 500 Internal Server Error – Unexpected server failure
Resource Naming
- Use nouns for resources, not verbs: `/orders` not `/getOrders`
- Use plural nouns: `/users`, `/products`, `/invoices`
- Use lowercase with hyphens for multi-word resources: `/order-items`
- Nest related resources: `/users/{id}/orders`
Versioning
Include the version in the URL path to allow breaking changes:
GET /api/v1/users GET /api/v2/users
Alternatively, use request headers (`Accept: application/vnd.api+json;version=2`), but URL versioning is simpler to debug and cache.
Pagination
For large collections always paginate. Use query parameters:
GET /products?page=2&per_page=50
Include pagination metadata in the response:
{
"data": [...],
"meta": { "page": 2, "per_page": 50, "total": 1240 }
}Error Handling
Return consistent error objects:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The 'email' field is required.",
"field": "email"
}
}Filtering, Sorting, and Searching
GET /products?category=electronics&sort=-price&q=laptop
- `sort=-price` means descending by price (minus prefix = descending)
- `q=laptop` is a full-text search term
Scenario
You are a BA at a retail bank. The mobile team needs a REST API for the customer-facing app.
Resource Design
/accounts – list customer's accounts
/accounts/{id} – get account details
/accounts/{id}/transactions – get transaction history
/transfers – initiate a transfer
/cards – list customer's cards
/cards/{id}/block – block a cardExample Requests
Get account details: ``` GET /api/v1/accounts/ACC-42 Authorization: Bearer <token>
Response 200: { "id": "ACC-42", "type": "current", "balance": 15420.00, "currency": "EUR", "iban": "DE89370400440532013000" } ```
Initiate a transfer: ``` POST /api/v1/transfers { "from_account": "ACC-42", "to_iban": "PL61109010140000071219812874", "amount": 500.00, "currency": "EUR", "description": "Rent payment" }
Response 201: { "transfer_id": "TXN-9981", "status": "pending", "estimated_completion": "2024-03-15T18:00:00Z" } ```
Key Design Decisions
- Transfers are a separate resource (not `POST /accounts/{id}/debit`) because they represent a business event with their own lifecycle.
- Card blocking uses a sub-resource action (`/cards/{id}/block`) — this is a pragmatic deviation from pure REST that is widely accepted for actions.
- The transfer endpoint returns 202 Accepted for async processing or 201 Created once the transfer record is created.
- Sensitive fields (full card number) are never returned — only masked values.
What is a Message Broker?
A message broker is middleware that translates messages between formal messaging protocols. It decouples producers (senders) from consumers (receivers), enabling asynchronous communication and load levelling.
AMQP — Advanced Message Queuing Protocol
RabbitMQ implements AMQP 0-9-1. Key entities:
- Producer — an application that publishes messages.
- Exchange — receives messages from producers and routes them to queues based on rules.
- Binding — a rule that tells an exchange which queue(s) to route messages to.
- Queue — a buffer that stores messages until consumers process them.
- Consumer — an application that receives messages from a queue.
Exchange Types
| Type | Routing Logic |
|---|---|
| Direct | Routes to queues whose binding key exactly matches the message routing key |
| Fanout | Broadcasts to all bound queues, ignoring routing key |
| Topic | Routes using wildcard patterns (`*` = one word, `#` = zero or more words) |
| Headers | Routes based on message header attributes |
Message Properties
- Delivery mode: 1 = transient (fast, lost on restart), 2 = persistent (written to disk)
- Content type: MIME type of the message body
- Expiration: TTL in milliseconds
- Priority: 0–255 (requires queue max-priority setting)
Acknowledgements
- Manual ack: consumer explicitly acks or nacks after processing. Prevents message loss.
- Auto ack: message is removed from queue as soon as delivered. Risk of data loss if consumer crashes mid-processing.
Scenario
An e-commerce platform uses RabbitMQ to decouple the order placement flow from downstream services (inventory, shipping, email notifications).
Architecture
[Web App] → (POST /orders) → [Order Service]
↓ publish to exchange
[orders.topic exchange]
/ | \
[inventory.q] [shipping.q] [email.q]
↓ ↓ ↓
[Inventory [Shipping [Email
Service] Service] Service]Exchange Setup
Using a topic exchange named `orders.topic`:
- `inventory.*` binding → inventory queue
- `shipping.paid` binding → shipping queue
- `#` binding → email queue (receives all events)
Message Flow
1. Customer places order → Order Service saves to DB. 2. Order Service publishes `order.created` with routing key `order.created` to the exchange. 3. Inventory Service receives the message, reserves stock, publishes `inventory.reserved`. 4. Shipping Service receives `shipping.paid` (after payment confirmed), creates a shipment. 5. Email Service receives every event and sends the appropriate notification.
Benefits
- Resilience: if the Email Service is down, messages queue up and are processed when it restarts.
- Scalability: add more Inventory Service instances as consumers to handle peak load.
- Decoupling: Order Service doesn't need to know about Inventory, Shipping, or Email services.
What is Apache Kafka?
Apache Kafka is a distributed event streaming platform designed for high-throughput, fault-tolerant, real-time data pipelines and event-driven architectures.
Core Concepts
- Event (Message): An immutable record of something that happened, containing a key, value, timestamp, and optional headers.
- Topic: A named log of events. Topics are append-only and ordered.
- Partition: A topic is split into partitions for parallelism. Each partition is an ordered, immutable sequence.
- Offset: A unique sequential ID for each message within a partition. Consumers track their position using offsets.
- Producer: An application that writes events to Kafka topics.
- Consumer: An application that reads events from topics.
- Consumer Group: A set of consumers that cooperate to consume a topic. Each partition is consumed by exactly one consumer in the group.
- Broker: A Kafka server. A Kafka cluster is composed of multiple brokers.
- Replication: Each partition is replicated across brokers for fault tolerance. One broker is the leader; others are followers.
Kafka vs RabbitMQ
| Kafka | RabbitMQ | |
|---|---|---|
| Model | Log-based, pull | Queue-based, push |
| Message retention | Configurable (days/weeks) | Until consumed |
| Ordering | Per partition | Per queue |
| Throughput | Very high (millions/sec) | High (thousands/sec) |
| Use case | Event streaming, audit logs | Task queues, RPC |
| Replay | Yes | No (once consumed) |
Key Configuration Parameters
- `retention.ms`: how long messages are kept
- `replication.factor`: number of replicas per partition
- `min.insync.replicas`: minimum replicas that must acknowledge writes
- `auto.offset.reset`: earliest or latest — what to do when no committed offset exists
Scenario
A SaaS company needs real-time analytics for its platform: page views, user actions, and errors must be processed and available in dashboards within seconds.
Pipeline Architecture
[Web/Mobile Apps]
↓ HTTP events
[Event Collector Service]
↓ produce
[Kafka Cluster]
| | |
[pageviews] [actions] [errors] ← Topics
| | |
[Stream Processor (Kafka Streams / Flink)]
↓
[ClickHouse / Elasticsearch]
↓
[Dashboard / Alerts]Topic Design
- `pageviews` — one message per page view: `{ user_id, page, timestamp, session_id }`
- `user_actions` — button clicks, form submissions: `{ user_id, action_type, element_id, payload }`
- `app_errors` — JavaScript and server errors: `{ severity, message, stack_trace, user_id }`
Consumer Groups
- analytics-writers: consume all topics → write to ClickHouse for historical queries
- realtime-dashboard: consume all topics → update Redis counters → WebSocket push to dashboards
- alerting: consume `app_errors` → trigger PagerDuty if error rate exceeds threshold
Why Kafka Here?
- Volume: 50,000 events/second at peak — Kafka handles this effortlessly.
- Multiple consumers: the same stream is consumed independently by 3 different consumer groups.
- Replay: if the analytics writer has a bug, we fix it and replay events from any point in time.
- Durability: events are persisted for 7 days, enabling backfills.