Audit dan perkuat backend & integrasi eksternal AWCMS-Micro (resiliensi, outbox, webhook/provider, versioning API, observability). Gunakan saat menambah/mengeraskan integrasi ke provider luar (R2, WhatsApp, email, AI, pajak), memperbaiki keandalan delivery, atau menaikkan ketahanan backend. Menegakkan ADR-0006 (provider opsional, di luar transaksi) dan pola outbox doc 16.
Audit dan perkuat backend & integrasi eksternal AWCMS-Micro (resiliensi, outbox, webhook/provider, versioning API, observability). Gunakan saat menambah/mengeraskan integrasi ke provider luar (R2, WhatsApp, email, AI, pajak), memperbaiki keandalan delivery, atau menaikkan ketahanan backend. Menegakkan ADR-0006 (provider opsional, di luar transaksi) dan pola outbox doc 16.
AWCMS-Micro — Backend & Integration Hardening
Sumber kebenaran: docs/awcms-micro/16_backend_data_access_integration.md (transactional outbox, transaction wrapper, idempotency), docs/awcms-micro/05_openapi_asyncapi_detail.md (kontrak API/event), dan docs/awcms-micro/10_template_kode_coding_standard.md (guardrail). Skill ini peningkatan: menaikkan keandalan & ketahanan, bukan sekadar membuat endpoint (itu awcms-micro-new-endpoint).
Prinsip integrasi (non-negotiable)
Provider eksternal opsional (ADR-0006): R2/WA/email/AI/pajak tidak boleh jadi dependency alur operasional kritis, dan tidak boleh dipanggil di dalam transaksi DB. Provider off → aksi tetap masuk antrean, bukan gagal. Shared worker runner (src/lib/jobs/job-runner.ts, Issue #697) — dipakai untuk orkestrasi scripts/*.ts (lock, batching, exit code, telemetry) — dirancang KONSISTEN dengan aturan ini: tidak mendorong pola yang menaruh panggilan provider di dalam transaksi DB yang sama; handler job yang memanggil provider tetap wajib melakukannya di luar withTenant's transaction block, persis pola dispatcher outbox di bawah.
Secret hanya dari environment (doc 18); flag aktif tanpa kredensial → gagal start (fail-fast).
Checklist ketahanan
Transactional outbox — efek samping eksternal (kirim WA/email, upload objek, publish event) ditulis ke tabel outbox dalam transaksi bisnis, lalu dispatcher terpisah mengirim di luar transaksi (at-least-once). Bukan panggilan langsung inline. Contoh nyata sudah ada, ikuti pola ini: awcms_micro_object_sync_queue + dispatchObjectSyncQueue (src/modules/sync-storage/application/object-dispatch.ts, Issue #436) — tiga fase CLAIM (transaksi pendek, FOR UPDATE SKIP LOCKED, status transien sending sebagai lease) → UPLOAD (di luar transaksi apa pun) → FINALIZE (transaksi pendek kedua). awcms_micro_sync_outbox (event lokal antar-node) belum punya dispatcher live. Email (epic #492, Issue #493-#495) adalah contoh kedua nyata dari pola ini, dengan variasi: awcms_micro_email_messages (sql/020) + dispatchEmailQueue (src/modules/email/application/email-dispatch.ts) — CLAIM (reuse next_attempt_at sebagai lease, sama seperti object queue) → SEND (render dari template_key/variables lalu panggil EmailProvider.send, di luar transaksi apa pun) → FINALIZE (retry_wait/sent/failed + catat email_delivery_attempts). Caller nyata sudah ada sejak Issue #496/#497 (epic #492 selesai): password reset (POST /auth/password/forgot) dan bulk announcement (POST /email/announcements[/preview], two-tier ABAC) sama-sama meng-enqueue ke email_messages sebelum bun run email:dispatch mengklaimnya. social_publishing (Issue #643) adalah contoh ketiga, sama shape-nya. Generic MULTI-consumer counterpart (Issue #742, epic platform-evolution #738): domain_event_runtime — satu event bisa fan-out ke banyak consumer terdaftar (bukan satu dispatcher/satu provider seperti tiga contoh di atas), dengan ordering eksplisit per aggregate/order-key. Dua reference consumer bawaannya same-process/DB-only (bukan provider eksternal), jadi claim+eksekusi+finalize sengaja SATU transaksi, bukan CLAIM/CALL/FINALIZE 3-fase — lihat src/modules/domain-event-runtime/README.md §Execution model untuk kapan pola 3-fase tetap wajib (consumer out-of-transaction/broker-backed masa depan).
Timeout + retry + backoff — tiap panggilan keluar punya timeout eksplisit (withTimeout, src/lib/integration/timeout.ts, Issue #436 — reuse, jangan bikin ulang); retry dengan exponential backoff berbatas (pola evaluateObjectRetry, maks percobaan); jangan retry tak terbatas.
Circuit breaker — dependency yang gagal beruntun dibuka sementara agar tak menyeret sistem. src/lib/database/circuit-breaker.ts punya registry per-provider sejak Issue #436: getProviderCircuitBreaker(providerKey) untuk provider eksternal (reuse, jangan bikin circuit breaker terpisah) — beda dari singleton database yang sudah ada sejak awal. Saat breaker terbuka, baris yang butuh provider itu tidak diklaim sama sekali pada pass tersebut (tetap pending); baris yang tidak butuh provider tetap jalan.
Idempotency di batas integrasi — dispatcher aman diulang (dedup by natural key / Idempotency-Key / ledger batch seperti sync_push_batches); pengiriman ganda tak menduplikasi efek. Untuk upload objek: key upload (objectKey) sendiri jadi dedup key alami (PUT S3/R2 ke key sama = overwrite, bukan duplikat) — tidak selalu perlu ledger terpisah.
Dead-letter / status gagal — item yang habis retry ditandai failed dengan last_error, bukan hilang diam-diam; ada jalur retry manual admin (mis. /sync/object-queue/{id}/retry).
Verifikasi webhook masuk — signature HMAC + anti-replay (timestamp skew) untuk callback provider, pola awcms-micro-sync-hmac. Jangan percaya payload tak tertandatangani.
SSRF-safe — URL provider dari konfigurasi tepercaya (env), bukan dari input user; validasi host bila perlu. Contoh nyata: object-storage-uploader.ts (Issue #436) — endpoint R2 selalu dari process.env.R2_ACCOUNT_ID, tak pernah dari request.
Kontrak sinkron — endpoint baru/berubah → OpenAPI diperbarui; event baru/berubah → AsyncAPI diperbarui; module.ts publishes/subscribes akurat. bun run api:spec:check hijau. Dispatcher/CLI internal (bukan endpoint HTTP) tidak butuh update OpenAPI.
Versioning & kompatibilitas — perubahan breaking di path/skema butuh versi baru atau additive; jangan patahkan klien lama (pola cookie additive di /auth/login).
Observability — correlation ID otomatis di meta.correlationId untuk endpoint /api/* sejak Issue #447 (tidak perlu wiring manual, lihat awcms-micro-observability); log terstruktur JSON (hormati LOG_LEVEL); aksi high-risk masuk audit; redaksi secret/PII sebelum log; extension point setLogSink/setAuditExportHook tersedia bagi aplikasi turunan yang butuh forward ke SIEM eksternal (awcms-micro-observability).
Degradasi anggun — bila provider/flag off, endpoint operasional tetap jalan (POS tak berhenti); pesan/objek tetap terantre.
Rate limiting / anti-abuse — batasi endpoint publik/mahal. src/lib/security/rate-limit.ts (Issue #437) sudah menyediakan fixed-window counter generik (checkRateLimit, keyed sumber+konteks) — dipakai login, reuse untuk endpoint publik/mahal lain, jangan bikin limiter terpisah. In-memory per-proses (tidak dibagi antar instance) — cukup untuk topologi single-instance, deployment multi-instance perlu rate limiting di edge/proxy.
Verifikasi
Uji provider off: aksi tetap masuk outbox/queue, tak ada error operasional.
Uji retry: paksa gagal → item mundur backoff → habis percobaan → failed + last_error, retry manual mengembalikan ke pending.
Uji idempotency dispatcher: kirim ulang batch → efek tak berganda.
bun run check hijau; OpenAPI/AsyncAPI sinkron; correlation ID muncul di log lintas hop.
Skill terkait
awcms-micro-new-endpoint / awcms-micro-new-event (kontrak), awcms-micro-sync-hmac (signature), awcms-micro-idempotency (mutation), awcms-micro-audit-log (jejak), awcms-micro-observability (correlation ID, extension point), awcms-micro-performance (I/O di luar transaksi).