Membangun Event-Driven Architecture dengan AWS EventBridge dan SQS: Decoupling Microservices
Service Tightly Coupled Menghambat Iterasi Tim
Ketika Service A memanggil Service B lewat HTTP langsung, kontrak endpoint menjadi beban yang harus dijaga dua tim sekaligus. Perubahan satu field pada respons Service B cukup untuk memutus Service A yang belum sempat diperbarui. Jadwal deploy dua service juga saling terikat, sehingga satu rilis kecil memaksa tim lain menunggu.
Skenario yang paling sering kami temui berada di alur pesanan. Order Service memanggil Inventory Service untuk memastikan stok tersedia sebelum pesanan dibuat. Selama Inventory Service melakukan deploy ulang, request Order Service menggantung sampai batas timeout. Latency puncak naik, antrean request menumpuk, dan pelanggan menerima pesan error. Pada pola synchronous request, kegagalan satu layanan langsung menjadi kegagalan seluruh rantai.
Decoupling memindahkan beban itu ke lapisan pesan. Producer cukup mengumumkan event seperti order.created ke satu titik, tanpa perlu tahu service mana yang memprosesnya. Consumer boleh ditambah, dipisah, atau di-deploy ulang tanpa menyentuh kode producer. Latency pemrosesan tidak lagi menentukan latency producer, karena producer selesai begitu event diterima. Ketahanan saat kegagalan juga berubah bentuk: pesan yang gagal menumpuk di antrian dan diproses ulang setelah layanan pulih, bukan langsung melempar error ke pelanggan.
Alur Event dari Producer Menuju Consumer di EventBridge dan SQS
Amazon EventBridge berperan sebagai event bus berbasis pattern matching. Producer mengirim event JSON lewat PutEvents, kemudian EventBridge membandingkan field source dan detail-type terhadap event pattern pada setiap rule. Event yang cocok diteruskan ke target terdaftar, sedangkan yang tidak cocok diabaikan tanpa memicu error di producer.
Amazon SQS berdiri di belakang bus sebagai antrian buffer. Consumer membaca pesan dengan ReceiveMessage sesuai kecepatannya sendiri, sehingga lonjakan event tidak langsung menekan compute consumer. Event bus yang abstrak ini ibarat pusat sortir surat yang meneruskan setiap amplop ke kotak masuk yang tepat.
Alur lengkapnya berjalan berurutan. Producer memanggil PutEvents dengan payload order.created. EventBridge mencocokkan event pattern lalu meneruskan pesan ke SQS queue. Antrian menyimpan pesan sampai consumer siap membacanya. Consumer mengambil pesan, memproses isi event, lalu menghapus pesan dari antrian.

Gambar: Diagram event yang dibandingkan terhadap event pattern setiap rule lalu diteruskan ke target — Sumber: AWS Documentation
Pertanyaan berikutnya adalah kenapa SQS perlu berada di belakang EventBridge, bukan target Lambda langsung. Target Lambda tidak memiliki visibility timeout, pengaturan retry yang fleksibel, maupun dead-letter queue bawaan. SQS menyediakan ketiganya. Visibility timeout menjaga satu pesan hanya diproses oleh satu proses dalam satu waktu. Retry dan dead-letter queue memberi jaring pengaman ketika pemrosesan gagal berulang. Dengan konfigurasi ini, kita tinggal mendefinisikan infrastruktur dalam satu template.
Mendefinisikan Event Bus, Rule, dan Antrian SQS dengan AWS SAM
Satu file template.yaml sudah cukup untuk seluruh infrastruktur. Struktur AWS SAM terdiri dari Globals untuk konfigurasi bersama, Resources untuk deklarasi resource, dan Outputs untuk nilai yang dikonsumsi stack lain. Di dalam Resources kita mendeklarasikan AWS::Events::EventBus custom, AWS::Events::Rule dengan event pattern pada source dan detail-type, target berupa AWS::SQS::Queue, attachment policy yang mengizinkan EventBridge mengirim pesan, serta AWS::Serverless::Function dengan events tipe SQS.
Python Fundamentals
Master the fundamentals of Python through hands-on, real-world projects. Designe...
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Globals:
Function:
Runtime: nodejs20.x
Timeout: 15
MemorySize: 256
Resources:
OrderEventBus:
Type: AWS::Events::EventBus
Properties:
Name: order-events
OrderCreatedRule:
Type: AWS::Events::Rule
Properties:
EventBusName: !Ref OrderEventBus
EventPattern: {source: [app.order-service], detail-type: [order.created]}
Targets: [{Id: OrderQueue, Arn: !GetAtt OrderQueue.Arn}]
OrderQueue:
Type: AWS::SQS::Queue
Properties: {QueueName: order-created-queue, VisibilityTimeout: 60}
OrderQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
Queues: [!Ref OrderQueue]
PolicyDocument:
Statement:
- {Effect: Allow, Principal: {Service: events.amazonaws.com}, Action: sqs:SendMessage, Resource: !GetAtt OrderQueue.Arn}
OrderConsumer:
Type: AWS::Serverless::Function
Properties:
Handler: src/handlers/order-consumer.handler
Events: {QueueEvent: {Type: SQS, Properties: {Queue: !GetAtt OrderQueue.Arn, BatchSize: 10}}}
Outputs:
EventBusArn: {Value: !Ref OrderEventBus}Logika template ini berpusat pada satu bus bernama order-events. Rule hanya meneruskan event dengan source dan detail-type yang cocok, sehingga event lain di bus yang sama tidak ikut masuk antrian. Queue policy menjadi langkah wajib, karena tanpa izin sqs:SendMessage dari events.amazonaws.com, pengiriman dari EventBridge ditolak dengan error akses. Parameter batchSize: 10 mengatur berapa record diproses dalam satu invocation consumer.
Workflow deploy memakai sam build untuk mengompilasi fungsi, lalu sam deploy --guided untuk membuat stack. Hasil yang kita harapkan berupa stack ARN dengan status CREATE_COMPLETE. Setelah itu, aws events list-event-buses dan aws events list-rules --event-bus-name order-events dipakai untuk memastikan bus dan rule sudah terdaftar.
Menulis Consumer Lambda yang Memproses Event dari SQS
Bentuk payload SQS berbeda dari event mentah EventBridge. Setiap entry pada Records[] memiliki body berupa string JSON, jadi langkah pertama handler adalah JSON.parse yang dibungkus penanganan error. Setelah payload terbaca, kita menjalankan validasi field wajib, memproses bisnis, lalu melaporkan record yang gagal melalui batchItemFailures.
import type { SQSEvent, SQSBatchResponse } from "aws-lambda";
const processOrder = (eventId: string, orderId: string) => {
// logika bisnis: reservasi stok dan pencatatan pesanan
return { eventId, orderId, status: "PROCESSED" };
};
export const handler = async (event: SQSEvent): Promise<SQSBatchResponse> => {
const batchItemFailures: { itemIdentifier: string }[] = [];
for (const record of event.Records) {
try {
const envelope = JSON.parse(record.body);
const detail = envelope.detail ?? envelope;
if (!detail.orderId) throw new Error("missing orderId");
const result = processOrder(envelope.id, detail.orderId);
console.log("event processed", JSON.stringify(result));
} catch (err) {
console.error("event failed", record.messageId, (err as Error).message);
batchItemFailures.push({ itemIdentifier: record.messageId });
}
}
return { batchItemFailures };
};Workflow handler mengikuti alur parse, validasi, proses, lalu laporkan. JSON.parse yang gagal ditangkap pada blok catch, sehingga satu payload rusak tidak menghentikan seluruh batch. Strategi partial batch response membuat SQS mengulang hanya record yang terdaftar pada batchItemFailures, sementara record sukses tetap dihapus dari antrian. Tanpa strategi ini, satu record buruk memaksa seluruh batch diproses ulang dan biaya invocation membengkak.
Output yang kita harapkan berupa log terstruktur di CloudWatch Logs, yaitu event processed {"eventId":"...","orderId":"...","status":"PROCESSED"} untuk payload valid dan event failed <messageId> <alasan> untuk yang gagal. Untuk mengujinya, kita mengirim event dummy dengan aws events put-events memakai source: app.order-service dan detail-type: order.created, kemudian mengamati log consumer dan jumlah record yang kembali ke antrian.
Menangani Retry dan Poison Message dengan Dead-Letter Queue
Pesanan yang selalu gagal diproses disebut poison message. Penyebabnya paling sering payload tidak valid, misalnya field orderId hilang atau format tanggal salah, sehingga consumer jatuh di titik yang sama setiap kali mencoba. Tanpa batas percobaan, pesan jenis ini memutar terus di antrian dan menutupi masalah yang sebenarnya.
OrderQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: order-created-queue
VisibilityTimeout: 60
RedrivePolicy:
deadLetterTargetArn: !GetAtt OrderDeadLetterQueue.Arn
maxReceiveCount: 5
OrderDeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: order-created-dlq
MessageRetentionPeriod: 1209600
OrderConsumer:
Type: AWS::Serverless::Function
Properties:
Timeout: 30Dua aturan konfigurasi wajib kita pahami di sini. VisibilityTimeout pada queue harus lebih besar dari Timeout function Lambda, dalam kasus ini 60 detik terhadap 30 detik, supaya pesan tidak kembali terbaca saat consumer masih bekerja. RedrivePolicy mengatur batas percobaan lewat maxReceiveCount dan menunjuk deadLetterTargetArn sebagai tujuan akhir, ditambah MaximumRetryAttempts pada RetryPolicy target EventBridge untuk batas pengiriman dari bus.

Gambar: Diagram timeline pemrosesan request selama visibility timeout di Amazon SQS — Sumber: AWS Documentation
Workflow penangannya berjalan otomatis. Pesan yang gagal dicoba berulang sampai batas maxReceiveCount, lalu dipindahkan ke DLQ untuk dianalisis ulang tanpa mengganggu antrian utama. Tim cukup mem-batch pesan dari DLQ, memperbaiki format payload, dan mengirimnya ulang. Praktik pendampingnya adalah memasang alarm ketika jumlah pesan di DLQ lebih dari nol, karena angka itu sinyal bahwa event hilang dari alur normal.
Memantau Alur Event dengan CloudWatch Metrics dan Alarm
Empat metrik SQS menjadi titik pantau pertama. ApproximateNumberOfMessagesVisible menunjukkan panjang antrian yang menumpuk. NumberOfMessagesSent mengukur laju event masuk. NumberOfMessagesFailed menandai pengiriman yang ditolak. AgeOfOldestMessage mengungkap umur pesan tertua, indikator paling cepat untuk mendeteksi consumer yang tersendat.
Dari metrik itu kita membangun CloudWatch Alarm. Alarm untuk DLQ aktif saat ApproximateNumberOfMessagesVisible pada antrian cadangan melebihi nol. Pada antrian utama, alarm aktif saat AgeOfOldestMessage melewati ambang, misalnya 300 detik. Notifikasi dikirim ke topik SNS yang berlangganan email atau Slack channel tim, sehingga penumpukan terlihat sebelum pelanggan melaporkan keterlambatan notifikasi.
Untuk melacak lokasi kegagalan per record, kita mengaktifkan AWS X-Ray pada consumer Lambda. Trace menampilkan segmen SQS poll, eksekusi handler, dan panggilan service turunan dalam satu segmen waktu, sehingga titik gagal terlihat tanpa membaca log satu per satu.
Sebelum masuk ke produksi, checklist terakhir perlu kita lengkapi. Event pattern dibuat spesifik pada source dan detail-type, karena pola yang terlalu luas memicu fan-out tak terduga ke banyak target. IAM role consumer diberi hak minimum: membaca antrian dan menulis log saja. Biaya EventBridge dihitung per event, jadi pola yang hemat mencegah tagihan membengkak saat volume naik. Dengan ketiga hal ini, arsitektur event-driven siap diandalkan untuk beban produksi.
Ingin mempraktikkan AWS EventBridge, SQS, dan Lambda secara langsung pada proyek microservices end-to-end? Ikuti bootcamp Cloud Engineering di Rumah Coding dan bangun portofolio arsitektur event-driven pertama Anda bersama mentor.
Course Terkait
Python Fundamentals
Master the fundamentals of Python through hands-on, real-world projects. Designed for absolute beginners, this course takes you from writing your first line of code to building a fully functional application. By the end of this course, you will have a solid grasp of core programming concepts, data structures, and file management, laying a strong foundation for future studies in Data Science, Web Development, or Automation.
Personal Finance Tracker & Analyzer (CLI)
- Interactive Main Menu: A continuous loop menu allowing users to choose between adding records, viewing summaries, or exiting the app.
- Transaction Logging: Users can input transaction types (Income/Expense), amounts, categories (e.g., Food, Salary, Transport), and descriptions.
- Robust Input Validation: Utilizes try-except blocks to prevent the program from crashing if a user accidentally types letters instead of numbers for financial amounts.
LLM Bootcamp
This project-based bootcamp is designed for beginners to dive practically into the world of Large Language Models (LLMs). Through hands-on building, you will learn how to interact with top-tier AI APIs, master prompt engineering, orchestrate complex workflows using LangChain, and implement Retrieval-Augmented Generation (RAG) to query your own documents. By the end of this course, you will have the skills to build, test, and deploy a fully functional, custom AI web application.
Domain-Specific AI Knowledge Assistant
- Dynamic Document Processing: A sidebar interface allowing users to upload new PDF or TXT files, which the app automatically chunks, embeds, and stores in the vector database.
- Context-Aware Chat UI: A modern chat interface built with Streamlit that maintains conversation history, allowing users to ask follow-up questions naturally.
- Strict Guardrails (Anti-Hallucination): System instructions designed so the AI politely declines to answer questions that fall outside the context of the uploaded documents.