Fase 1 - Memahami Arsitektur Microservices dengan JHipster sebagai Laboratorium Instan

Regression Learning: Memahami Arsitektur Microservices dengan JHipster sebagai Laboratorium Instan

Bangun dulu. Pahami kemudian.

Kita akan menggunakan JHipster untuk mendapatkan sebuah laboratory microservices dengan cepat, lalu pada artikel-artikel berikutnya kita akan berjalan mundur untuk memahami apa yang sebenarnya terjadi di baliknya.

1. Kita Mulai dari yang Mudah

Kalau Anda seorang developer yang sudah bertahun-tahun bekerja dengan aplikasi monolith, kemungkinan besar Java, Spring, database, REST API, authentication, dan business logic bukanlah hal baru.

Yang mungkin terasa asing adalah ketika semuanya mulai terdistribusi.

Tiba-tiba kita menemukan istilah seperti:

  • Microservices — cara membangun aplikasi dengan memecahnya menjadi banyak service kecil yang berjalan sendiri-sendiri, bukan satu aplikasi besar (monolith).
  • API Gateway — satu pintu masuk yang menerima semua request dari client, lalu meneruskannya ke service yang tepat di belakangnya.
  • Service Discovery — mekanisme supaya satu service bisa "menemukan" alamat service lain secara otomatis, tanpa alamatnya di-hardcode.
  • Event Driven — cara service saling berkomunikasi dengan mengirim dan mendengarkan "kejadian" (event), bukan memanggil satu sama lain secara langsung.
  • Kafka — salah satu sistem message broker (perantara pesan) yang biasa dipakai untuk mengimplementasikan komunikasi event driven tadi.
  • Docker — alat untuk mengemas aplikasi beserta seluruh kebutuhannya ke dalam satu "container" yang bisa dijalankan konsisten di mesin manapun.
  • Kubernetes — platform untuk mengatur banyak container (menjalankan, menyalakan ulang, menyeimbangkan beban, dll) secara otomatis dalam skala besar.
  • OpenTelemetry — standar untuk mengumpulkan data observability (log, metric, trace) dari sistem yang terdistribusi, supaya kita bisa tahu apa yang sedang terjadi di dalamnya.

Anda tidak perlu menghafal definisi di atas. Cukup jadikan referensi kalau nanti istilahnya muncul lagi.

Dan yang lebih penting: jangan mulai belajar dari semuanya sekaligus.

Di seri ini kita akan menggunakan pendekatan yang sedikit berbeda:

Bangun sesuatu yang sudah jadi terlebih dahulu, kemudian kita mundur untuk memahami bagaimana dan mengapa sistem tersebut bekerja.

Saya menyebut pendekatan ini Regression Learning.


2. Kenapa JHipster?

Kita akan menggunakan JHipster sebagai laboratorium instan.

JHipster adalah sebuah application generator: alat yang menjawab beberapa pertanyaan lewat CLI (database apa yang dipakai, pakai authentication seperti apa, dst), lalu langsung membuatkan source code aplikasi Java/Spring yang siap dijalankan — lengkap dengan konfigurasi database, security, sampai frontend-nya.

Bayangkan seperti "wizard" saat install software, tapi hasil akhirnya bukan software yang terinstall, melainkan kode program yang bisa Anda baca, ubah, dan jalankan sendiri.

Untuk experiment kita, keuntungan utamanya sederhana:

Kita bisa mendapatkan sebuah lingkungan microservices yang bekerja tanpa harus menghabiskan hari pertama untuk membangun semuanya dari nol.

JHipster bukan tujuan akhir kita.

JHipster adalah alat eksperimen.

Dan seluruh konsep yang kita pelajari nantinya tetap bisa dibangun secara manual menggunakan Spring Boot dan teknologi lainnya.


3. Sedikit Tentang Yeoman

JHipster sendiri sebenarnya tidak membuat mesin generator-nya dari nol. Ia memanfaatkan Yeoman, yaitu framework yang dipakai untuk membuat code generator (jadi Yeoman adalah "generator-nya generator").

Sederhananya, alurnya begini:

Pilihan Developer
       ↓
 JHipster Generator
       ↓
 Yeoman
       ↓
Source Code + Configuration
       ↓
   Project Jadi

Jadi ketika kita menjalankan:

jhipster

dan memilih database, authentication, build tool, dan berbagai opsi lainnya, JHipster menggunakan generator untuk membentuk project berdasarkan pilihan tersebut.

Kita tidak perlu mempelajari Yeoman sekarang.

Cukup ingat:

Yeoman adalah mesin pembuat generator. JHipster memanfaatkan mesin tersebut untuk menghasilkan aplikasi.

Nanti konsep ini akan menjadi lebih menarik ketika kita membahas JHipster Blueprint.


Pre-Lab: Menyiapkan Microservices Laboratory

Sebelum membuat aplikasi, kita siapkan environment terlebih dahulu.

Kita tidak membutuhkan banyak hal.

Untuk experiment pertama cukup:

  • Java 21
  • Node.js LTS
  • NPM
  • Git
  • Docker
  • JHipster

4. Java 21

Java adalah development kit dan runtime yang akan digunakan untuk membangun serta menjalankan backend kita.

Cek:

java -version
javac -version

Pastikan menunjukkan Java 21.


5. Node.js dan NPM

Node.js adalah runtime JavaScript yang kita perlukan untuk menjalankan tooling JHipster (JHipster sendiri ditulis dan dijalankan di atas Node.js, meskipun aplikasi yang dihasilkannya berbasis Java).

NPM (Node Package Manager) adalah alat untuk meng-install package/library dari ekosistem Node.js — nanti kita pakai untuk meng-install JHipster itu sendiri.

Penting untuk digarisbawahi: kita tidak sedang menggunakan Node.js sebagai backend. Perannya di sini murni sebagai alat bantu (tooling) untuk generator.

Cek:

node --version
npm --version

Gunakan versi LTS Node.js.


6. Git

Git digunakan untuk menyimpan setiap tahap experiment sehingga kita bisa melihat perubahan dari satu tahap ke tahap berikutnya.

Cek:

git --version

7. Docker

Docker memungkinkan kita menjalankan aplikasi dalam container — bayangkan seperti "kotak" terisolasi yang sudah membawa aplikasi beserta semua kebutuhan runtime-nya (library, environment variable, dependency, dll), sehingga aplikasi tersebut bisa jalan sama persis di komputer manapun tanpa perlu install banyak hal secara manual.

Docker Compose adalah tool untuk menjalankan beberapa container sekaligus dengan satu file konfigurasi (misalnya: satu container untuk database, satu untuk service lain), alih-alih menjalankan satu-satu secara manual.

Nanti kombinasi Docker + Docker Compose akan sangat berguna ketika laboratory kita mulai terdiri dari banyak service dan database.

Cek:

docker --version
docker compose version

Lalu pastikan Docker benar-benar bisa menjalankan container:

docker run hello-world

Jika berhasil, Docker siap digunakan.


8. Install JHipster

Setelah Node.js dan NPM tersedia:

npm install -g generator-jhipster

Kemudian:

jhipster --version

Jika command tersebut menghasilkan versi JHipster, berarti CLI kita sudah siap.

Kita tidak perlu menghafalkan versinya. Yang penting CLI bisa dijalankan.


Environment Check

Sebelum mulai laboratory, lakukan pemeriksaan terakhir:

java -version
node --version
npm --version
git --version
docker --version
docker compose version
jhipster --version

Dan:

docker run hello-world

Kalau semuanya berjalan:

Java           ✓
Node.js        ✓
NPM            ✓
Git            ✓
Docker         ✓
Docker Compose ✓
JHipster       ✓

Laboratory Ready.

Kalau ada satu yang gagal, bereskan environment tersebut terlebih dahulu sebelum melanjutkan.


Praktek: Hasil Environment Check di Mesin Saya

Berikut hasil aktual dari environment check di atas, dijalankan pada macOS (Darwin), 22 Agustus 2026.

=== Java ===
java version "21.0.9" 2025-10-21 LTS
Java(TM) SE Runtime Environment (build 21.0.9+7-LTS-338)
Java HotSpot(TM) 64-Bit Server VM (build 21.0.9+7-LTS-338, mixed mode, sharing)

javac 21.0.9

=== Node & NPM ===
v25.9.0
11.12.1

=== Git ===
git version 2.50.1 (Apple Git-155)

=== Docker ===
Docker version 29.1.3, build f52814d
Docker Compose version v5.0.0-desktop.1

=== JHipster ===
(eval):5: command not found: jhipster

Temuan #1 — JHipster CLI belum terpasang.

Perintah jhipster --version gagal dengan command not found. Ini wajar untuk instalasi baru — artikel di bagian 8 memang mengasumsikan kita akan meng-install-nya. Solusinya jalankan:

npm install -g generator-jhipster

Output:

added 471 packages in 28s

147 packages are looking for funding
  run `npm fund` for details

Verifikasi ulang:

$ jhipster --version
9.2.0

Temuan #2 — Node.js yang terpasang bukan versi LTS.

Sedikit penjelasan dulu: Node.js merilis dua jalur versi secara paralel. LTS (Long Term Support) adalah versi yang mendapat dukungan dan perbaikan bug dalam jangka panjang, sehingga direkomendasikan untuk dipakai karena paling stabil. Current adalah versi rilis terbaru yang membawa fitur-fitur baru duluan, tapi umurnya lebih pendek dan berpotensi kurang stabil.

Artikel bagian 5 menyarankan menggunakan Node.js versi LTS, tapi versi yang terpasang di mesin saya adalah v25.9.0, yaitu jalur Current, bukan LTS (LTS aktif saat ini ada di jalur v22/v24). Untuk lab ini saya tetap melanjutkan dengan v25.9.0 karena JHipster 9.2.0 masih bisa generate project dengan baik, tapi ini dicatat sebagai potensi sumber masalah bila nanti ada perilaku Node yang tidak konsisten. Kalau ingin strict mengikuti rekomendasi, gunakan version manager (nvm/fnm) untuk switch ke Node LTS sebelum lanjut.

Docker langsung berhasil menjalankan docker run hello-world tanpa masalah:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
58dee6a49ef1: Pull complete
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

Hasil akhir environment check:

Java           ✓  21.0.9 LTS
Node.js        ✓  v25.9.0 (bukan LTS, lihat catatan di atas)
NPM            ✓  11.12.1
Git            ✓  2.50.1
Docker         ✓  29.1.3
Docker Compose ✓  v5.0.0-desktop.1
JHipster       ✓  9.2.0 (baru diinstall)

Laboratory Ready.


9. Apa yang Akan Kita Bangun?

Kita tidak akan membuat aplikasi e-commerce lengkap.

Kita hanya membutuhkan sesuatu yang cukup untuk mempelajari konsep.

Katakanlah kita membuat sistem sederhana:

                    Client
                       │
                       ↓
                 ┌───────────┐
                 │  Gateway  │
                 └─────┬─────┘
                       │
             ┌─────────┴─────────┐
             ↓                   ↓
      Product Service       Order Service
             │                   │
             ↓                   ↓
        Product DB           Order DB

Hanya itu.

Gateway + dua service + dua database.

Singkatnya, tiga hal yang akan kita generate:

  1. Gateway — pintu masuk tunggal untuk client (mis. browser/mobile app). Client tidak pernah bicara langsung ke Product Service atau Order Service, semua lewat Gateway dulu.
  2. Product Service — aplikasi backend kecil yang mengurus data produk, punya database sendiri.
  3. Order Service — aplikasi backend kecil yang mengurus data order, punya database sendiri juga (terpisah dari database Product).

Jangan tambahkan Kafka, Redis, Kubernetes, dan komponen lainnya dulu.

Belum. Fokus kita di Fase 1 ini murni: bisa generate, bisa jalan, bisa saling terhubung. Titik.


10. Generate Gateway

Buat project pertama menggunakan JHipster. Buat folder khusus untuk project ini, masuk ke dalamnya, lalu jalankan:

jhipster

Perintah ini akan membuka wizard interaktif yang menanyakan beberapa pertanyaan lewat terminal. Pertanyaan pertama yang penting untuk kita jawab:

Application type
→ Gateway

Untuk pertanyaan-pertanyaan lain yang muncul setelahnya (nama aplikasi, pilihan authentication, database, build tool, dst), pilih saja opsi default atau opsi yang paling umum/direkomendasikan (biasanya ditandai sebagai pilihan pertama). Detail dari setiap opsi tersebut tidak krusial untuk tujuan kita di Fase 1 ini — fokus kita adalah memahami bentuk arsitekturnya dulu, bukan menghafal setiap konfigurasi.

Untuk sekarang cukup pahami:

Gateway adalah pintu masuk yang digunakan client untuk berkomunikasi dengan service di belakangnya.

Artinya, nanti setelah semua service jalan, client (misalnya browser) cukup tahu satu alamat saja, yaitu alamat Gateway. Gateway-lah yang bertugas meneruskan request tersebut ke Product Service atau Order Service yang sesuai.

Kita belum perlu membahas bagaimana proses "meneruskan request ke service yang tepat" (disebut routing) dan bagaimana Gateway tahu alamat service-service tersebut (disebut service discovery) itu bekerja secara detail.

Itu akan menjadi bahan experiment berikutnya, setelah kita melihat dulu bahwa semuanya bisa berjalan.


Praktek: Generate Gateway di Mesin Saya

Karena saya menjalankan lab ini lewat automation environment (tidak ada terminal interaktif untuk menjawab wizard satu-satu dengan arrow key), saya memakai fitur JHipster JDL (JHipster Domain Language) — cara mendeklarasikan jawaban wizard di dalam sebuah file teks, lalu JHipster generate langsung berdasarkan file itu. Hasilnya sama persis dengan menjawab wizard secara manual, cuma cara memasukkan jawabannya saja yang berbeda.

Struktur folder kerja saya:

learning_microservices/
├── regression-learning-microservices-fase-1.md
└── lab/
    └── gateway/     ← akan dibuat oleh JHipster

File lab/gateway.jdl yang saya buat (isinya adalah representasi tertulis dari jawaban-jawaban wizard di bagian 10):

application {
  config {
    baseName gateway
    applicationType gateway
    packageName com.learningms.gateway
    authenticationType jwt
    prodDatabaseType postgresql
    devDatabaseType h2Disk
    buildTool maven
    clientFramework no
    serviceDiscoveryType no
    serverPort 8080
  }
}

Penjelasan pilihan di atas (setara dengan menjawab wizard satu per satu):

Pertanyaan wizard Jawaban Kenapa
Application type Gateway sesuai bagian 10
Authentication JWT paling sederhana untuk lab, tidak perlu setup OAuth server terpisah
Database PostgreSQL (prod), H2 (dev) H2 supaya bisa langsung jalan di laptop tanpa install database dulu
Build tool Maven paling umum di ekosistem Java
Client framework No (tanpa frontend) sengaja dikosongkan — fokus Fase 1 ini backend & arsitekturnya, bukan tampilan
Service discovery No supaya arsitekturnya tetap sesederhana diagram di bagian 9 (belum ada komponen registry tambahan)

Command yang dijalankan (dari dalam folder lab/):

jhipster jdl gateway.jdl --skip-git

Output (dipadatkan, baris create ... yang berjumlah ratusan disingkat):

Welcome to JHipster v9.2.0

INFO! Generating jdls gateway.jdl
     info Generating 1 application
 _______________________________________________________________________________________________________________

  Application files will be generated in folder: /Users/yusuf.ibrahim/Project2/learning_microservices/lab
 _______________________________________________________________________________________________________________

WARNING! Your Node version is not LTS (Long Term Support), use it at your own risk! JHipster does not support non-LTS releases, so if you encounter a bug, please use a LTS version first.
     info Generating 2,048 bit RSA key pair and self-signed certificate (SHA384withRSA) with a validity of 99,999 days
     info KeyStore '.../lab/src/main/resources/config/tls/keystore.p12' generated successfully.
- applying multi-step templates
✔ prettier configuration files committed to disk
   create pom.xml
   create mvnw
   create mvnw.cmd
   create package.json
   create src/main/java/com/learningms/gateway/GatewayApp.java
   create src/main/java/com/learningms/gateway/security/jwt/JWTRelayGatewayFilterFactory.java
   create src/main/java/com/learningms/gateway/config/SecurityConfiguration.java
   create src/main/resources/config/application.yml
   create src/main/resources/config/application-dev.yml
   create src/main/resources/config/application-prod.yml
   create src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml
   ... (± 180 file lain: domain User/Authority, REST resource, test, docker compose file, dsb)
✔ files committed to disk

Changes to package.json were detected.
Running npm install for you to install the required dependencies.

.git can't be found
added 489 packages, and audited 490 packages in 27s

160 packages are looking for funding
  run `npm fund` for details

2 high severity vulnerabilities
To address all issues (including breaking changes), run:
  npm audit fix --force

✔ Spring Boot 3.5.15 application generated successfully.
  Run your Spring Boot application:
  ./mvnw

Congratulations, JHipster execution is complete!

Temuan #3 — Peringatan Node non-LTS benar-benar muncul.

Persis seperti yang sudah dicatat di bagian Environment Check: JHipster sendiri menegur kita lewat WARNING! Your Node version is not LTS.... Generate tetap berhasil, tapi ini konfirmasi nyata bahwa memakai Node LTS itu bukan saran kosong.

Temuan #4 — .git can't be found.

Karena saya menjalankan jhipster jdl dengan flag --skip-git (belum ingin JHipster mengatur git secara otomatis untuk lab ini), husky (tool git-hook) sempat komplain saat proses npm install menjalankan prepare script-nya. Ini tidak mempengaruhi hasil generate — hanya berarti git hook belum aktif, yang memang belum kita butuhkan di tahap ini.

Temuan #5 — Ada 2 "high severity vulnerabilities" dari npm audit.

Ini peringatan standar dari dependency Node.js/frontend tooling JHipster (bukan dari kode aplikasi Java kita). Untuk lab belajar seperti ini aman diabaikan dulu; kalau untuk project production, ini wajib ditindaklanjuti dengan npm audit.

Hasil akhir: folder lab/gateway/ berisi project Spring Boot 3.5.15 lengkap (source code Java, pom.xml, mvnw, konfigurasi database, security JWT, dsb) — siap dijalankan dengan ./mvnw sesuai instruksi output di atas.


11. Generate Product Service

Buat folder baru (terpisah dari folder Gateway), lalu jalankan jhipster lagi di dalamnya untuk membuat project kedua.

Kali ini pilih:

Application type
→ Microservice application

Bedanya dengan Gateway: Microservice application menghasilkan backend "polos" tanpa tampilan/UI sendiri. Service ini tidak dirancang untuk diakses langsung oleh client — ia dirancang untuk dipanggil oleh Gateway (atau service lain).

Kita akan membuat sebuah entity sederhana bernama Product:

Product

id
name
price

Entity di sini maksudnya adalah sebuah "objek data" yang akan disimpan JHipster ke database sebagai tabel, lengkap dengan REST API CRUD (Create, Read, Update, Delete) yang otomatis dibuatkan untuknya. Jadi begitu Product selesai di-generate, kita sudah punya endpoint API untuk menambah, melihat, mengubah, dan menghapus data produk — tanpa menulis kode itu manual.

Tidak perlu membuat business logic yang rumit.

Tujuannya hanya supaya kita memiliki service yang benar-benar melakukan sesuatu (menyimpan dan menyajikan data), bukan sekadar service kosong.


Praktek: Generate Product Service di Mesin Saya

Sebelum generate Product Service, saya menemukan satu hal yang harus dibereskan dulu di Gateway.

Temuan #6 — JWT secret Gateway ternyata kosong (belum di-generate otomatis).

Saat mengecek application-dev.yml milik Gateway, ternyata baris konfigurasi jhipster.security.authentication.jwt.base64-secret tidak ada isinya sama sekali. Saya lacak ke kodenya (SecurityJwtConfiguration.java), dan benar — properti ini wajib diisi (tidak ada nilai default). Kalau dibiarkan, Gateway akan gagal start dengan error "Could not resolve placeholder".

Ini justru jadi poin penting untuk arsitektur microservices kita: base64-secret ini adalah kunci rahasia yang dipakai untuk menandatangani (dan memverifikasi) JWT token. Karena Product Service dan Order Service nantinya harus mempercayai token yang diterbitkan oleh Gateway, mereka wajib memakai secret yang sama persis. Kalau berbeda, token dari Gateway akan ditolak sebagai tidak valid oleh service lain.

Perbaikan: saya generate satu secret dengan:

openssl rand -base64 64

Lalu saya tambahkan nilai yang sama ke application-dev.yml di Gateway maupun Product Service:

security:
  authentication:
    jwt:
      base64-secret: zrh+MNKF02yrte8yRH4keAk9imkRI/CwlVVwBMV+1szcYmwnkPeAurbiBAfvVgXOx/IO7j9vUhrIXRxK/G52Xw==

Catatan: secret di atas hanya untuk lab lokal. Jangan pernah pakai/commit secret asli seperti ini untuk environment production — biasanya disuntikkan lewat environment variable atau secret manager.

Setelah itu, baru saya generate Product Service. File lab/product-service.jdl:

application {
  config {
    baseName productService
    applicationType microservice
    packageName com.learningms.product
    authenticationType jwt
    prodDatabaseType postgresql
    devDatabaseType h2Disk
    buildTool maven
    serviceDiscoveryType no
    serverPort 8081
  }
  entities Product
}

entity Product {
  name String required
  price BigDecimal required min(0)
}

Perhatikan: field id tidak perlu dituliskan di JDL — JHipster otomatis menambahkannya untuk setiap entity.

Command:

jhipster jdl product-service.jdl --skip-git

Output (dipadatkan):

INFO! Generating jdls product-service.jdl
     info Generating 1 application

WARNING! Your Node version is not LTS (Long Term Support), use it at your own risk!
WARNING! Microservice entities should have the id field type specified (e.g., id String) to make sure gateway and microservice types don't conflict
     info Generating 2,048 bit RSA key pair and self-signed certificate ...
- applying multi-step templates
✔ prettier configuration files committed to disk
   create pom.xml
   create src/main/java/com/learningms/product/domain/Product.java
   create src/main/java/com/learningms/product/web/rest/ProductResource.java
   create src/main/java/com/learningms/product/repository/ProductRepository.java
   create src/main/resources/config/liquibase/changelog/20231208120429_added_entity_Product.xml
   create src/main/resources/config/liquibase/fake-data/product.csv
   ... (± 150 file lain)
✔ files committed to disk

Running npm install for you to install the required dependencies.
.git can't be found
added 489 packages, and audited 490 packages in 33s
2 high severity vulnerabilities

✔ Spring Boot 4.0.7 application generated successfully.
  Run your Spring Boot application:
  ./mvnw

Congratulations, JHipster execution is complete!

Temuan #7 — Warning soal tipe field id di entity microservice.

WARNING! Microservice entities should have the id field type specified (e.g., id String) to make sure gateway and microservice types don't conflict

JHipster mengingatkan: di arsitektur microservices, sebaiknya tipe id dideklarasikan eksplisit (misalnya id String memakai UUID) supaya konsisten kalau suatu saat gateway juga perlu "mengenal" bentuk entity tersebut. Untuk lab sederhana ini saya biarkan default (Long, auto increment), tapi ini catatan bagus untuk arsitektur microservices yang lebih serius nantinya.

Aha Moment — Product Service ternyata pakai Spring Boot 4.0.7, Gateway pakai 3.5.15. Kok bisa beda?

Ini yang paling menarik dari sesi ini. Dua project di-generate pakai JHipster CLI versi yang sama persis (9.2.0), dalam rentang waktu hanya beberapa menit. Tapi:

Gateway         → Spring Boot 3.5.15
Product Service → Spring Boot 4.0.7

Awalnya ini terlihat seperti bug atau ketidakkonsistenan acak. Ternyata bukan. Setelah ditelusuri ke source code generator-nya, ketemu baris ini:

springBoot4: data => !(data.databaseTypeSql && data.reactive) && !data.databaseTypeCouchbase

Terjemahannya: JHipster akan memakai Spring Boot 4 untuk aplikasi manapun, kecuali aplikasi tersebut memakai kombinasi database SQL + reactive (atau database Couchbase) — dalam kasus itu, JHipster sengaja mundur ke Spring Boot 3.

Kenapa itu penting untuk kita:

  • Gateway di JHipster dibangun di atas Spring Cloud Gateway, yang sifatnya reactive, dan kita memakai database SQL (PostgreSQL/H2) → kena kombinasi "SQL + reactive" → JHipster sengaja menahannya di Spring Boot 3, kemungkinan karena dukungan reactive-SQL di ekosistem Spring Boot 4 belum sepenuhnya matang saat generator ini dirilis.
  • Product Service adalah microservice biasa (non-reactive, servlet-based) dengan database SQL → tidak kena kombinasi tadi → bebas dipakaikan Spring Boot 4, versi paling baru.

Jadi ini bukan cacat, melainkan keputusan sadar dari tim JHipster di tengah masa transisi ekosistem Spring Boot 3 → 4.

Yang lebih menarik lagi: ini justru pelajaran arsitektural yang berharga, bukan cuma trivia. Gateway dan Product Service saling berkomunikasi lewat HTTP/REST — bukan dengan saling meng-import class Java satu sama lain. Karena itu, mereka tidak perlu berjalan di atas versi Spring Boot yang sama untuk bisa saling bicara. Ini adalah salah satu keuntungan nyata microservices dibanding monolith: setiap service bebas memilih (atau terpaksa memilih, seperti kasus kita) versi framework/library-nya sendiri, selama kontrak komunikasinya (REST API, format JSON, dst) tetap kompatibel. Di monolith, satu project = satu versi Spring Boot untuk semua modul; tidak ada pilihan.

Hasil akhir: folder lab/product-service/ berisi project microservice Spring Boot 4.0.7 lengkap dengan entity Product (REST API CRUD otomatis di /api/products), dan JWT secret yang sudah disamakan dengan Gateway.


12. Generate Order Service

Lakukan hal yang sama seperti Product Service: buat folder baru, jalankan jhipster, pilih Microservice application, lalu buat entity Order:

Order

id
orderNumber
total

Sekarang kita memiliki tiga project yang berdiri sendiri-sendiri:

Gateway
Product Service
Order Service

Masing-masing adalah aplikasi yang terpisah sepenuhnya — folder sendiri, source code sendiri, dan (setelah dijalankan) proses sendiri. Tidak ada satupun dari ketiganya yang "menyatu" secara fisik dengan yang lain. Inilah salah satu ciri dasar microservices: setiap service bisa dikembangkan, di-build, dan dijalankan secara independen.


Praktek: Generate Order Service di Mesin Saya

Prosesnya sama persis seperti Product Service. File lab/order-service.jdl:

application {
  config {
    baseName orderService
    applicationType microservice
    packageName com.learningms.order
    authenticationType jwt
    prodDatabaseType postgresql
    devDatabaseType h2Disk
    buildTool maven
    serviceDiscoveryType no
    serverPort 8082
  }
  entities Order
}

entity Order {
  orderNumber String required
  total BigDecimal required min(0)
}

Perhatikan serverPort 8082 — sengaja beda dari Gateway (8080) dan Product Service (8081), supaya ketiganya bisa jalan bersamaan di satu laptop tanpa rebutan port.

Command:

jhipster jdl order-service.jdl --skip-git

Output (dipadatkan):

INFO! Generating jdls order-service.jdl
     info Generating 1 application

WARNING! Your Node version is not LTS (Long Term Support), use it at your own risk!
WARNING! Microservice entities should have the id field type specified (e.g., id String) to make sure gateway and microservice types don't conflict
     info Generating 2,048 bit RSA key pair and self-signed certificate ...
- applying multi-step templates
✔ prettier configuration files committed to disk
   create pom.xml
   create src/main/java/com/learningms/order/domain/Order.java
   create src/main/java/com/learningms/order/web/rest/OrderResource.java
   create src/main/java/com/learningms/order/repository/OrderRepository.java
   create src/main/resources/config/liquibase/changelog/20231226115209_added_entity_Order.xml
   create src/main/resources/config/liquibase/fake-data/jhi_order.csv
   ... (± 150 file lain)
✔ files committed to disk

Running npm install for you to install the required dependencies.
.git can't be found
added 489 packages, and audited 490 packages in 27s
2 high severity vulnerabilities

✔ Spring Boot 4.0.7 application generated successfully.
  Run your Spring Boot application:
  ./mvnw

Congratulations, JHipster execution is complete!

Seperti dugaan dari Aha Moment sebelumnya: Order Service juga non-reactive + SQL, jadi ia ikut mendapat Spring Boot 4.0.7 — sama dengan Product Service. Konsisten dengan aturan yang sudah kita temukan di source code generator.

Temuan #8 — Nama tabel Order diam-diam berubah jadi jhi_order.

Perhatikan baris ini di output generate:

create src/main/resources/config/liquibase/fake-data/jhi_order.csv

Dan kalau dicek di kode:

@Entity
@Table(name = "jhi_order")

Bukan kesalahan — ini fitur pengaman dari JHipster. ORDER adalah reserved keyword di SQL (dipakai untuk klausa ORDER BY). Kalau nama tabel dibiarkan persis order, query SQL yang dihasilkan bisa ambigu atau error di beberapa database. JHipster otomatis mendeteksi ini dan menambahkan prefix jhi_ supaya aman.

Pelajaran buat pembaca: ini kenapa penting mengenal reserved keyword di database sebelum menamai entity/tabel sendiri secara manual (di luar JHipster) — nama seperti order, user, group, atau select berpotensi bikin masalah kalau tidak di-quote atau diberi prefix.

Setelah generate, JWT secret yang sama (dari bagian sebelumnya) juga saya tambahkan ke application-dev.yml Order Service, supaya ketiga aplikasi konsisten saling percaya token.

Hasil akhir: kita sekarang punya tiga folder mandiri:

learning_microservices/
└── lab/
    ├── gateway/           (Spring Boot 3.5.15, port 8080)
    ├── product-service/   (Spring Boot 4.0.7, port 8081)
    └── order-service/     (Spring Boot 4.0.7, port 8082)

Ketiganya sudah punya JWT secret yang sama, database sendiri-sendiri (H2 untuk dev), dan siap dijalankan.


13. Jalankan Laboratory

Setiap project yang di-generate JHipster sudah dilengkapi wrapper script untuk menjalankannya, tanpa perlu install build tool secara terpisah — tergantung pilihan build tool saat generate tadi, biasanya salah satu dari:

./mvnw          # jika memilih Maven
./gradlew       # jika memilih Gradle

Jalankan ketiga project tersebut (Gateway, Product Service, Order Service) satu per satu, masing-masing di terminal-nya sendiri, sesuai konfigurasi project yang dihasilkan JHipster tadi. Setiap service akan berjalan di port-nya masing-masing sehingga bisa aktif bersamaan tanpa bentrok.

Kemudian lihat apa yang terjadi.

Client masuk melalui:

Client
  ↓
Gateway

Gateway kemudian seharusnya meneruskan request ke service yang sesuai:

Client
  ↓
Gateway
  ├──→ Product Service
  │
  └──→ Order Service

Dan masing-masing service memiliki persistence sendiri — istilah "persistence" di sini artinya tempat penyimpanan data permanen (database). Jadi Product Service menyimpan datanya sendiri, terpisah dari database milik Order Service.

Kata "seharusnya" di atas sengaja saya garis bawahi, karena pada praktek di bawah ini kita akan membuktikan sendiri apakah benar seperti itu — dan jawabannya cukup mengejutkan.


Praktek: Menjalankan Ketiga Aplikasi di Mesin Saya

Ini adalah bagian paling "berdarah-darah" dari Fase 1 — dua kesalahan konfigurasi nyata ditemukan di sini, lengkap dengan proses debug-nya. Saya sengaja tulis lengkap termasuk yang gagal, supaya kalau pembaca mengalami hal serupa, sudah ada peta jalannya.

Percobaan 1 — Gagal: Rebutan Port

Command yang dijalankan (tiga terminal terpisah):

cd lab/gateway && ./mvnw
cd lab/product-service && ./mvnw
cd lab/order-service && ./mvnw

Hasil: Gateway langsung gagal start.

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Saya cek siapa pemilik port 8080:

lsof -i :8080

Ternyata port 8080 (default Gateway) dan 8081 (default Product Service) sudah dipakai oleh project kerja saya yang lain yang sedang berjalan di background (satu aplikasi Spring Boot lewat IntelliJ, satu lagi Vite dev server). Bukan masalah dari JHipster maupun konfigurasi kita — murni port bentrok dengan proses lain di laptop yang sama.

Keputusan penting: saya tidak mematikan proses-proses tersebut, karena itu pekerjaan lain yang sedang berjalan dan bukan wewenang lab ini untuk mengganggunya. Solusi yang benar untuk kasus seperti ini selalu sama: ubah port aplikasi kita, bukan matikan aplikasi orang lain.

Perbaikan: saya pindahkan server.port di application-dev.yml masing-masing aplikasi ke rentang yang sudah dipastikan kosong lebih dulu dengan lsof:

Aplikasi Port lama Port baru
Gateway 8080 8770
Product Service 8081 8771
Order Service 8082 8772

Pelajaran buat pembaca: ini kenapa penting membiasakan cek port yang tersedia (lsof -i :<port>) sebelum menjalankan service baru, terutama di laptop development yang biasanya sudah punya banyak proses lain berjalan. Jangan reflek mematikan proses asing hanya karena porta yang kita mau kebetulan terpakai.

Percobaan 2 — Berhasil Start, Tapi Ada yang Aneh

Setelah port diganti, ketiga aplikasi berhasil start:

Started GatewayApp in 3.602 seconds
	Application 'gateway' is running! Access URLs:
	Local: 		http://localhost:8770/

Started ProductServiceApp in 6.822 seconds
	Application 'productService' is running! Access URLs:
	Local: 		http://localhost:8771/

Started OrderServiceApp in 6.822 seconds
	Application 'orderService' is running! Access URLs:
	Local: 		http://localhost:8772/

Saya cek kesehatan ketiganya lewat endpoint actuator bawaan JHipster:

curl http://localhost:8770/management/health
curl http://localhost:8771/management/health
curl http://localhost:8772/management/health

Ketiganya menjawab sama:

{"status":"UP","groups":["liveness","readiness"]}

Screenshot langsung dari browser (bukan cuma curl), supaya lebih meyakinkan:

Gateway health check
Gateway (port 8770) — {"status":"UP",...}

Product Service health check
Product Service (port 8771) — {"status":"UP",...}

Order Service health check
Order Service (port 8772) — {"status":"UP",...}

Ketiga aplikasi hidup dan sehat, masing-masing di dunia-nya sendiri. Sampai sini sesuai rencana.

Percobaan 3 — Membuktikan Pertanyaan di Bagian 14 Lebih Awal

Saya penasaran, jadi saya coba duluan salah satu pertanyaan yang harusnya baru kita bahas nanti: "Apakah Gateway benar-benar bisa meneruskan request ke Product Service?"

Konvensi routing JHipster untuk gateway tanpa service discovery biasanya berbentuk /services/{nama-service}/**. Saya coba:

curl -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8770/services/productservice/api/products

Hasil: HTTP 401.

Awalnya saya kira ini artinya "route-nya ada, tapi butuh login". Ternyata dugaan itu salah — saya buktikan dengan mencoba path yang jelas-jelas tidak pernah ada:

curl -o /dev/null -w "HTTP %{http_code}\n" http://localhost:8770/services/tidakada/api/apapun

Hasil: HTTP 401 juga. Sama persis.

Artinya: 401 ini datang dari Spring Security yang menolak semua request tak-terautentikasi ke pola path /services/**, jauh sebelum Spring Cloud Gateway sempat memeriksa apakah route-nya benar-benar ada. Jadi 401 di sini bukan bukti route ada — cuma bukti bahwa security-nya bekerja.

Untuk membuktikan yang sebenarnya, saya perlu login dulu:

curl -X POST http://localhost:8770/api/authenticate \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}'

(Catatan: admin/admin adalah user bawaan yang otomatis dibuat JHipster untuk keperluan development.)

Respons memberi JWT token. Saya pakai token itu untuk mencoba lagi lewat Gateway:

curl -H "Authorization: Bearer <token>" http://localhost:8770/services/productservice/api/products

Hasil kali ini:

{
  "type" : "https://www.jhipster.tech/problem/problem-with-message",
  "title" : "Not Found",
  "status" : 404,
  "detail" : "404 NOT_FOUND \"No static resource services/productservice/api/products.\"",
  "path" : "http://localhost:8770/services/productservice/api/products"
}

404 Not Found — kali ini baru bukti yang sesungguhnya. Setelah lolos dari pemeriksaan security, Gateway benar-benar tidak tahu ke mana harus meneruskan request itu. Ia bahkan mencoba mencarinya sebagai static file (perilaku default Spring saat tidak ada route/controller yang cocok) dan gagal menemukannya.

Ini adalah jawaban nyata untuk salah satu pertanyaan di bagian 14 nanti: "Bagaimana Gateway tahu Product Service berada di mana?" Jawabannya, dibuktikan langsung: sekarang ini, dia belum tahu sama sekali. Wajar — kita sengaja memilih serviceDiscoveryType: no di bagian 10, dan tidak menambahkan rute manual apapun. Ini akan menjadi topik utama di Fase 2.

Screenshot bukti langsung dari browser (request dan response dirender di halaman supaya terlihat jelas):

404 saat mencoba lewat Gateway
Lewat Gateway dengan token valid → tetap 404 Not Found, karena route memang tidak pernah didaftarkan.

Percobaan 4 — Gagal Lagi: Token Ditolak Padahal Secret Sudah Disamakan

Sambil di sana, saya coba juga akses langsung ke Product Service (bukan lewat Gateway) pakai token yang sama:

curl -H "Authorization: Bearer <token>" http://localhost:8771/api/products

Hasil: HTTP 401 — token ditolak. Ini aneh, karena kita sudah repot-repot menyamakan base64-secret di application-dev.yml Gateway dan Product Service sejak bagian 11.

Investigasi: saya perhatikan baris di banner startup tadi:

Profile(s): [dev, secret-samples, api-docs]

Ada profile tambahan bernama secret-samples yang ikut aktif, bukan cuma dev. Saya cek isi filenya, application-secret-samples.yml, yang bahkan sudah punya komentar penjelasan sendiri di generated code-nya:

# ===================================================================
# Samples for required secrets to be used in development and ci using prod profile.
# This configuration overrides the application.yml file.
# ===================================================================

Dan benar saja — file ini punya base64-secret isinya sendiri, berbeda di setiap aplikasi (JHipster men-generate satu nilai acak per aplikasi):

Gateway:          YWI1ZTRhNmJiYTEy... (secret A)
Product Service:  NGY0ZTc4ZGZmNjY4... (secret B, BEDA dari A)
Order Service:    N2ZjMWY3ZmNmNTQz... (secret C, BEDA lagi)

Ini akar masalahnya. Karena file application-secret-samples.yml dimuat lebih akhir (dan karenanya menang) dibanding application-dev.yml, nilai secret yang saya set manual di bagian 11 tidak pernah benar-benar terpakai. Gateway menandatangani token pakai secret A miliknya sendiri, sementara Product Service mencoba memverifikasinya pakai secret B miliknya sendiri — tidak akan pernah cocok.

Perbaikan: saya edit ulang, kali ini di file yang benar-benar dipakai — application-secret-samples.yml — di ketiga aplikasi, menyamakan nilainya:

jhipster:
  security:
    authentication:
      jwt:
        base64-secret: zrh+MNKF02yrte8yRH4keAk9imkRI/CwlVVwBMV+1szcYmwnkPeAurbiBAfvVgXOx/IO7j9vUhrIXRxK/G52Xw==

Lalu restart ketiga aplikasi.

Pelajaran buat pembaca: mengedit satu file konfigurasi (application-dev.yml) tidak menjamin nilainya benar-benar dipakai kalau ada profile lain yang ikut aktif dan memuat setelahnya. Selalu cek baris Profile(s): [...] di banner startup Spring Boot — itu daftar semua profile yang benar-benar aktif, bukan cuma yang secara eksplisit kita niatkan.

Percobaan 5 — Berhasil Sepenuhnya

Setelah restart, saya ulangi seluruh alur dari awal:

# 1. Login ke Gateway
curl -X POST http://localhost:8770/api/authenticate \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin"}'
# → dapat token baru

# 2. Akses LANGSUNG ke Product Service dengan token itu
curl -H "Authorization: Bearer <token>" http://localhost:8771/api/products

Hasil kali ini HTTP 200, dengan data sungguhan (fake data hasil seed otomatis JHipster):

[
  {"id":1,"name":"yippee","price":8498.29},
  {"id":2,"name":"gosh","price":13165.96},
  {"id":3,"name":"rosy","price":9744.99}
]

(dipotong, aslinya 30 baris data)

Order Service juga menjawab HTTP 200 dengan pola yang sama.

200 OK langsung ke Product Service
Akses langsung ke Product Service (tanpa lewat Gateway) dengan token yang sama → 200 OK, 30 data produk berhasil didapat.

Sekarang token dari Gateway benar-benar dipercaya oleh Product Service dan Order Service. Tapi ketika saya coba lagi lewat Gateway (/services/productservice/api/products), hasilnya tetap 404 — mengonfirmasi bahwa masalah token dan masalah routing adalah dua hal yang benar-benar terpisah, dan keduanya sekarang sama-sama sudah kita pahami akar penyebabnya.

Ringkasan Semua Temuan di Bagian Ini

# Masalah Penyebab Perbaikan
1 Gateway gagal start Port 8080/8081 dipakai proses lain di laptop Pindah ke port 8770/8771/8772, tanpa mematikan proses siapapun
2 401 di path Gateway yang bahkan tidak ada Spring Security menolak semua path /services/** yang tak-terautentikasi, sebelum proses routing dicek Login dulu untuk dapat JWT, baru bisa menguji apakah route-nya benar-benar ada
3 Gateway 404 saat meneruskan ke Product Service serviceDiscoveryType: no + tidak ada route manual → Gateway memang tidak tahu Product/Order Service ada (belum diperbaiki — sengaja, ini topik Fase 2)
4 Token dari Gateway ditolak Product/Order Service (401) Profile secret-samples menimpa secret manual kita dengan secret acak yang berbeda di tiap aplikasi Edit application-secret-samples.yml di ketiga aplikasi dengan secret yang sama

Cheat Sheet: Menyalakan, Mengecek, dan Mematikan Service

Karena kita akan bolak-balik menjalankan lab ini (apalagi nanti di Fase 2), berikut catatan praktis yang saya pakai sendiri selama sesi ini — supaya pembaca tidak perlu mengulang proses trial-and-error yang sama.

1. Menyalakan

Jalankan tiap aplikasi dari folder-nya masing-masing. Bisa di tiga terminal terpisah:

cd lab/gateway && ./mvnw
cd lab/product-service && ./mvnw
cd lab/order-service && ./mvnw

Atau kalau mau satu perintah saja (jalan di background, log ditulis ke file):

cd lab
(cd gateway && ./mvnw > ../gateway-run.log 2>&1 &)
(cd product-service && ./mvnw > ../product-service-run.log 2>&1 &)
(cd order-service && ./mvnw > ../order-service-run.log 2>&1 &)

Tunggu sampai muncul baris Started XxxApp in N seconds di masing-masing log — itu tandanya aplikasi sudah siap menerima request (biasanya 4–8 detik kalau dependency sudah ter-cache di ~/.m2).

2. Mengecek Status

Cek apakah port sedang dipakai (berguna sebelum start, untuk hindari bentrok seperti Percobaan 1 di atas):

lsof -i :8770   # Gateway
lsof -i :8771   # Product Service
lsof -i :8772   # Order Service

Kosong = belum ada yang jalan di situ. Ada output = sudah dipakai (cek dulu proses apa sebelum menyimpulkan itu punya kita).

Cek apakah aplikasi benar-benar hidup dan sehat, pakai actuator health endpoint bawaan JHipster (selalu publik, tidak butuh login):

curl http://localhost:8770/management/health
curl http://localhost:8771/management/health
curl http://localhost:8772/management/health

Jawaban sehat: {"status":"UP","groups":["liveness","readiness"]}.

Cek proses Java yang sedang berjalan (kalau butuh tahu PID untuk keperluan lain):

pgrep -f "com.learningms"
# atau lebih spesifik per aplikasi:
pgrep -f "com.learningms.gateway.GatewayApp"
pgrep -f "com.learningms.product.ProductServiceApp"
pgrep -f "com.learningms.order.OrderServiceApp"

3. Mematikan

Selalu cari tahu dulu PID mana yang benar-benar milik lab kita sebelum mematikan apapun — jangan pernah asal kill berdasarkan nomor port saja, karena port itu bisa saja dipakai proses lain yang tidak ada hubungannya dengan lab ini (persis seperti kejadian di Percobaan 1).

pgrep -f "com.learningms"
kill <PID1> <PID2> <PID3>

Beri waktu 2–3 detik untuk graceful shutdown, lalu verifikasi sudah benar-benar berhenti:

sleep 3
pgrep -f "com.learningms" || echo "semua sudah berhenti"

Kalau masih ada yang bandel setelah beberapa detik, baru gunakan kill -9 <PID> sebagai upaya terakhir.

Aturan yang saya pegang selama sesi ini: boleh mematikan proses yang jelas-jelas kita mulai sendiri sebagai bagian dari lab. Tidak boleh mematikan proses lain di laptop yang kebetulan memakai port yang sama — solusinya selalu pindah port kita, bukan usir proses orang lain.


Diagram Arsitektur Hasil Praktek

Component Diagram

Ini bentuk nyata dari apa yang sudah berhasil kita bangun dan jalankan — bukan lagi rencana, tapi kondisi yang sudah terverifikasi lewat curl di atas.

graph TB
    Client["Client<br/>(curl / browser)"]

    subgraph GW["Gateway — port 8770"]
        GWAuth["/api/authenticate<br/>(menerbitkan JWT)"]
        GWRoute["Spring Cloud Gateway routing<br/>(HANYA punya rute ke dirinya sendiri)"]
    end

    subgraph PS["Product Service — port 8771"]
        PSApi["/api/products"]
        PSDB[("Product DB<br/>H2, terpisah")]
        PSApi --> PSDB
    end

    subgraph OS["Order Service — port 8772"]
        OSApi["/api/orders"]
        OSDB[("Order DB<br/>H2, terpisah")]
        OSApi --> OSDB
    end

    Secret[["base64-secret<br/>(disamakan manual di 3 aplikasi)"]]

    Client -->|"1 . login"| GWAuth
    GWAuth -.->|signs with| Secret
    Client -->|"2 . request langsung + token"| PSApi
    Client -->|"2 . request langsung + token"| OSApi
    PSApi -.->|verifies with| Secret
    OSApi -.->|verifies with| Secret
    Client -.->|"3 . lewat Gateway → 404 Not Found"| GWRoute
    GWRoute -.->|"rute tidak ada"| PSApi

    style GWRoute fill:#fff3cd,stroke:#d39e00

Penjelasan: garis putus-putus dari Gateway ke Product Service sengaja digambar putus — itu justru poin utamanya. Secara arsitektur, gambar besarnya (bagian 9) memang Gateway di depan lalu meneruskan ke dua service. Tapi secara kenyataan konfigurasi saat ini, jalur itu belum ada. Client baru bisa mengakses Product/Order Service kalau memanggil portnya secara langsung.

Flow Diagram (Sequence)

Diagram ini menunjukkan urutan request yang benar-benar terjadi saat pengujian di atas — termasuk jalur yang berhasil maupun yang gagal.

sequenceDiagram
    participant C as Client
    participant G as Gateway :8770
    participant P as Product Service :8771

    C->>G: POST /api/authenticate (admin/admin)
    G-->>C: 200 OK + JWT token

    Note over C,P: Jalur A — akses langsung (berhasil)
    C->>P: GET /api/products (Bearer token)
    P->>P: verifikasi signature dengan shared secret
    P-->>C: 200 OK + data produk

    Note over C,G: Jalur B — lewat Gateway (gagal, sesuai rencana Fase 2)
    C->>G: GET /services/productservice/api/products (Bearer token)
    G->>G: Spring Security: token valid, lanjut
    G->>G: cari route yang cocok untuk "productservice"
    G-->>C: 404 Not Found ("No static resource ...")
    Note right of G: Route ke Product Service<br/>memang tidak pernah didaftarkan

Penjelasan: perhatikan bahwa Jalur A dan Jalur B sama-sama membawa token yang valid dan sudah lolos autentikasi. Perbedaan hasilnya (200 vs 404) murni soal routing, bukan soal keamanan. Ini bukti paling jernih bahwa "punya Gateway" dan "Gateway tahu cara meneruskan ke service lain" adalah dua hal yang berbeda — dan kita baru punya yang pertama.

Activity Diagram

Diagram ini merangkum keseluruhan aktivitas Fase 1 dari awal generate sampai lab benar-benar terverifikasi jalan, termasuk dua titik keputusan (troubleshooting) yang benar-benar kita alami.

flowchart TD
    A["Tulis JDL: gateway.jdl,<br/>product-service.jdl, order-service.jdl"] --> B["jhipster jdl *.jdl<br/>(generate 3 aplikasi)"]
    B --> C["Set base64-secret sama<br/>di application-dev.yml"]
    C --> D["./mvnw untuk ketiga aplikasi"]
    D --> E{"Start sukses?"}
    E -- "Gagal: port 8080/8081<br/>dipakai proses lain" --> F["Cek lsof -i, JANGAN matikan<br/>proses orang lain, pindah port"]
    F --> D
    E -- "Ya" --> G["Cek /management/health<br/>ketiga aplikasi"]
    G --> H["POST /api/authenticate<br/>ambil JWT dari Gateway"]
    H --> I["Panggil /api/products langsung<br/>dengan token"]
    I --> J{"200 OK?"}
    J -- "Gagal: 401,<br/>secret beda-beda" --> K["Temukan profile secret-samples<br/>menimpa secret manual kita"]
    K --> L["Edit application-secret-samples.yml<br/>di 3 aplikasi, samakan secret"]
    L --> D
    J -- "Ya" --> M["Coba akses via Gateway<br/>/services/productservice/**"]
    M --> N{"Route ada?"}
    N -- "Tidak: 404 Not Found" --> O["Terbukti: Gateway belum tahu<br/>lokasi Product/Order Service"]
    O --> P["Lanjut ke Bagian 14 —<br/>pertanyaan terbuka untuk Fase 2"]

    style F fill:#fff3cd,stroke:#d39e00
    style K fill:#fff3cd,stroke:#d39e00
    style O fill:#f8d7da,stroke:#dc3545

Penjelasan: dua kotak kuning adalah dua momen troubleshooting nyata yang kita alami (port bentrok, dan secret yang diam-diam ditimpa profile lain). Kotak merah di akhir adalah temuan yang justru disengaja tetap dibiarkan terbuka — itulah jembatan menuju Fase 2.


14. Berhenti Sebentar

Sampai di sini, jangan buru-buru menjelaskan semuanya.

Kita sudah berhasil membuat sesuatu yang disebut:

Microservices Architecture

Tetapi coba perhatikan diagram tadi.

Kalau diperhatikan baik-baik, ada beberapa hal yang sebenarnya belum kita ketahui jawabannya — dan itu wajar, karena memang belum kita bahas. Beberapa pertanyaan menarik yang muncul:

  • Bagaimana Gateway tahu Product Service berada di mana?
  • Bagaimana Gateway tahu Order Service berada di mana?
  • Bagaimana service-service tersebut berkomunikasi?
  • Kenapa database-nya dipisahkan?
  • Apa yang terjadi kalau Product Service mati?
  • Bagaimana kalau network bermasalah?
  • Apakah sistem ini benar-benar lebih baik daripada monolith?

Nah.

Sekarang kita mulai melakukan regression.


15. Kita Mundur

Pertanyaan-pertanyaan di atas belum akan kita jawab sekarang. Pada artikel berikutnya (Fase 2) barulah kita akan mengambil sistem yang baru saja kita buat ini dan membongkarnya satu per satu.

Kita akan mulai dari pertanyaan paling fundamental:

Sebenarnya apa yang berubah ketika sebuah monolith kita pecah menjadi microservices?

Kita akan membuat versi monolith-nya.

Kemudian membandingkannya dengan versi microservices yang baru saja kita buat.

Dari sana kita akan mulai memahami:

  • coupling
  • service boundary
  • bounded context
  • database per service
  • synchronous communication
  • distributed system
  • trade-off microservices

Dan perlahan-lahan kita akan menghilangkan "magic" JHipster.

Karena tujuan akhir seri ini bukan supaya Anda hafal command JHipster.

Tujuannya adalah supaya ketika suatu hari Anda melihat arsitektur seperti ini:

Gateway
   ↓
Service A
   ↓
Kafka
   ↓
Service B
   ↓
Database

Anda tidak lagi melihatnya sebagai sekumpulan teknologi yang menakutkan.

Anda bisa melihat:

"Oh, saya tahu kenapa komponen-komponen ini ada di sini."

Dan dari situlah perjalanan kita benar-benar dimulai.

Comments

Popular posts from this blog

Numpang Kerja Remote dari Bandung Creative Hub

Debugging PHP Web dengan XDebug di Intellij IDEA (PHP STORM)

Membangun AI Development Assistant Lokal