Struktur proyek
Pisahkan transport, business logic, dan akses data dengan dependency yang jelas.
Panduan teknis untuk membangun layanan backend yang konsisten, aman, observable, dan siap berjalan di production.
Satu baseline untuk menghasilkan layanan backend yang konsisten, mudah dites, mudah dideploy, aman, observable, dan mudah dirawat.
Standardisasi bukan menyeragamkan framework. Standardisasi memastikan perilaku operasional, kualitas kode, kontrak API, dan readiness setiap layanan dapat diprediksi.
Pisahkan transport, business logic, dan akses data dengan dependency yang jelas.
Gunakan formatter, linter, strict typing, dan naming yang otomatis diperiksa CI.
Gunakan envelope, error code, pagination, dan correlation ID yang konsisten.
Petakan error domain ke status HTTP; jangan bocorkan stack trace atau detail internal.
Validasi seluruh input di boundary sebelum masuk ke use case atau service.
Log terstruktur, punya request ID, level yang tepat, dan bebas data sensitif.
Konfigurasi lewat environment; fail fast saat variable wajib tidak tersedia.
Least privilege, secret management, dependency scan, rate limit, dan audit trail.
Prioritaskan unit dan integration test pada jalur bisnis kritis serta kontrak API.
Build reproducible, healthcheck, migration aman, observability, dan rollback plan.
Twelve-factor dan stateless application
Graceful shutdown serta readiness/liveness check
Structured logging, metrics, tracing, dan correlation ID
Centralized error format dan strict typing jika tersedia
Dependency injection dan config melalui environment
Secure secret handling dan automated test
CI menjalankan lint, test, build, dan security scan
Container-first deployment, API versioning, dokumentasi standar, dan Bruno collection
Standar penulisan kode backend agar konsisten, mudah dibaca, mudah diuji, mudah dikembangkan, dan tetap sederhana tanpa over-engineering.
Design pattern adalah referensi solusi untuk masalah desain yang berulang, bukan target implementasi. Gunakan pattern hanya ketika membuat kode lebih jelas, testable, dan mudah berubah. Untuk problem sederhana, pilih solusi paling sederhana yang tetap rapi dan scalable.
Kode harus mengungkapkan maksudnya melalui struktur, nama, dan alur yang jelas tanpa komentar berlebihan.
Gunakan nama yang spesifik dan konsisten; class berupa noun, function berupa verb, boolean memakai is/has/can.
Function melakukan satu pekerjaan pada satu level abstraksi. Pecah ketika alur sulit dibaca atau diuji.
Satu module atau class memiliki satu alasan utama untuk berubah dan tidak mencampur banyak concern.
Pisahkan request handling, validasi, business rule, data access, dan external integration.
Ekstrak komponen ketika perilaku benar-benar berulang dan stabil, bukan berdasarkan kemiripan sesaat.
Hapus duplikasi pengetahuan dan business rule; jangan memaksakan abstraksi untuk dua baris yang kebetulan sama.
Business logic dapat diuji tanpa database, network, clock, atau external service yang nyata.
| Area | Standar | Hindari |
|---|---|---|
| Naming | Nama menjelaskan domain dan maksud | Singkatan ambigu seperti data1, temp, mgr |
| Function/method | Satu tujuan, input jelas, return type konsisten | Function panjang dengan banyak side effect |
| Parameter | Gunakan object/DTO untuk parameter yang saling terkait | Banyak positional parameter dan boolean flag |
| Hardcoded value | Pindahkan konfigurasi ke config atau environment | URL, timeout, credential, dan environment di source |
| Magic number/string | Gunakan named constant, enum, atau value object | Angka/status string tanpa konteks |
| Duplicate rule | Satu sumber untuk business rule yang sama | Copy-paste validasi atau perhitungan |
| Interface | Definisikan contract pada boundary yang perlu diganti atau diuji | Interface satu-implementasi tanpa alasan |
| Dependency | Inject dependency dari luar melalui constructor/factory | Membuat database/client eksternal di business logic |
| Folder | Struktur konsisten berdasarkan module/domain dan layer | File sejenis tersebar tanpa aturan |
| Comments | Jelaskan alasan, constraint, dan trade-off | Komentar yang hanya mengulang kode |
Controller / Handler
↓
Request Validation / DTO
↓
Service / Usecase
↓
Repository / Data Access
↓
Database / External API| Layer | Tanggung jawab | Tidak boleh |
|---|---|---|
| Controller / Handler | Terima request, panggil use case, map response | Business logic atau query database |
| Validation / DTO | Validasi bentuk, tipe, format, dan boundary input | Menjalankan workflow bisnis |
| Service / Usecase | Orkestrasi business rule dan transaction boundary | Bergantung pada detail HTTP/framework |
| Repository / Data Access | Query dan mapping model persistence | Menentukan keputusan bisnis |
| External adapter | Terjemahkan contract aplikasi ke API/library eksternal | Membocorkan format vendor ke domain |
| Domain/model | Representasikan aturan dan invariant bisnis | Bergantung langsung pada transport atau database |
| Pattern | Gunakan ketika | Jangan dipaksakan ketika |
|---|---|---|
| Repository | Akses database perlu dipisahkan dari business logic atau diganti saat test | ORM sederhana sudah cukup dan wrapper hanya meneruskan method |
| Service | Business logic/use case perlu dipisahkan agar controller tetap tipis | Service hanya menjadi pass-through tanpa aturan atau orkestrasi |
| Factory | Pembuatan object memiliki variasi, dependency, atau aturan konstruksi khusus | Object dapat dibuat jelas dengan constructor biasa |
| Strategy | Ada beberapa algoritma atau rule yang dapat dipilih dan ditukar | Hanya ada satu if sederhana yang stabil |
| Adapter | External API/library mempunyai contract berbeda dari contract internal | Format dependency sudah sesuai dan tidak perlu isolasi |
| Facade | Consumer memerlukan satu pintu sederhana ke beberapa proses atau subsystem kompleks | Facade hanya menambah nama baru tanpa menyederhanakan apa pun |
| Observer / Pub-Sub | Notifikasi, audit log, atau async flow perlu merespons event secara terpisah | Proses harus sinkron, atomik, dan urut dalam satu transaksi |
| Builder | Object/request kompleks memiliki banyak opsi dan konstruksi bertahap | Object kecil dapat dibuat dengan DTO/constructor |
| Dependency Injection | Class perlu bergantung pada contract agar implementasi dapat diganti dan diuji | Dependency berupa value sederhana yang tidak memiliki perilaku |
| MVC / layered architecture | Request handling, business logic, dan data access perlu dipisahkan | Layer tambahan tidak memiliki tanggung jawab yang berbeda |
interface UserRepository {
findByEmail(email: string): Promise<User | null>;
save(user: User): Promise<User>;
}
class RegisterUser {
constructor(private readonly users: UserRepository) {}
async execute(input: RegisterUserInput): Promise<User> {
const existing = await this.users.findByEmail(input.email);
if (existing) throw new EmailAlreadyUsedError();
return this.users.save(User.create(input));
}
}
// Unit test dapat meng-inject in-memory repository tanpa database nyata.| Concern | Standar | Lokasi implementasi |
|---|---|---|
| Validation | Schema, format error, dan sanitasi konsisten | Request/DTO validator |
| Response | Envelope, pagination, message, dan metadata konsisten | Response mapper/resource |
| Error handling | Error domain dipetakan terpusat ke status dan error code | Global exception/error handler |
| Logging | Structured log, request ID, level tepat, tanpa secret | Logger abstraction/middleware |
| Configuration | Typed config divalidasi sekali saat startup | Config module |
| External service | Timeout, retry, mapping, dan error vendor diisolasi | Adapter/client module |
Mulai dari kebutuhan dan perubahan yang benar-benar terjadi, bukan kemungkinan yang belum jelas.
Jangan membuat interface, base class, factory, atau generic hanya untuk satu implementasi tanpa kebutuhan test/boundary.
Abstraksi harus mengurangi pengetahuan yang tersebar, bukan sekadar memindahkan kode ke file lain.
Controller/handler tetap tipis dan business logic tidak menumpuk pada transport layer.
Query database tidak tersebar di controller, handler, job, atau utility tanpa data access boundary.
Pilih composition dan function sederhana sebelum inheritance atau hierarchy yang dalam.
Hapus pattern ketika kompleksitasnya lebih besar daripada masalah yang diselesaikan.
Pastikan solusi masih mudah dipahami engineer berikutnya tanpa penjelasan lisan khusus.
https://www.scribd.com/document/467496968/design-patterns-pdfBuka referensi Kode memiliki naming dan struktur yang konsisten, controller tipis, business rule berada pada layer yang tepat, dependency dapat diganti saat test, error/response/validation/logging terpusat, serta tidak memakai pattern yang tidak memberikan manfaat nyata.
Standar pengujian unit backend untuk memverifikasi business rule secara cepat, deterministik, terisolasi, dan mudah dipahami saat terjadi kegagalan.
Unit test memverifikasi satu unit perilaku—umumnya function, class, service, atau use case—tanpa menjalankan database, network, queue, filesystem, dan framework container nyata. Tujuannya memberi feedback cepat terhadap business rule, bukan mengejar angka coverage semata.
| Aspek | Unit test | Integration test | End-to-end test |
|---|---|---|---|
| Scope | Satu function/class/use case | Interaksi beberapa komponen | Flow aplikasi dari entry point hingga dependency nyata |
| Dependency | Diganti stub, fake, atau mock | Sebagian nyata, misalnya database test | Environment semirip mungkin dengan production |
| Kecepatan | Sangat cepat dan dijalankan sering | Lebih lambat | Paling lambat dan lebih mahal |
| Tujuan | Business rule dan edge case | Kontrak antar-komponen | Keyakinan pada critical user journey |
| Contoh | Perhitungan biaya dan rule status | Repository dengan database | Request API lengkap hingga persistence |
Satu test selesai cepat dan seluruh suite unit dapat dijalankan setiap kali developer mengubah kode.
Tidak bergantung pada database, network, queue, file, urutan test lain, atau shared mutable state.
Input yang sama selalu menghasilkan outcome yang sama; waktu, random, UUID, dan environment dikontrol.
Nama dan struktur test menjelaskan rule bisnis, kondisi, tindakan, serta hasil yang diharapkan.
Setiap test dapat berjalan sendiri, paralel, atau dalam urutan apa pun tanpa memengaruhi hasil.
Assertion memeriksa observable behavior dan kontrak, bukan private method atau urutan internal yang rapuh.
| Tahap | Isi |
|---|---|
| Arrange | Siapkan input, subject under test, dependency pengganti, serta expected result. |
| Act | Jalankan satu perilaku utama yang sedang diuji. |
| Assert | Periksa output, perubahan state, error domain, atau side effect yang menjadi kontrak. |
Happy path untuk memastikan rule menghasilkan outcome yang benar.
Validation failure dan input kosong, salah format, atau di luar rentang.
Boundary value tepat sebelum, pada, dan setelah batas business rule.
Dependency mengembalikan not found, conflict, timeout, atau error lain yang dipetakan oleh unit.
Authorization atau state transition yang diizinkan dan ditolak.
Idempotency dan duplicate request bila unit menghasilkan side effect.
Pembulatan, timezone, tanggal pergantian hari, atau expiry jika relevan.
Regression test untuk setiap bug yang telah ditemukan dan diperbaiki.
| Jenis | Gunakan ketika | Perhatian |
|---|---|---|
| Stub | Dependency perlu mengembalikan nilai atau error tertentu | Jangan menambahkan verifikasi interaksi yang tidak dibutuhkan. |
| Fake | Perlu implementasi ringan seperti in-memory repository | Perilakunya harus tetap mengikuti contract implementasi nyata. |
| Mock | Interaksi dengan dependency adalah bagian penting dari kontrak | Hindari mock setiap method karena test menjadi terikat implementasi. |
| Spy | Perlu memeriksa side effect seperti event diterbitkan sekali | Gunakan hanya untuk interaksi yang observable dan penting. |
import { describe, expect, it, vi } from "vitest";
describe("RegisterUser", () => {
it("menolak email yang sudah terdaftar", async () => {
// Arrange
const users = {
findByEmail: vi.fn().mockResolvedValue({ id: "usr_existing" }),
save: vi.fn(),
};
const service = new RegisterUser(users);
// Act
const action = service.execute({ email: "user@bit.co.id" });
// Assert
await expect(action).rejects.toThrow(EmailAlreadyUsedError);
expect(users.save).not.toHaveBeenCalled();
});
});| Area | Standar |
|---|---|
| Nama test | Nyatakan kondisi dan hasil: should_reject_registration_when_email_already_exists atau bahasa yang konsisten di proyek. |
| Grouping | Kelompokkan berdasarkan unit dan behavior, bukan berdasarkan urutan eksekusi. |
| Lokasi | Gunakan konvensi stack secara konsisten: berdampingan dengan source atau pada folder tests/unit. |
| Fixture | Gunakan factory/builder kecil untuk data valid; override hanya field yang relevan dengan skenario. |
| Assertion | Buat assertion spesifik dan minimal sehingga alasan kegagalan langsung terlihat. |
| Stack | Tool umum | Standar unit test |
|---|---|---|
| Laravel | PHPUnit atau Pest | Test service/domain tanpa boot framework bila tidak diperlukan; gunakan Mockery serta Laravel fakes untuk boundary yang relevan. |
| Node.js TypeScript | Vitest, Jest, atau node:test | Gunakan strict typing, inject dependency, reset mock setelah test, dan kontrol timer/clock saat menguji waktu. |
| Go | testing package | Gunakan file *_test.go, table-driven test, subtest, interface pada consumer boundary, serta go test ./.... |
| Spring Boot | JUnit Jupiter, AssertJ, Mockito | Instansiasi class secara langsung untuk unit test; jangan memakai @SpringBootTest jika Spring context tidak dibutuhkan. |
| Python | pytest dan unittest.mock | Gunakan fixture kecil, monkeypatch hanya pada boundary, parametrization untuk variasi input, dan hindari I/O nyata. |
Coverage membantu menemukan kode yang belum diuji, tetapi tidak membuktikan kualitas assertion atau kelengkapan skenario.
Prioritaskan coverage tinggi pada business rule, authorization, perhitungan, state transition, dan error mapping yang kritis.
Jangan menulis test tanpa nilai hanya untuk mencapai 100% coverage atau menguji getter/setter sederhana.
Tetapkan threshold CI berdasarkan baseline dan risiko proyek; perubahan baru tidak boleh menurunkan coverage tanpa alasan yang direview.
Jalankan unit test pada setiap merge request dan hentikan merge bila test gagal atau flaky test belum ditangani.
Pisahkan laporan unit, integration, dan end-to-end agar kegagalan mudah diklasifikasikan.
Unit test mengakses database, Redis, filesystem, network, atau API eksternal nyata.
Satu test memverifikasi terlalu banyak behavior yang tidak berkaitan.
Mock private method atau seluruh internal call sehingga refactor aman justru merusak test.
Menggunakan sleep, waktu sistem, random, UUID, atau urutan eksekusi tanpa kontrol.
Menangkap exception tanpa memastikan jenis dan error code yang diharapkan.
Test saling berbagi state dan hanya lulus bila dijalankan dalam urutan tertentu.
Menghapus atau skip test gagal tanpa tiket, alasan, owner, dan batas waktu perbaikan.
Menyalin setup besar ke setiap test daripada memakai factory/builder atau helper yang tetap eksplisit.
https://go.dev/doc/tutorial/add-a-testBuka referensi Referensi eksternalSpring Boot TestingReferensi resmi tentang unit test dengan dependency injection dan pemisahannya dari test yang membutuhkan ApplicationContext.https://docs.spring.io/spring-boot/reference/testing/spring-applications.htmlBuka referensi Business rule baru dan berubah memiliki unit test untuk jalur sukses, gagal, serta boundary yang relevan; test cepat, deterministik, independen, bebas I/O nyata, mudah dipahami, lulus lokal dan CI, serta regression test ditambahkan untuk bug yang diperbaiki.
Standar kontribusi backend di GitLab BIT agar setiap branch, commit, merge request, dan proses review dapat ditelusuri kembali ke tiket JIRA.
Setiap perubahan backend wajib memiliki tiket JIRA. Gunakan ID tiket yang sama pada nama branch, commit message, dan merge request agar konteks pekerjaan serta riwayat perubahannya mudah ditelusuri.
| Artefak | Format | Contoh |
|---|---|---|
| Branch | <JIRA-ID>-<scope>-<deskripsi-singkat> | PRIMA-438-be-general-daftar-antrian |
| Commit | <JIRA-ID> [BE] <ringkasan perubahan> | PRIMA-438 [BE] General & Daftar Antrian |
| Merge request | <JIRA-ID> [BE] <ringkasan perubahan> | PRIMA-438 [BE] General & Daftar Antrian |
Buat satu branch untuk satu tiket JIRA atau satu scope perubahan yang jelas.
Gunakan ID tiket dengan huruf kapital, lalu tulis scope dan deskripsi branch dalam lowercase kebab-case.
Gunakan scope be untuk perubahan backend agar jenis pekerjaan mudah dikenali.
Commit harus fokus, dapat dipahami, dan tidak mencampur perubahan lain yang tidak berkaitan dengan tiket.
Awali commit message dengan ID JIRA dan penanda [BE], kemudian jelaskan hasil perubahan secara ringkas.
Jangan memasukkan secret, credential, file environment lokal, hasil build, atau dependency ke commit.
# Mulai dari branch utama yang terbaru
git checkout main
git pull origin main
# Buat branch berdasarkan tiket JIRA
git checkout -b PRIMA-438-be-general-daftar-antrian
# Periksa perubahan sebelum commit
git status
git diff
# Tambahkan file yang memang termasuk scope tiket
git add path/to/changed-file
git commit -m "PRIMA-438 [BE] General & Daftar Antrian"
# Push branch ke GitLab BIT
git push -u origin PRIMA-438-be-general-daftar-antrian| Bagian | Standar minimum |
|---|---|
| Judul | Gunakan ID JIRA, scope [BE], dan ringkasan yang sama jelasnya dengan commit. |
| Deskripsi | Jelaskan tujuan, perubahan utama, dampak, cara pengujian, serta catatan migration/config bila ada. |
| JIRA | Cantumkan link tiket dan pastikan scope merge request sesuai acceptance criteria. |
| Bukti pengujian | Sertakan hasil lint, test, build, API collection, screenshot, atau log yang relevan. |
| Risiko | Jelaskan breaking change, perubahan kontrak API, database migration, dependency, dan rollback plan. |
| Reviewer | Tetapkan senior atau lead sebagai reviewer; gunakan reviewer sesama tim yang memahami area tersebut bila lead berhalangan. |
Developer menyelesaikan self-check, memperbarui dokumentasi/API collection, lalu memastikan lint, test, dan build berhasil.
Developer push branch dan membuat merge request yang terhubung dengan tiket JIRA.
Senior atau lead meninjau ketepatan solusi, kualitas kode, keamanan, test, serta dampak operasional.
Jika lead tidak dapat melakukan review, review dapat dilakukan oleh senior atau anggota tim lain yang memahami scope perubahan.
Developer menindaklanjuti komentar review dan tidak menandai diskusi selesai sebelum perbaikannya tersedia atau alasannya disepakati.
Merge hanya dilakukan setelah review disetujui, pipeline wajib lulus, dan tidak ada diskusi penting yang belum selesai.
Setelah merge, pastikan status tiket JIRA diperbarui mengikuti workflow proyek dan branch dibersihkan sesuai kebijakan repository.
Ketiadaan lead tidak boleh membuat perubahan langsung di-merge tanpa review. Minta senior atau rekan satu tim yang kompeten pada area tersebut untuk meninjau perubahan dan memberikan approval.
https://git.bitgroup.devBuka referensi Branch, commit, dan merge request terhubung ke tiket JIRA; perubahan tetap dalam scope; pemeriksaan otomatis lulus; dokumentasi dan test diperbarui; serta review telah disetujui oleh lead, senior, atau reviewer tim yang kompeten.
Kontrak HTTP yang eksplisit membuat integrasi lebih aman, mudah di-debug, dan kompatibel lintas layanan.
Gunakan JSON, ISO 8601 UTC, validasi schema, dan header X-Request-ID.
Filter eksplisit, sort allowlist, serta page dan per_page dengan batas maksimum.
Versi mayor pada URL seperti /api/v1; perubahan breaking wajib versi baru.
Dokumentasi standar dan Bruno collection wajib diperbarui dalam MR yang sama. OpenAPI/Swagger terpisah opsional; jika dipakai, kontraknya harus sinkron.
Sertakan message yang ringkas, konsisten, mudah dipahami client, dan tidak membocorkan detail internal.
{
"success": true,
"message": "Data pengguna berhasil diambil",
"data": {
"id": "usr_01J8...",
"name": "Nadia Putri"
},
"meta": {
"request_id": "req_01J9...",
"timestamp": "2026-09-02T08:15:30Z"
}
}{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Permintaan tidak valid",
"details": [
{ "field": "email", "message": "Format email tidak valid" }
]
},
"meta": { "request_id": "req_01J9..." }
}| Status | Gunakan untuk | Catatan |
|---|---|---|
| 200 OK | Read/update berhasil | Response memiliki data |
| 201 Created | Resource berhasil dibuat | Sertakan Location bila relevan |
| 204 No Content | Delete/action tanpa body | Body harus kosong |
| 400 Bad Request | Request tidak dapat diproses | Bukan untuk error server |
| 401 / 403 | Belum login / tidak berwenang | Bedakan authentication dan authorization |
| 404 / 409 | Tidak ditemukan / konflik state | Gunakan error code domain |
| 422 Unprocessable | Validasi field gagal | Sertakan detail field |
| 500 / 503 | Error internal / dependency unavailable | Log detail, kirim pesan generik |
Pagination: page, per_page (default 20, maksimum 100), total, dan total_pages.
Filtering: gunakan nama field eksplisit; jangan menerima raw SQL dari client.
Sorting: format sort=-created_at,name dan batasi field yang diizinkan.
Setiap response membawa request_id agar log antar-service dapat dikorelasikan.
Standar dokumentasi dan pengujian manual API berbasis file dengan Bruno sebagai tujuan utama migrasi dari Postman.
Semua endpoint backend wajib memiliki API collection. Selama masa transisi Postman masih boleh digunakan, tetapi setiap endpoint baru atau yang berubah harus mulai tersedia di Bruno dan disimpan bersama repository sebagai API collection as code.
Engineer dapat menjalankan request, memeriksa response, dan mengulang skenario tanpa menyusun request dari awal.
Method, URL, auth, header, payload, contoh response, dan assertion terdokumentasi dekat dengan source code.
Developer baru cukup clone repository, membuka folder Bruno, memilih environment, lalu menjalankan collection.
Collection menjadi smoke test kontrak utama sebelum deploy dan dapat dijalankan melalui Bruno CLI di CI.
Perubahan endpoint dan collection direview dalam pull request yang sama agar backend dan consumer selalu sinkron.
Collection menjadi contoh executable yang dapat digunakan saat berkomunikasi dengan frontend atau service lain.
| Aspek | Postman pada masa transisi | Standar Bruno |
|---|---|---|
| Penyimpanan | Collection sering berpusat di workspace/export | Request tersimpan sebagai file teks di repository |
| Review | Perubahan export JSON sulit dibaca | Diff `.bru` kecil dan mudah direview melalui Git |
| Ownership | Dapat terpisah dari lifecycle source code | Collection mengikuti branch, tag, dan release aplikasi |
| Kolaborasi | Sinkronisasi bergantung workspace atau export | Clone, branch, commit, pull request, dan merge |
| Automation | Dapat dijalankan melalui tooling Postman | Collection Runner dan Bruno CLI `bru run` |
| Target | Tetap boleh untuk collection lama sementara | Wajib untuk endpoint baru dan tujuan akhir migrasi |
docs/03-api/bruno/
├── bruno.json
├── collection.bru
├── .env.example
├── environments/
│ ├── local.bru
│ ├── staging.bru
│ └── production.bru.example
├── auth/
│ ├── folder.bru
│ ├── login.bru
│ └── refresh-token.bru
├── users/
│ ├── folder.bru
│ ├── list-users.bru
│ ├── detail-user.bru
│ ├── create-user.bru
│ ├── update-user.bru
│ └── delete-user.bru
└── products/
├── folder.bru
├── list-products.bru
├── detail-product.bru
├── create-product.bru
├── update-product.bru
└── delete-product.bru| Area | Standar | Contoh |
|---|---|---|
| Root | Satu folder `docs/03-api/bruno/` per backend repository | docs/03-api/bruno/bruno.json |
| Grouping | Kelompokkan berdasarkan module atau business domain | auth/, users/, products/ |
| File request | Gunakan kebab-case dan pola aksi-resource | list-users.bru, create-user.bru |
| Nama request | Gunakan kata kerja yang konsisten dan mudah dicari | List Users, Create User |
| Urutan | Auth sebelum request yang memerlukan token; gunakan `seq` bila runner bergantung urutan | Login → List Users |
| Coverage | Minimal CRUD, auth, validation error, not found, dan critical business flow | Create User — Invalid Email |
# environments/local.bru
vars {
base_url: http://localhost:3000
access_token: {{process.env.ACCESS_TOKEN}}
refresh_token: {{process.env.REFRESH_TOKEN}}
}
# .env.example — hanya nama variable, tanpa nilai asli
ACCESS_TOKEN=
REFRESH_TOKEN=
LOGIN_EMAIL=
LOGIN_PASSWORD=Dilarang menyimpan access token, refresh token, password, API key, cookie, client secret, atau secret production di file `.bru`. Gunakan process environment, `.env` lokal yang diabaikan Git, secret manager, atau runtime variable. Commit hanya `.env.example` dan template environment tanpa nilai rahasia.
meta {
name: Login
type: http
seq: 1
}
post {
url: {{base_url}}/api/v1/auth/login
body: json
auth: none
}
headers {
content-type: application/json
accept: application/json
}
body:json {
{
"email": "{{process.env.LOGIN_EMAIL}}",
"password": "{{process.env.LOGIN_PASSWORD}}"
}
}
script:post-response {
const body = res.getBody();
bru.setVar("access_token", body.data.access_token);
bru.setVar("refresh_token", body.data.refresh_token);
}
tests {
test("login berhasil", function () {
expect(res.getStatus()).to.equal(200);
});
test("response memiliki access token", function () {
expect(res.getBody().data.access_token).to.be.a("string");
});
}Gunakan auth/login request untuk memperoleh token dan simpan sementara dengan `bru.setVar()`.
Gunakan `{{access_token}}` pada Bearer auth request berikutnya; runtime variable tidak disimpan ke file.
Pre-request script hanya digunakan untuk kebutuhan dinamis seperti signature, timestamp, nonce, atau header turunan.
Setiap endpoint penting minimal menguji status code dan struktur response atau business field utama.
Tambahkan assertion untuk validation error, unauthorized, forbidden, not found, dan conflict bila relevan.
Hindari script kompleks; business logic tetap diuji di automated test backend, bukan dipindahkan ke collection.
| Kebutuhan | Cara | Standar |
|---|---|---|
| Satu request | Buka Bruno, pilih environment, lalu Send | Pastikan environment aktif terlihat sebelum request production |
| Manual collection | Gunakan Collection Runner | Jalankan auth dan critical flow sebelum deploy |
| Seluruh collection | `cd bruno && bru run --env local` | Gunakan Bruno CLI yang dipin pada project/CI |
| Satu module | `bru run users --env staging` | Inject secret melalui environment CI |
| CI/CD | Jalankan `bru run` setelah deploy ke test environment | Gagal assertion harus menggagalkan validation job |
Simpan folder `docs/03-api/bruno/` di repository backend dan commit bersama perubahan endpoint terkait.
Developer memperbarui request, environment template, test, dan urutan collection pada branch yang sama.
Reviewer memeriksa method, URL, auth, payload, variable, assertion, serta memastikan tidak ada secret di diff.
Jalankan collection pada local atau staging dan lampirkan hasil bila perubahan menyentuh critical flow.
Merge collection bersama source code agar branch, tag, release, dan rollback tetap memiliki kontrak yang sesuai.
Inventarisasi collection Postman aktif dan tentukan owner setiap module.
Export collection Postman lalu import ke Bruno; jangan menyalin credential atau environment secret.
Susun ulang folder berdasarkan domain dan normalisasi naming request serta variable.
Pindahkan token/password ke process environment dan tambahkan `.env.example`.
Tambahkan assertion untuk response penting lalu validasi melalui Collection Runner dan CLI.
Review hasil migrasi melalui pull request; tandai collection Postman lama sebagai deprecated/read-only.
Standar penggunaan SQL dan NoSQL oleh backend agar model data konsisten, akses aman, query terukur, dan perubahan schema dapat dikendalikan.
Backend harus memilih database berdasarkan kebutuhan data dan pola akses, bukan tren. Setiap service wajib memiliki ownership data yang jelas, model dan naming yang konsisten, akses tervalidasi, query terukur, perubahan schema terkontrol, serta backup dan observability yang dapat diuji.
Setiap perubahan struktur database wajib dibuat sebagai migration atau versioned script, disimpan bersama source code, dan melalui code review. Larangan melakukan perubahan manual langsung di production mencakup pembuatan/perubahan/penghapusan table, collection, column, field, index, constraint, relation, view, function, serta data reference yang dibutuhkan aplikasi.
| Aspek | SQL | NoSQL |
|---|---|---|
| Model data | Tabel, row, column, dan relasi eksplisit | Document, key-value, wide-column, atau graph |
| Cocok untuk | Data relasional dan aturan integritas kuat | Pola akses spesifik, schema fleksibel, atau skala distribusi tertentu |
| Consistency | Umumnya transaksi ACID dan constraint kuat | Bergantung produk; dapat strong atau eventual consistency |
| Query | Join dan query ad hoc lebih kuat | Dioptimalkan untuk access pattern yang sudah diketahui |
| Schema | Migration versioned dan backward-compatible | Schema tetap harus didefinisikan serta divalidasi di aplikasi |
| Contoh penggunaan | User, transaksi, billing, inventory | Session, cache, event, catalog, activity feed |
Satu service menjadi pemilik data. Service lain mengakses melalui API atau event, bukan tabel secara langsung.
Gunakan satu gaya penamaan untuk tabel, collection, field, primary key, foreign key, index, dan constraint.
Model mengikuti kebutuhan bisnis dan access pattern; hindari menyimpan data yang sama tanpa aturan sinkronisasi.
Validasi tipe, format, panjang, enum, dan business invariant di boundary serta constraint database bila tersedia.
Akses database melalui repository/data access layer; controller tidak menjalankan query secara langsung.
Klasifikasikan data, batasi field yang disimpan, enkripsi bila perlu, dan jangan menulis secret atau PII ke log.
Gunakan created_at dan updated_at secara konsisten; tambahkan deleted_at atau actor ID bila kebutuhan audit menuntutnya.
Tetapkan sumber data utama dan aturan sinkronisasi untuk cache, search index, replica, atau read model.
| Area | Standar | Yang harus dihindari |
|---|---|---|
| Connection | Gunakan pool/client singleton, atur limit, lifetime, idle, dan graceful shutdown | Membuat koneksi atau pool baru pada setiap request |
| Query | Parameterized query, field eksplisit, pagination, timeout, dan query plan | Raw query dari input, SELECT *, atau query tanpa batas |
| Index | Buat berdasarkan filter, sort, join, dan access pattern nyata | Index berlebihan atau tanpa bukti penggunaan |
| Transaction | Boundary singkat, commit/rollback eksplisit, dan failure path diuji | Network call lambat di dalam transaksi |
| N+1 | Gunakan eager loading, batch, aggregate, atau data loader | Query berulang di dalam loop |
| Pagination | Gunakan cursor untuk data besar; offset masih boleh untuk data terbatas | Mengambil seluruh dataset ke memory |
| Read/write | Gunakan replica setelah memahami consistency dan replication lag | Membaca data yang harus langsung konsisten dari replica |
| Caching | Tentukan TTL, invalidation, key naming, dan fallback | Menganggap cache sebagai source of truth |
Setiap backend wajib memastikan penggunaan koneksi database tetap terkendali saat traffic normal, lonjakan beban, autoscaling, rolling deployment, dan gangguan dependency. Connection Pool Hell adalah kondisi ketika koneksi habis atau antrean memperoleh koneksi terus membesar sehingga request timeout dan retry memperparah beban. Pool/proxy terpasang saja belum memenuhi standar: budget koneksi, lifecycle, backpressure, monitoring, dan hasil pengujian wajib tersedia.
| Lapisan | Tanggung jawab | Batas yang wajib dipahami |
|---|---|---|
| Pool aplikasi | Gunakan ulang koneksi dan batasi pekerjaan yang memakai database dalam satu proses | Pool setiap proses berbeda; jumlahkan web, worker, scheduler, replica, dan koneksi langsung |
| PostgreSQL → PgBouncer | Gunakan sebagai pilihan standar pooling eksternal ketika koneksi banyak atau sering berganti | Transaction pooling melepas koneksi setelah transaksi selesai; fitur session harus diuji kompatibilitasnya |
| MySQL → ProxySQL | Gunakan sebagai pilihan standar proxy pooling/multiplexing | Transaksi aktif serta beberapa penggunaan session state dapat menonaktifkan multiplexing dan menahan koneksi backend |
| NoSQL | Gunakan pool/client driver yang sesuai produk dan topology | PgBouncer dan ProxySQL bukan solusi umum NoSQL; hitung pool per server/node sesuai perilaku driver |
Hitung per server database tujuan.
Budget aplikasi = limit yang disetujui
- koneksi operasional/service lain
- headroom pemulihan
Contoh: 200 - 40 - 40 = 120 koneksi maksimum untuk aplikasi.
Tanpa proxy:
web: 6 replica × 2 proses × pool_max 8 = 96
worker: 2 replica × 1 proses × pool_max 8 = 16
total: 112 <= 120
Jumlah replica harus mencakup puncak autoscaling + rolling surge.
Tambahkan scheduler, migration, monitoring, dan pool lain jika belum dihitung.
Dengan proxy:
2 instance proxy × batas backend agregat 50 = 100
jalur langsung yang diizinkan = 10
total koneksi ke database = 110 <= 120
Koneksi client ke proxy berbeda dari koneksi proxy ke database.
Batas backend agregat mencakup seluruh pool/user/database/hostgroup
yang menuju server yang sama. Ulangi perhitungan saat failover.
Budget adalah plafon, bukan target untuk selalu dipenuhi.
Tentukan pool efektif melalui pengukuran throughput dan latency.Pada runtime long-lived, buat pool/client sekali per proses dan konfigurasi, gunakan ulang, lalu tutup setelah request/job selesai saat graceful shutdown. Jangan membuat pool per request.
Sesuaikan lifecycle dengan runtime: PHP request-based dan persistent connection tidak identik dengan shared pool. Hitung jumlah proses PHP/FrankenPHP dan queue worker; verifikasi pembersihan state pada worker long-lived.
Connection yang dipinjam wajib dikembalikan pada jalur sukses, error, timeout, dan cancellation. Gunakan finally/defer/with atau mekanisme driver; tutup cursor/result dan selesaikan commit/rollback.
Pisahkan connect timeout, pool-acquire timeout, query timeout, transaction deadline, dan request deadline. Budget total harus muat dalam deadline request; cancellation harus membatalkan pekerjaan dan membersihkan koneksi.
Batasi antrean menunggu koneksi dan concurrency request/job. Gunakan rate limit atau respons 503 saat kapasitas sementara habis; jangan menunggu tanpa batas.
Jangan menahan transaksi sambil memanggil API eksternal, menunggu queue, atau mengolah file. Cegah N+1, full scan tak perlu, dan transaksi idle.
Retry hanya error transient, dengan batas percobaan, backoff dan jitter. Hindari retry bertingkat di semua layer; pastikan write idempotent dan periksa hasil jika commit tidak pasti.
| Pilihan | Yang harus dikonfigurasi/diperiksa | Jika tidak kompatibel |
|---|---|---|
| PgBouncer | Bedakan max_client_conn dari default_pool_size per pasangan user/database; tetapkan max_db_connections/max_user_connections sesuai topology dan hitung seluruh instance proxy | Uji LISTEN, session advisory lock, temporary table dan state session. Pisahkan workload tersebut ke session pool atau jalur langsung dengan budget khusus |
| Prepared statement PostgreSQL | Protocol-level prepared statement pada transaction pooling memerlukan dukungan versi dan max_prepared_statements non-zero; SQL PREPARE/DEALLOCATE memiliki batasan berbeda | Uji ORM/driver dan migration tool; gunakan mode yang kompatibel untuk workload tersebut |
| ProxySQL | Bedakan koneksi frontend dari batas backend mysql_servers.max_connections. Hitung server yang sama di berbagai hostgroup/instance, dan pantau koneksi used/free serta multiplexing | Periksa transaksi panjang, temporary table, lock, dan session/user variables yang menahan koneksi. Jangan memaksa multiplexing bila merusak session semantics |
| Operasional proxy | Batasi akses, gunakan credential/TLS sesuai kebutuhan, healthcheck, monitoring, kapasitas file descriptor, dan strategi failover | Uji reconnect dengan backoff dan anggaran saat failover; proxy harus punya kapasitas dan prosedur pemulihan yang terukur |
| Gejala | Periksa | Tindakan |
|---|---|---|
| Used connection tidak turun setelah pekerjaan selesai | Connection leak, cursor terbuka, transaksi belum selesai | Perbaiki release/close dan rollback pada seluruh jalur; uji error dan cancellation |
| Acquire wait tinggi, query lambat, lock meningkat | Query plan, index, N+1, blocking transaction, idle in transaction | Optimalkan query dan pendekkan transaksi; batasi batch, jangan langsung membesarkan pool |
| Koneksi melonjak saat deploy/autoscaling | Jumlah proses × replica × pool, rolling surge, pool minimum, reconnect storm | Turunkan pool/concurrency atau cap replica; lakukan rollout bertahap sesuai budget |
| Client proxy banyak dan backend tetap penuh | Session pinning/multiplexing, transaksi aktif, batas backend seluruh proxy | Perbaiki pola session dan pisahkan workload khusus; proxy tidak menambah kapasitas eksekusi database |
| Timeout memperoleh koneksi tetapi DB masih longgar | Batas pool lokal, jaringan, auth/TLS, routing, antrean proxy | Perbaiki bottleneck yang terbukti; sesuaikan pool bertahap hanya setelah budget dan load test mendukung |
| Database CPU/I/O sudah jenuh | Throughput, query termahal, cache miss, write contention | Kurangi beban, optimalkan query/index; evaluasi scaling database setelah pengukuran |
Stabilkan beban: batasi request masuk, concurrency worker, dan batch job; tunda pekerjaan non-kritis. Catat dampak serta kondisi pemulihan.
Perbaiki connection leak, query lambat, dan transaksi panjang terlebih dahulu. Menambah max_connections atau replica aplikasi tanpa perhitungan dapat memperburuk saturasi.
Jika proxy tidak tersedia atau tidak kompatibel, gunakan pool driver berbatas kecil, cap total proses/replica, antrean terbatas, dan acquire timeout. Dokumentasikan alasan dan hasil uji; jangan membuat fallback bypass proxy tanpa budget.
Untuk proses yang boleh ditunda, gunakan queue dengan consumer concurrency terbatas, status tracking, timeout, dan idempotency. Queue menyerap lonjakan, tetapi backlog tetap perlu limit dan monitoring.
Kurangi read berulang dengan cache yang memiliki TTL/invalidation dan perlindungan cache stampede. Gunakan read replica hanya untuk operasi yang menerima replication lag; write tetap menuju primary.
Jika kapasitas eksekusi database memang habis, evaluasi CPU/memory/I/O, optimasi schema/index, atau pemisahan workload. Sharding adalah langkah lanjutan jika kebutuhan dan biaya operasionalnya terbukti.
Ulangi load test dan dokumentasikan budget baru sebelum perubahan pool, proxy, worker, replica, atau database dibawa ke production.
Catat pool min/max per proses, jumlah replica maksimum termasuk rollout, seluruh instance proxy, koneksi langsung, dan alokasi per server database.
Pantau active/idle connection, acquire wait p95/p99, pending request, timeout/error, durasi query/transaksi, lock, dan CPU/I/O database. Tetapkan alert dengan PIC serta langkah mitigasi.
Uji traffic normal, burst, soak test, worker paralel, rolling deployment, dan failover. Catat target throughput/latency/error sesuai SLO proyek.
Simulasikan dependency lambat, query gagal, dan pembatalan request; koneksi harus kembali ke baseline dan antrean harus terkuras setelah beban turun.
Dokumentasikan hasil, konfigurasi, dashboard, serta runbook pada dokumentasi database/operasional dan MR. Tanpa bukti batas koneksi dan pemulihan, service belum memenuhi standar readiness.
https://www.pgbouncer.org/features.htmlBuka referensi Referensi eksternalPgBouncer — Connection LimitsBatas client, pool per user/database, dan koneksi backend; gunakan untuk menghitung budget proxy.https://www.pgbouncer.org/config.htmlBuka referensi Referensi eksternalProxySQL — MultiplexingKondisi yang menahan koneksi backend serta cara memeriksa multiplexing.https://proxysql.com/documentation/multiplexing/Buka referensi Referensi eksternalHikariCP — About Pool SizingMengapa pool yang lebih besar belum tentu meningkatkan throughput dan perlunya pengujian kapasitas.https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-SizingBuka referensi | Area | SQL | NoSQL |
|---|---|---|
| Identitas | Primary key wajib stabil; foreign key untuk relasi penting | Document/key wajib stabil dan mendukung partition strategy |
| Integritas | Gunakan NOT NULL, UNIQUE, CHECK, dan foreign key | Validasi schema di aplikasi dan database jika produknya mendukung |
| Relasi | Normalisasi secukupnya; denormalisasi hanya berdasarkan kebutuhan | Embedding untuk data yang dibaca bersama; reference untuk lifecycle terpisah |
| Atomicity | Gunakan transaction untuk perubahan yang harus konsisten | Pahami batas atomic operation dan transaction pada produk yang dipakai |
| Index | Tinjau execution plan dan biaya write | Index mengikuti query shape dan partition key |
| Consistency | Pilih isolation level sesuai risiko bisnis | Dokumentasikan strong/eventual consistency dan dampaknya ke user |
Semua perubahan schema, index, constraint, view, database function, atau reference data wajib melalui migration/versioned script dan code review.
Migration wajib masuk ke repository dan merge request yang sama dengan kode yang membutuhkannya agar perubahan dapat ditelusuri serta dijalankan konsisten di setiap environment.
Dilarang mengubah struktur database secara manual langsung di production; emergency change wajib segera dibuatkan migration rekonsiliasi dan dokumentasi insiden.
Migration production harus kecil, backward-compatible, dapat diobservasi, dan memiliki rollback atau roll-forward plan.
Gunakan pola expand-and-contract untuk rename, perubahan tipe, atau penghapusan column/field.
Seeder harus idempotent, terpisah antara development dan production, serta tidak membawa credential atau PII.
Tetapkan retention, archival, soft/hard delete, dan mekanisme pemenuhan penghapusan data.
Backup belum dianggap siap sebelum restore diuji secara berkala dengan target RPO dan RTO yang jelas.
Gunakan credential berbeda per aplikasi dan environment dengan prinsip least privilege.
Simpan credential di secret manager atau runtime environment; aktifkan TLS untuk koneksi database.
Pantau latency, error rate, slow query, connection usage, lock/deadlock, storage, dan replication lag.
Log nama operasi dan durasi tanpa merekam raw credential, token, atau data sensitif.
Tetapkan query timeout dan alert berdasarkan service-level objective, bukan nilai default tanpa evaluasi.
Audit akses administratif dan perubahan data kritis sesuai kebutuhan bisnis serta regulasi.
Gunakan SQL sebagai pilihan awal ketika data relasional dan transaksi merupakan kebutuhan utama. Gunakan NoSQL ketika access pattern, model data, latency, atau kebutuhan distribusi memang lebih tepat—dan dokumentasikan trade-off consistency, operasional, serta recovery sebelum diadopsi.
Pilih model eksekusi berdasarkan kebutuhan hasil langsung, durasi proses, risiko kegagalan dependency, kapasitas worker, dan pengalaman pengguna.
Gunakan synchronous ketika proses sederhana dan hasilnya dibutuhkan langsung. Gunakan asynchronous ketika pekerjaan lama, berat, dapat ditunda, atau perlu diisolasi dari kegagalan dependency. Jangan memilih async hanya agar arsitektur terlihat modern.
Proses berjalan langsung dalam request-response dan pengguna menunggu hasil akhirnya.
Pekerjaan dikirim ke background job atau queue sehingga pengguna tidak perlu menunggu selesai.
Backend menangani banyak pekerjaan dalam rentang waktu yang saling overlap, tidak harus pada saat yang persis sama.
Beberapa pekerjaan benar-benar berjalan bersamaan menggunakan beberapa CPU core, process, atau thread.
Satu thread utama tetap dapat concurrent; Node.js melayani banyak operasi I/O melalui event loop.
Thread pool dapat menjalankan pekerjaan paralel, terutama untuk CPU-bound atau blocking workload.
Background worker atau queue worker memproses unit kerja di luar request utama dengan payload, status, timeout, dan retry policy.
Producer menerbitkan event dan consumer memprosesnya secara terpisah melalui kontrak berversi.
| Aspek | Synchronous | Asynchronous |
|---|---|---|
| Eksekusi | Selesai di request yang sama | Dijadwalkan dan diproses terpisah |
| Pengalaman user | Menunggu hasil final | Menerima acknowledgment dan status pekerjaan |
| Cocok untuk | Cepat, sederhana, hasil dibutuhkan langsung | Lama, berat, dapat ditunda, atau dependency tidak stabil |
| Respons API | 200/201 dengan hasil final | 202 Accepted + job_id atau status URL |
| Kegagalan | Error langsung dikembalikan | Retry terbatas, dead-letter queue, dan recovery flow |
| Observability | Latency, throughput, error rate | Queue depth, lag, attempts, job age, dead jobs |
| Aspek | Concurrency | Parallelism |
|---|---|---|
| Makna | Banyak pekerjaan maju secara overlap | Banyak pekerjaan berjalan pada saat yang sama |
| Kebutuhan CPU | Dapat terjadi pada satu CPU core | Umumnya membutuhkan beberapa core, process, atau thread |
| Kekuatan utama | Efisiensi menunggu I/O | Mempercepat pekerjaan CPU-bound |
| Contoh | Event loop menangani banyak koneksi | Beberapa worker memproses gambar bersamaan |
| Risiko | Starvation dan blocking event loop | Race condition, contention, dan overhead koordinasi |
| Aspek | Single-thread | Multi-thread |
|---|---|---|
| Model | Satu thread utama menjalankan callback/task | Beberapa thread tersedia melalui thread pool/runtime |
| Concurrency | Tetap bisa concurrent melalui event loop dan non-blocking I/O | Task dapat dijadwalkan ke beberapa thread |
| Parallelism | Tidak untuk JavaScript CPU work pada thread utama | Dapat menjalankan CPU work secara paralel pada beberapa core |
| Cocok untuk | I/O-bound dengan operasi non-blocking | CPU-bound atau library/runtime blocking |
| Perhatian | Blocking call menghentikan seluruh event loop | Shared state memerlukan sinkronisasi yang aman |
| Aspek | Request-response | Background job |
|---|---|---|
| Lifecycle | Terikat koneksi dan timeout request | Dikelola queue worker secara independen |
| Hasil | Harus tersedia sebelum response | Dapat dilihat melalui polling, callback, atau notifikasi |
| Kapasitas | Dibatasi web instance dan connection | Dibatasi concurrency serta kapasitas worker |
| Use case | Login, lookup, validasi, CRUD ringan | Email/WA, report, export, upload/file processing |
| Scaling | Tambah web replica | Tambah worker berdasarkan queue depth dan downstream limit |
Gunakan untuk kirim email/WhatsApp, generate report, export data, dan upload atau pemrosesan file.
Gunakan ketika proses lama, berat, dapat ditunda, atau bergantung pada layanan eksternal yang rentan gagal.
Gunakan ketika throughput perlu di-buffer dan kapasitas worker harus dikontrol terpisah dari web server.
Jangan gunakan jika operasi cepat, sederhana, hasilnya dibutuhkan langsung, atau harus atomik dalam transaksi request.
Jangan memecah proses ke queue tanpa status tracking dan cara pengguna mengetahui hasil akhirnya.
Simpan queued, processing, completed, atau failed beserta progress dan timestamp.
Pakai exponential backoff + jitter, batas percobaan, dan retry hanya pada error transient.
Request, network call, job, dan shutdown masing-masing wajib memiliki deadline yang jelas.
Gunakan idempotency key atau unique constraint agar retry tidak menggandakan efek.
Lindungi shared state dengan atomic update, optimistic lock, atau lock bila benar-benar perlu.
Batasi concurrency berdasarkan CPU, memory, koneksi database, rate limit, dan kapasitas downstream.
Teruskan correlation ID dari producer ke message, worker log, dan downstream request.
Event-driven flow wajib punya schema version, consumer idempotent, dan prosedur replay.
| Stack | Model eksekusi | Standar implementasi |
|---|---|---|
| Laravel | Queue, Job, dan Worker | Gunakan Horizon atau Supervisor; atur retry, timeout, failed jobs, dan idempotency. |
| Node.js TypeScript | Event loop, async/await, Promise | Jangan block event loop; gunakan BullMQ dengan Redis untuk queue worker bila diperlukan. |
| Go | Goroutine, channel, dan worker pool | Batasi goroutine, propagasikan context timeout, tangani panic, dan hindari leak. |
| Spring Boot | Thread pool, @Async, scheduler | Konfigurasi executor secara eksplisit; gunakan message broker jika lifecycle perlu dipisah. |
| Python | async/await atau worker process | Gunakan async untuk I/O-bound, Celery/RQ untuk job, dan hindari blocking call di async handler. |
Setiap background job memiliki status tracking, timeout, retry policy, idempotency, structured logging, correlation ID, batas concurrency worker, dead-letter handling, dan prosedur replay yang terdokumentasi.
Producer menyimpan perubahan bisnis dan event secara konsisten, misalnya dengan transactional outbox.
Broker menerima event dengan event ID, correlation ID, schema version, dan occurred_at.
Queue worker mengambil message sesuai batas concurrency dan menetapkan processing timeout.
Consumer memeriksa idempotency sebelum menghasilkan side effect.
Message berhasil di-ack, dijadwalkan retry, atau dipindahkan ke dead-letter queue untuk investigasi.
Panduan alur backend lintas service: request-response, event, kepemilikan data, konsistensi, dan pemulihan ketika sebagian proses gagal.
Service memiliki tanggung jawab bisnis, kontrak, data, dan lifecycle deployment yang jelas. Gunakan microservices ketika kebutuhan deployment, scaling, dan isolasi domain membenarkan biaya operasionalnya. Untuk domain yang belum stabil atau aplikasi sederhana, modular monolith dapat menjadi titik awal.
| Komponen | Tanggung jawab | Batas |
|---|---|---|
| API Gateway | Routing, autentikasi awal, rate limit, dan request ID | Business rule tetap berada di service domain |
| Identity Service | Identitas, token, dan pengelolaan akses | Service tujuan tetap memeriksa izin terhadap resource |
| Antrian Service | Pendaftaran, nomor, kapasitas, dan status antrian | Pemilik data antrian dan aturan konsistensinya |
| Notification Service | Pengiriman email/WhatsApp dan status pengiriman | Kegagalan pengiriman tidak membatalkan antrian yang sudah tersimpan |
| Message broker | Menyalurkan event ke consumer | Bukan sumber utama status bisnis |
Satu transaksi pendaftaran, dua jalur pemrosesan.
Kirim permintaan pendaftaran beserta token dan idempotency key.
Validasi input, izin, dan kapasitas antrian.
Simpan antrian dan event dalam satu transaksi lokal.
Setelah commit, service mengembalikan 201 + antrian_id melalui gateway ke client.
Setelah transaksi commit, relay menerbitkan event AntrianCreated.
Consumer memeriksa duplikasi event dan memproses pengiriman.
Kirim pesan, lalu Notification Service menyimpan status pengiriman.
Jika pengiriman gagal, lakukan retry terkontrol. Antrian yang sudah tersimpan tetap valid.
Client dapat mengecek status melalui GET ke Antrian Service lewat gateway. Respons pendaftaran tidak menunggu notifikasi selesai.
Client mengirim request melalui gateway dengan token dan idempotency key untuk operasi yang memerlukannya.
Gateway meneruskan request dan trace context ke service tujuan melalui alamat service yang dikonfigurasi atau service discovery.
Service memvalidasi input, identitas, izin resource, dan business rule; panggilan downstream memiliki deadline yang mengikuti budget request.
Service menyimpan hasil dalam transaksi lokal dan mengembalikan response API standar dengan message serta request_id.
Gunakan 201 saat resource sudah dibuat. Jika pekerjaan baru diterima untuk diproses kemudian, gunakan 202 disertai operation_id dan endpoint status.
Antrian Service menyimpan perubahan bisnis dan event AntrianCreated pada outbox dalam transaksi database yang sama.
Relay membaca outbox, menerbitkan event ke broker, lalu mencatat keberhasilan publish. Publish dapat terulang jika relay gagal sebelum mencatat hasil.
Event membawa event_id, event_type, schema_version, occurred_at, aggregate_id, trace context, dan payload minimum sesuai kontrak.
Consumer memeriksa event_id dan menjalankan pemrosesan secara idempotent. Setiap service menyimpan status pemrosesannya sendiri.
ACK dikirim setelah pemrosesan berhasil. Error sementara mendapat retry terbatas dengan backoff dan jitter; kegagalan permanen masuk dead-letter flow untuk investigasi dan replay.
Untuk side effect eksternal, gunakan idempotency key provider bila tersedia dan rekonsiliasi status ketika hasil pengiriman tidak pasti.
| Area | Aturan backend |
|---|---|
| HTTP / gRPC | Untuk hasil langsung; hindari rantai panggilan panjang dan tetapkan timeout per dependency |
| Event / queue | Untuk pekerjaan tertunda; dokumentasikan status, duplicate handling, ordering per entity bila diperlukan, dan replay |
| Data ownership | Service lain mengakses melalui API/event, tidak membaca atau menulis tabel milik service lain secara langsung |
| SQL / NoSQL | Pilih berdasarkan access pattern; isolation schema dan credential tetap wajib meskipun berbagi instance database |
| Migration wajib | Perubahan schema/index dan transformasi data memakai migration/versioned script pada repository pemilik data; uji kompatibilitas versi lama dan baru |
| Read model | Salinan data lintas domain diperbarui lewat event; dokumentasikan eventual consistency dan cara rekonsiliasi |
| Contract | Versioning API/event, dokumentasi standar dan Bruno collection diperbarui bersama kode; OpenAPI disinkronkan jika dipakai; uji kompatibilitas producer-consumer |
Transaksi database satu service tidak otomatis mencakup service lain. Untuk proses bertahap seperti reservasi kapasitas lalu pembayaran, gunakan Saga bila diperlukan: setiap tahap melakukan transaksi lokal, dan kegagalan memicu tindakan kompensasi seperti melepas reservasi. Kompensasi dapat gagal dan perlu retry serta rekonsiliasi; ini bukan rollback database global. Dokumentasikan orchestrator atau event choreography yang dipilih dan status akhir proses.
| Kondisi | Penanganan |
|---|---|
| Response timeout setelah write | Hasil belum tentu gagal; periksa status atau ulangi dengan idempotency key yang sama |
| Broker tidak tersedia | Event tetap pending di outbox; pantau usia backlog dan pulihkan relay |
| Dependency terus gagal | Batasi retry, gunakan circuit breaker dan isolasi worker/pool; fallback harus menjaga makna hasil bisnis |
| Event duplikat / terlambat | Deduplication dan version check per entity; jangan menimpa state baru dengan event lama |
| Notification gagal | Simpan status failed/pending dan lakukan retry; status antrian tetap mengikuti hasil transaksi Antrian Service |
| Kompensasi gagal | Tandai perlu rekonsiliasi, alert ke PIC, dan simpan jejak untuk pemulihan |
Hubungkan perubahan service dengan tiket JIRA dan MR; review dampak kontrak ke consumer sebelum merge.
Jalankan unit test business rule, integration test database/broker, contract test, dan end-to-end test untuk alur kritis termasuk partial failure.
Rilis artifact berversi per service. Urutkan migration dan rollout berdasarkan kompatibilitas; catat service, versi, dan perubahan data di Production Release Record.
Amankan komunikasi internal dengan TLS dan identitas service yang tervalidasi; akses internal tetap mengikuti least privilege.
Hubungkan logs dan traces lintas HTTP serta message; pantau latency, error, outbox lag, queue age, dan status proses bisnis.
Verifikasi healthcheck dan alur bisnis setelah deploy; dokumentasikan rollback aplikasi, roll-forward data, serta replay event yang aman.
https://learn.microsoft.com/en-us/azure/architecture/guide/architecture-styles/microservicesBuka referensi Referensi eksternalMicroservices.io — Transactional OutboxPola menyimpan perubahan data dan event secara atomik, beserta risiko publish duplikat.https://microservices.io/patterns/data/transactional-outbox.htmlBuka referensi Referensi eksternalMicroservices.io — SagaTransaksi lokal berurutan dan kompensasi untuk proses bisnis lintas service.https://microservices.io/patterns/data/saga.htmlBuka referensi Standar telemetry backend agar tim dapat mendeteksi gangguan, memahami dampaknya, menelusuri penyebab, dan memverifikasi pemulihan dengan cepat.
Monitoring memberi tahu ketika kondisi yang telah dikenali bermasalah. Observability membantu menjawab mengapa masalah terjadi dengan menghubungkan logs, metrics, dan traces pada service, environment, version, request, serta operasi bisnis yang sama.
Rekaman event terstruktur untuk menjelaskan apa yang terjadi, kapan, pada service mana, dan dalam konteks request atau job apa.
Pengukuran numerik teragregasi untuk melihat kesehatan, tren, kapasitas, serta pencapaian SLI/SLO.
Perjalanan end-to-end sebuah request atau job melalui service, database, queue, dan external dependency.
request_id, trace_id, dan span_id menghubungkan telemetry agar investigasi tidak dilakukan dengan menebak waktu kejadian.
Ringkasan operasional untuk latency, traffic, error, saturation, dependency, queue, database, dan indikator bisnis kritis.
Notifikasi actionable berbasis dampak pengguna atau risiko SLO, lengkap dengan owner, severity, dan runbook.
| Field | Aturan |
|---|---|
| timestamp | Gunakan waktu UTC dalam format ISO 8601. |
| level | Gunakan debug, info, warn, dan error secara konsisten; production tidak menjalankan debug tanpa kebutuhan terbatas. |
| service & environment | Catat nama service, environment, version, serta instance/pod bila tersedia. |
| message & event | Gunakan pesan singkat yang stabil dan event name yang dapat dicari, bukan paragraf bebas. |
| request_id & trace_id | Terima atau buat ID di entry point, lalu teruskan ke downstream request, event, dan background job. |
| operation | Catat route/template, method, status, duration_ms, serta error_code; jangan jadikan raw URL ber-ID sebagai label metric. |
| business context | Boleh mencatat entity ID atau operation ID yang aman dan diperlukan untuk investigasi. |
| error | Catat jenis error dan stack trace pada boundary terpusat tanpa menduplikasi error yang sama di setiap layer. |
{
"timestamp": "2026-09-06T09:21:42.117Z",
"level": "error",
"service": "prima-api",
"environment": "production",
"version": "v1.8.0",
"event": "queue.job_failed",
"message": "Gagal memproses sinkronisasi antrian",
"request_id": "req_01K4...",
"trace_id": "4bf92f3577b34da6a3ce929d0e0e4736",
"job_id": "job_01K4...",
"attempt": 3,
"duration_ms": 1820,
"error_code": "DOWNSTREAM_TIMEOUT"
}| Area | Metric yang dipantau | Tujuan |
|---|---|---|
| Traffic | Request rate dan throughput per route/service | Melihat beban serta perubahan pola penggunaan. |
| Errors | Error rate, status 5xx/4xx penting, exception, dan failed job | Mendeteksi kegagalan teknis dan bisnis. |
| Latency | p50, p95, dan p99 response/dependency/job duration | Menangkap pengalaman lambat yang tertutup oleh nilai rata-rata. |
| Saturation | CPU, memory, thread/goroutine, connection pool, disk, dan worker utilization | Mendeteksi kapasitas yang mendekati batas. |
| Database | Query latency, slow query, connection usage, lock/deadlock, replication lag | Mengetahui bottleneck dan risiko data layer. |
| Queue | Queue depth, oldest job age, processing rate, retry, failed/dead-letter job | Mengukur backlog dan kesehatan worker. |
| Dependency | Latency, error, timeout, circuit-breaker state, dan rate-limit response | Membedakan kegagalan internal dengan downstream. |
| Business | Keberhasilan proses kritis seperti login, pendaftaran, pembayaran, atau penerbitan dokumen | Mengukur apakah service benar-benar memberi hasil kepada pengguna. |
Buat root span pada HTTP entry point, consumer event, scheduler, dan queue worker.
Buat child span untuk database query, cache, external HTTP call, publish message, dan operasi penting lain.
Teruskan trace context menggunakan standar W3C Trace Context melalui header dan message metadata.
Sertakan trace_id dan span_id pada structured log agar perpindahan dari alert ke trace dan log mudah dilakukan.
Jangan menaruh request body, response body, token, atau PII sebagai span attribute tanpa klasifikasi dan sanitasi.
Gunakan sampling yang terdokumentasi; pertahankan trace error dan operasi kritis sesuai kemampuan platform.
| Komponen | Standar |
|---|---|
| Liveness | Menjawab apakah proses hidup; jangan gagal hanya karena dependency sementara tidak tersedia. |
| Readiness | Menjawab apakah instance siap menerima traffic dan memeriksa dependency yang benar-benar wajib. |
| Dashboard | Tampilkan golden signals, deployment marker/version, dependency, database, queue, serta business flow kritis. |
| Alert | Alert berdasarkan gejala dan dampak yang perlu tindakan; hindari alert untuk setiap exception tunggal. |
| Severity | Definisikan severity, jalur eskalasi, owner, jam dukungan, dan target response. |
| Runbook | Setiap alert penting memiliki link dashboard, query awal, kemungkinan penyebab, mitigasi, dan prosedur eskalasi. |
| Istilah | Penggunaan |
|---|---|
| SLI | Ukuran perilaku service dari sudut pandang pengguna, misalnya successful request ratio atau latency p95. |
| SLO | Target reliabilitas untuk SLI pada periode tertentu, disepakati sesuai kebutuhan bisnis. |
| Error budget | Batas toleransi kegagalan yang membantu menyeimbangkan reliability dengan kecepatan perubahan. |
| Alert threshold | Gunakan threshold yang terkait dampak/SLO dan durasi yang cukup agar tidak menghasilkan noise. |
Dilarang mencatat password, access token, refresh token, API key, session, cookie, atau authorization header.
Masking atau hapus PII dan data sensitif sebelum telemetry meninggalkan aplikasi.
Batasi metric label dan span attribute ber-cardinality tinggi seperti user ID, raw URL, atau payload dinamis.
Tetapkan retention, akses, enkripsi, dan audit untuk platform observability sesuai klasifikasi data.
Telemetry tidak boleh menyebabkan request gagal; gunakan buffering, timeout, dan batas resource pada exporter/agent.
Pantau pipeline telemetry itu sendiri: dropped data, exporter failure, collector queue, dan ingestion delay.
| Stack | Standar implementasi |
|---|---|
| Laravel | Gunakan structured logging dengan context, request ID middleware, Horizon metrics untuk queue, dan OpenTelemetry SDK/exporter bila tracing diterapkan. |
| Node.js TypeScript | Gunakan logger JSON seperti Pino/Winston, AsyncLocalStorage untuk request context, serta OpenTelemetry Node SDK dan auto-instrumentation yang relevan. |
| Go | Gunakan log/slog atau logger terstruktur, context untuk propagasi, OpenTelemetry Go SDK, serta Prometheus-compatible metrics bila menjadi standar platform. |
| Spring Boot | Gunakan Actuator/Micrometer untuk health dan metrics, MDC untuk correlation, structured logging, serta OpenTelemetry Java agent/SDK. |
| Python | Gunakan logging terstruktur, contextvars untuk correlation, OpenTelemetry Python, dan instrumentasi ASGI/WSGI, database, serta worker. |
https://opentelemetry.io/docs/concepts/observability-primer/Buka referensi Referensi eksternalGoogle SRE — Monitoring Distributed SystemsReferensi untuk golden signals, dashboard yang berguna, serta alert yang actionable dan rendah noise.https://sre.google/sre-book/monitoring-distributed-systems/Buka referensi Service mengeluarkan logs, metrics, dan traces yang saling terhubung; dashboard memperlihatkan kesehatan teknis serta flow bisnis; alert memiliki owner dan runbook; deployment dapat dibandingkan melalui version marker; dan seluruh telemetry bebas secret serta data sensitif yang tidak diperlukan.
Image production harus kecil, deterministic, aman, tidak membawa secret, dan menjalankan proses sebagai non-root.
Multi-stage build, base image dipin, cache dependency optimal, dan runtime minimal.
Abaikan .git, env, test artifacts, local dependency, logs, dan secret.
Gunakan untuk dependency local seperti database, Redis, atau broker bila diperlukan.
Dokumentasikan key tanpa nilai rahasia dan jelaskan apakah buildtime/runtime.
Pisahkan liveness dan readiness; readiness memeriksa kesiapan menerima traffic.
Buat user khusus atau gunakan user bawaan image sebelum proses dijalankan.
FROM node:24-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci
FROM node:24-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM node:24-alpine AS runtime
ENV NODE_ENV=production
WORKDIR /app
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=deps --chown=node:node /app/node_modules ./node_modules
USER node
HEALTHCHECK --interval=30s CMD wget -qO- http://localhost:3000/health || exit 1
EXPOSE 3000
CMD ["node", "dist/server.js"]Image dibangun di CI, hasil scan tidak memiliki vulnerability kritis, berjalan sebagai non-root, menerima config saat runtime, dan lulus smoke test dari image yang sama dengan production.
Pisahkan nilai untuk menghasilkan artifact dari konfigurasi yang baru diketahui ketika aplikasi dijalankan.
| Aspek | Buildtime | Runtime |
|---|---|---|
| Tujuan | Menghasilkan artifact/image | Mengonfigurasi instance berjalan |
| Contoh | Install deps, compile TS, binary Go, JAR | DATABASE_URL, REDIS_URL, LOG_LEVEL |
| Waktu tersedia | CI atau docker build | Container start / platform config |
| Secret | Hindari; gunakan secret mount sementara | Inject via secret manager/platform |
| Perubahan | Memerlukan rebuild | Restart/redeploy tanpa rebuild artifact |
| Prinsip | Artifact immutable dan reproducible | Satu artifact untuk semua environment |
# Runtime — tidak berisi nilai asli
APP_ENV=
LOG_LEVEL=
DATABASE_URL=
REDIS_URL=
API_KEY=
QUEUE_CONNECTION=
# Build metadata — aman untuk diekspos
APP_VERSION=
COMMIT_SHA=Jangan gunakan ARG atau ENV Docker untuk menyimpan secret production permanen.
Validasi seluruh runtime variable saat startup dan hentikan proses bila key wajib kosong.
Rotasi secret tanpa mengubah source code dan batasi akses berdasarkan environment.
Jangan log isi environment atau credential ketika validasi gagal.
Gunakan kemampuan framework untuk boundary HTTP, tetapi pertahankan business rule di service yang dapat diuji.
| Area | Standar Laravel | Tooling |
|---|---|---|
| HTTP | Controller tipis, Form Request, API Resource | OpenAPI / Scramble |
| Business | Service/Action berisi use case | Dependency injection |
| Data | Repository bila abstraksi memberi nilai | Eloquent + eager loading |
| Quality | PSR-12, static analysis level tinggi | Pint, PHPStan/Larastan |
| Test | Feature test untuk API, unit untuk domain | Pest atau PHPUnit |
| Async | Job idempotent, retry/backoff, failed jobs | Queue + Horizon |
| Schema | Migration kecil dan backward-compatible | Artisan migrate |
| Env | config() di aplikasi; jangan env() di luar config | .env.example |
| Runtime | Gunakan FrankenPHP; aktifkan worker mode hanya setelah state dan memory lifecycle diuji | FrankenPHP / Octane |
app/
├── Actions/
├── Http/Controllers/Api/V1/
├── Http/Requests/
├── Http/Resources/
├── Models/
├── Repositories/
├── Services/
└── Transformers/
tests/
├── Feature/
└── Unit/Gunakan app/Transformers/ untuk memetakan model atau hasil service ke struktur data response yang konsisten. Batasi tanggung jawabnya pada pemetaan field dan format data; business logic tetap di service dan query di repository. Jika dipakai bersama Http/Resources, tetapkan Transformers sebagai mapper dan Resources sebagai pembungkus response agar pemetaan tidak diduplikasi.
FROM dunglas/frankenphp:1-php8.4-bookworm AS base
RUN install-php-extensions \
pdo_mysql \
pdo_pgsql \
intl \
opcache \
zip
WORKDIR /app
FROM base AS build
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--no-scripts \
--prefer-dist \
--optimize-autoloader
COPY . .
RUN composer dump-autoload --no-dev --classmap-authoritative --no-scripts \
&& php artisan package:discover --ansi
FROM base AS runtime
ENV APP_ENV=production \
APP_DEBUG=false \
SERVER_NAME=:8080 \
XDG_CONFIG_HOME=/config \
XDG_DATA_HOME=/data
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
&& mkdir -p /config /data storage/framework/cache storage/framework/sessions storage/framework/views \
&& chown -R www-data:www-data /config /data /app
COPY --from=build --chown=www-data:www-data /app /app
USER www-data
EXPOSE 8080
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile"]Strict TypeScript, validasi runtime, dan singleton resource adalah baseline layanan Node.js yang sehat.
| Area | Standar Node.js | Tooling |
|---|---|---|
| HTTP | Route/controller hanya orkestrasi | Fastify / NestJS / Express |
| Validation | DTO divalidasi saat runtime | Zod / Valibot / class-validator |
| Business | Service bebas detail transport | Dependency injection |
| Data | Repository + pool/client singleton | Prisma / TypeORM / Knex |
| Quality | strict: true, no implicit any | ESLint + Prettier |
| Test | Unit, integration, contract | Vitest atau Jest |
| Async | Worker terpisah dan idempotent | BullMQ / broker client |
| Env | Schema env divalidasi ketika startup | Zod + process.env |
| Runtime | Jalankan aplikasi melalui pm2-runtime; PM2 wajib berada di production dependencies | PM2 |
src/
├── config/
├── modules/users/
│ ├── user.controller.ts
│ ├── user.schema.ts
│ ├── user.service.ts
│ └── user.repository.ts
├── shared/
├── workers/
└── server.tsFROM node:24-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci \
&& npm ls pm2 --omit=dev
COPY . .
RUN npm run build && npm prune --omit=dev
FROM node:24-alpine AS runtime
ENV NODE_ENV=production \
PM2_HOME=/tmp/pm2
WORKDIR /app
COPY --from=build --chown=node:node /app/dist ./dist
COPY --from=build --chown=node:node /app/node_modules ./node_modules
RUN mkdir -p /tmp/pm2 && chown -R node:node /tmp/pm2
USER node
EXPOSE 3000
CMD ["./node_modules/.bin/pm2-runtime", "--json", "dist/server.js", "--name", "api"]Gunakan context untuk deadline, interface pada consumer boundary, dan graceful shutdown pada semua server.
| Area | Standar Go | Tooling |
|---|---|---|
| HTTP | Handler parse/validate/map response | net/http / Chi / Gin |
| Business | Usecase menerima context | Interface kecil |
| Data | Repository; sql.DB sebagai singleton pool | database/sql / sqlc |
| Timeout | Deadline diteruskan ke downstream | context.WithTimeout |
| Quality | Format dan static analysis wajib | gofmt, go vet, golangci-lint |
| Test | Table-driven + race detector | go test -race ./... |
| Shutdown | Stop traffic, drain request, close pool | signal.NotifyContext |
| Env | Parse dan validate config satu kali | envconfig / custom |
cmd/api/main.go
internal/
├── config/
├── user/
│ ├── handler.go
│ ├── service.go
│ └── repository.go
├── platform/database/
└── worker/
migrations/FROM golang:1.25-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /bin/api ./cmd/api
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=build /bin/api /api
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/api"]Layer yang jelas, DTO tervalidasi, exception terpusat, dan HikariCP terukur menjadi baseline layanan Java.
| Area | Standar Spring Boot | Tooling |
|---|---|---|
| HTTP | Controller + DTO, tidak expose entity | Jakarta Validation |
| Error | Satu format error global | @RestControllerAdvice |
| Business | Service dengan transaction boundary | @Transactional |
| Data | Repository dan query terukur | Spring Data JPA |
| Pool | Ukur size, timeout, lifetime | HikariCP + metrics |
| Schema | Migration versioned di CI/deploy | Flyway / Liquibase |
| Test | Unit dan sliced integration test | JUnit 5 / Mockito / Testcontainers |
| Env | Typed configuration + validation | @ConfigurationProperties |
com.company.service/
├── config/
├── common/exception/
└── user/
├── UserController.java
├── UserService.java
├── UserRepository.java
├── dto/
└── entity/FROM eclipse-temurin:25-jdk-alpine AS build
WORKDIR /app
COPY .mvn .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline
COPY src src
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:25-jre-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
USER app
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Tegaskan lifecycle session, schema boundary, dan proses worker agar layanan Python stabil dalam jangka panjang.
| Area | Standar Python | Tooling |
|---|---|---|
| Framework | Pilih sesuai kebutuhan, bukan tren | FastAPI / Django / Flask |
| Validation | Schema request dan response eksplisit | Pydantic / DRF Serializer |
| Business | Service terpisah dari route/view | Dependency injection ringan |
| Data | Session per unit-of-work, selalu close | SQLAlchemy / Django ORM |
| Quality | Type hint dan format konsisten | Ruff + mypy/pyright |
| Test | Fixture terisolasi dan integration DB | pytest + Testcontainers |
| Async | Worker terpisah, retry idempotent | Celery / RQ |
| Env | Typed settings divalidasi startup | pydantic-settings |
app/
├── api/v1/
├── core/config.py
├── db/session.py
├── models/
├── schemas/
├── repositories/
├── services/
├── workers/
└── main.py
tests/FROM python:3.14-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip wheel --no-cache-dir --wheel-dir /wheels -r requirements.txt
FROM python:3.14-slim
RUN useradd --create-home app
WORKDIR /app
COPY --from=builder /wheels /wheels
RUN pip install --no-cache-dir /wheels/*
COPY --chown=app:app . .
USER app
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]Dua keluaran mandatory untuk setiap repository backend: dokumentasi standar service dan Bruno API collection. Dokumen tambahan disediakan sesuai kebutuhan.
Gunakan docs/README.md sebagai indeks. Kelompokkan scope, alur bisnis, API, database, arsitektur, dan operasional dalam folder 01 sampai 06.
Simpan seluruh endpoint aktual dalam docs/03-api/bruno/ di repository: auth, environment non-rahasia, variable, contoh request/response, dan assertion penting.
Klik nama file untuk membuka contoh isinya. File Markdown, OpenAPI, dan Bruno tersedia.
docs/
├── 01-overview/
│ ├── system-context.md
│ └── container-diagram.md
├── 02-business-flow/
│ ├── application-flow.md
│ └── business-rules.md
├── 03-api/
│ ├── openapi.yaml
│ ├── authentication.md
│ ├── error-codes.md
│ └── bruno/
│ ├── bruno.json
│ ├── environments/
│ │ ├── local.bru
│ │ ├── staging.bru
│ │ └── production.bru.example
│ ├── auth/
│ │ └── login.bru
│ └── antrian/
│ ├── create-antrian.bru
│ └── detail-antrian.bru
├── 04-database/
│ ├── erd.md
│ └── data-dictionary.md
├── 05-architecture/
│ ├── sequence-diagrams.md
│ └── adr/
│ └── 0001-notifikasi-asynchronous.md
├── 06-operations/
│ ├── deployment.md
│ ├── environment.md
│ ├── monitoring.md
│ └── incident-runbook.md
└── README.md
Folder docs/ berisi template dengan contoh, bukan API yang sudah tersedia di portal. Sesuaikan service, endpoint, data, dan prosedur operasional dengan proyek. Diagram, OpenAPI, ADR, dan runbook terpisah tetap opsional; dokumentasi standar dan Bruno collection mandatory.
| Folder | Contoh isi |
|---|---|
| 01-overview | Pengguna, gateway, Antrian Service, database, dan provider notifikasi |
| 02-business-flow | Login → daftar → simpan antrian → notifikasi; rule kapasitas dan idempotency |
| 03-api | OpenAPI, autentikasi Bearer, error codes, dan Bruno: login/create/detail |
| 04-database | ERD serta dictionary service, antrian, idempotency, dan outbox |
| 05-architecture | Sequence diagram dan contoh ADR pengiriman notifikasi asynchronous |
| 06-operations | Template deployment, environment, monitoring, dan penanganan notifikasi tertunda |
meta {
name: Create Antrian
type: http
seq: 1
}
post {
url: {{base_url}}/api/v1/antrian
body: json
auth: bearer
}
auth:bearer {
token: {{access_token}}
}
headers {
Content-Type: application/json
Accept: application/json
Idempotency-Key: {{idempotency_key}}
}
body:json {
{
"service_code": "GENERAL"
}
}
script:post-response {
bru.setVar("antrian_id", "");
if (res.getStatus() === 201) {
bru.setVar("antrian_id", res.getBody().data.antrian_id);
}
}
tests {
test("Antrian dibuat dengan status waiting", function () {
const body = res.getBody();
expect(res.getStatus()).to.equal(201);
expect(body.success).to.equal(true);
expect(body.message).to.be.a("string");
expect(body.data.antrian_id).to.be.a("string").and.not.empty;
expect(body.data.status).to.equal("waiting");
expect(body.meta.request_id).to.be.a("string");
});
}
docs {
Contoh request yang mengubah data. Jalankan Login lebih dahulu.
Gunakan key baru untuk pendaftaran baru; retry memakai key dan payload yang sama.
}Buka docs/03-api/bruno/ di Bruno dan pilih environment local; base_url contoh http://localhost:8080.
Siapkan backend sesuai OpenAPI contoh atau sesuaikan request dengan endpoint proyek. Portal ini tidak menyediakan endpoint tersebut.
Sediakan LOGIN_EMAIL dan LOGIN_PASSWORD melalui environment proses Bruno lokal, lalu jalankan Login untuk memperoleh access_token sementara.
Jalankan Create Antrian, lalu Detail Antrian; antrian_id diteruskan melalui runtime variable.
Periksa assertion. Ganti idempotency_key untuk operasi baru; retry tetap menggunakan key dan body yang sama.
Identitas service, tujuan, scope bisnis, PIC teknis, repository, dan link JIRA.
Versi runtime, dependency, instalasi, command start, dan konfigurasi melalui .env.example tanpa secret.
Struktur kode, alur bisnis utama, serta dependency antar-service.
Database ownership, lokasi/command migration wajib, urutan eksekusi, dan recovery plan.
Lokasi docs/03-api/bruno/, cara memilih environment, autentikasi, dan menjalankan request.
Command test/build, deployment, healthcheck, observability, dan tautan release record di JIRA/MR.
Setiap endpoint memiliki request dengan method, path, parameter, header, body, dan kebutuhan autentikasi.
Group berdasarkan module/domain; gunakan nama request yang jelas dan file kebab-case.
Gunakan base_url dan variable token sesuai kebutuhan; simpan hanya konfigurasi non-rahasia di Git.
Dokumentasikan response/message dan error; tambahkan assertion untuk status dan hasil bisnis penting.
Perbarui collection bersama perubahan endpoint dan sertakan hasil pengujian pada MR.
| Dokumen | Kapan berguna |
|---|---|
| ADR | Keputusan arsitektur memiliki trade-off yang perlu dilacak |
| Diagram arsitektur / sequence | Alur antar-service sulit dijelaskan dengan teks |
| OpenAPI / Swagger terpisah | Consumer memerlukan schema machine-readable atau generated client |
| Runbook / troubleshooting terpisah | Prosedur operasional membutuhkan panduan rinci |
| Changelog terpisah | Perlu ringkasan release selain catatan JIRA/MR |
| Onboarding terpisah | Setup membutuhkan petunjuk lebih panjang |
Informasi penting tentang API, migration, deployment, dan recovery tetap wajib tersedia dalam dua keluaran baseline atau tautan JIRA/MR terkait. Dokumen tambahan tidak diwajibkan sebagai file tersendiri; jika dipakai, tetap perbarui bersama perubahan kode.
Developer memperbarui dokumentasi dan collection dalam MR yang sama. Reviewer memastikan isinya sesuai implementasi, dapat digunakan untuk setup dan verifikasi, serta bebas secret. Indeks dan contoh lengkap tersedia di docs/README.md. Bruno berada di docs/03-api/bruno/. Contoh memakai sistem antrian dan harus disesuaikan dengan backend proyek.
Pemeriksaan mandiri teknis sebelum push, merge, atau meminta review dari engineer lain.
Checklist tersimpan pada browser ini. Centang hanya setelah Anda memverifikasi implementasi atau bukti CI, bukan berdasarkan asumsi.
Syarat minimum sebelum sebuah layanan menerima traffic production dan masuk rotasi operasional.
Catat apa yang dirilis, perubahan database, cara deployment, bukti verifikasi, serta rollback plan sebelum meminta approval production. Salin hasil form ke tiket JIRA atau merge request agar menjadi dokumentasi bersama dan dapat ditelusuri kembali.
Pastikan merge request telah disetujui, pipeline lulus, artifact/image sudah teridentifikasi, dan scope release sesuai tiket JIRA.
Lengkapi Production Release Record, termasuk daftar fitur, perubahan API/config/dependency, serta dampak ke consumer.
Jika ada migration, review script, durasi, lock risk, kompatibilitas dengan versi aplikasi lama/baru, backup, dan rollback atau roll-forward plan.
Dapatkan approval dari pihak yang berwenang dan tentukan PIC, waktu deployment, kanal komunikasi, serta kondisi go/no-go.
Jalankan migration dan deployment mengikuti urutan yang telah disetujui; jangan menjalankan perubahan manual yang tidak tercatat.
Lakukan smoke test, healthcheck, validasi endpoint/queue/database, serta pantau log, metric, error rate, dan latency.
Jika acceptance criteria gagal atau metrik memburuk melewati batas, hentikan rollout dan jalankan rollback/roll-forward plan.
Perbarui release record dan tiket JIRA dengan hasil akhir, waktu selesai, versi yang aktif, serta incident/follow-up bila ada.
| Area | Yang wajib didokumentasikan |
|---|---|
| Schema | Tabel/collection, column/field, index, constraint, tipe data, dan relasi yang ditambah, diubah, atau dihapus. |
| Data migration | Data yang dipindahkan/backfill, estimasi volume dan durasi, batching, serta cara melanjutkan bila proses terhenti. |
| Compatibility | Pastikan migration kompatibel dengan aplikasi sebelum dan sesudah deploy; gunakan pola expand-migrate-contract untuk breaking change. |
| Operational risk | Potensi lock, downtime, kenaikan CPU/storage, replication lag, dan dampak terhadap query atau service lain. |
| Recovery | Backup/PITR yang tersedia, rollback atau roll-forward script, PIC eksekusi, dan batas waktu keputusan. |
| Verification | Query atau pemeriksaan untuk memastikan schema, jumlah data, constraint, index, serta aplikasi berjalan benar. |
| Kontrol | Standar |
|---|---|
| Protected environment | Batasi akses deploy production hanya untuk role atau pengguna yang berwenang. |
| Manual approval | Gunakan approval sebelum job production dijalankan jika fitur tersedia pada GitLab BIT. |
| Single deployment | Cegah dua deployment berjalan bersamaan pada environment yang sama, misalnya dengan resource_group: production. |
| Protected variable | Simpan secret sebagai protected dan environment-scoped CI/CD variable; jangan tulis nilainya di form atau repository. |
| Traceability | Catat environment, commit SHA/image tag, pipeline, pelaksana, waktu, dan hasil deployment. |
Setelah lengkap, salin Markdown atau unduh dokumen untuk dilampirkan ke JIRA atau merge request.
https://docs.gitlab.com/ci/environments/deployment_safety/Buka referensi Deployment sukses bukan akhir pekerjaan. Layanan dinyatakan siap ketika tim dapat mendeteksi masalah, mendiagnosisnya melalui telemetry, dan melakukan rollback dengan aman.