- product-service (HTTP/REST, 8081): Müşteriden gelen indirim hesaplama isteğini alır. gRPC istemcisi olarak davranıp diğer servise sorar. Ürün/kategori veritabanı bu servistedir.
- discount-service (gRPC, 9090 / Spring Boot 8082): İndirim kodu ve kategoriye göre indirim tutarını hesaplar. İndirim ve kategori veritabanı bu servistedir.
- order-service (HTTP/REST, 8083): Sipariş oluşturur, toplam tutarı hesaplar ve RabbitMQ kuyruğuna sipariş oluşturuldu olayı yayınlar.
- notification-service (HTTP/REST 8084): RabbitMQ
order.createdkuyruğunu dinler, e-posta bildirimi gönderir (dev: MailHog). - Veritabanları: Her servis kendi Postgres’ine bağlıdır.
product-service(54322),discount-service(54323),order-service(54324).product-servicetarafındaki kategori ilediscount-servicetarafındaki kategori,external_idüzerinden eşleşir.
- Her iki servis Spring Boot ile açılır, kendi Postgres’ine Hikari pool üzerinden bağlanır.
discount-servicegRPC sunucusunu 9090 portunda yayımlar veDiscountServiceRPC’sini kaydeder.product-service,application.properties’ten gRPC host/port’u okur veManagedChannel+BlockingStubhazırlar.- Her iki DB’ye örnek veriler
data.sqlile yüklenir (dev senaryosu).
- order-service bir sipariş oluşturduğunda
order.createdkuyruğuna bir mesaj yayınlar (RabbitMQ). - notification-service bu kuyruğu dinler ve MailHog üzerinden kullanıcıya "siparişiniz oluşturuldu" e-postası gönderir.
RabbitMQ tasarımı (bu proje):
- Exchange tanımlamadık, varsayılan empty direct exchange kullanılır.
- Kuyruk:
order.created(durable). ProducerconvertAndSend(queueName, payload)ile yayın yapar. - Mesaj formatı: JSON (
Jackson2JsonMessageConverter).
Bu projede her servis kendi compose/çalıştırma adımlarına sahiptir.
- Notification stack (RabbitMQ + MailHog + notification-service)
cd notification-service
docker compose up --build
- RabbitMQ UI:
http://localhost:15672(guest/guest) - MailHog UI:
http://localhost:8025
- Order Postgres
cd ../order-service
docker compose up -d
- Order-service’i çalıştır
./gradlew bootRun
# veya
./gradlew build && java -jar build/libs/order_service-0.0.1-SNAPSHOT.jar
- (Opsiyonel) Discount ve Product servisleri
- Her servisin kendi
docker-compose.ymldosyası Postgres’i ayağa kaldırır. - Servisleri
./gradlew bootRunile çalıştırabilirsiniz.
- Örnek sipariş oluşturma isteği (order-service)
POST http://localhost:8083/api/orders
Content-Type: application/json
{
"customerId": 1,
"customerEmail": "user@example.com",
"items": [
{ "productId": 101, "unitPrice": 100.00, "quantity": 1 }
]
}
- E-postayı doğrulama
- MailHog UI:
http://localhost:8025 - "Your order #... has been created" başlıklı e-postayı göreceksiniz.
-
Müşteri, e-ticaret uygulamasında indirim kodu girer. UI/Backend,
product-service’e HTTP isteği atar:- Endpoint:
POST /api/discounts/calculate - Gövde:
code,price,externalCategoryId
- Endpoint:
-
product-serviceiçindekiDiscountController, gelen DTO’yuDiscount.proto’ya uygunDiscountRequest’e çevirir. -
product-servicegRPC istemcisi,DiscountService/getDiscountRPC çağrısınıdiscount-service’e yapar:- Kanal:
localhost:9090(dev), prod’da servis DNS’i. - Çağrı tipi: blocking (senkron).
- Kanal:
-
discount-servicegRPC sunucusu isteği alır:externalCategoryIdile kendi DB’sindeCategorybulur.- İlgili kategoriye bağlı
Discountkaydını (kuponu)codeile arar. - Bulursa:
newPrice = price - discountPrice(0’ın altına düşmez). - Bulamazsa:
newPrice = price,response.statusCode=false,message="Invalid discount code".
-
discount-service,DiscountResponsedöner;product-serviceyanıtı alır ve HTTP için DTO’ya çevirir. -
product-serviceHTTP 200 ile sonuç döner:oldPrice,newPrice,code,response.statusCode,response.message.
- Bu akışta indirim “hesaplanır” ve sonuç döndürülür; ürün tablosundaki temel fiyat değiştirilmez.
- E-ticaret uygulamalarında indirim, genellikle sepette/checkout’ta geçici olarak uygulanır; sipariş fiyatı buna göre hesaplanır. Ürünün “liste fiyatı” sabit kalır.
- İhtiyaç varsa, sepet/checkout servisi bu yanıtı kullanarak satır kalemi tutarlarını günceller; kalıcı bir “order total” oluşturur.
- Kategori bulunamazsa:
discount-servicehata üretir (gRPC INTERNAL);product-servicebu hatayı HTTP 5xx olarak yüzeye yansıtabilir. - Geçersiz kupon: Başarılı HTTP/gRPC yanıtı gelir ama
statusCode=falsevenewPrice=oldPriceolur. - Bağlantı sorunu: gRPC kanalında hata →
product-servicetarafında exception fırlar (şu an özel handling yok).
- HTTP/REST:
product-service8081,order-service8083,notification-service8084. - gRPC:
discount-service9090. (Spring Boot 8082’de koşar.) - RabbitMQ: 5672 (AMQP), 15672 (UI).
- MailHog: 1025 (SMTP), 8025 (UI).
- Veritabanları:
product-service→ 54322,discount-service→ 54323,order-service→ 54324.
- gRPC
DiscountRequest:code(string),price(float),externalCategoryId(int64). - gRPC
DiscountResponse:code,oldPrice,newPrice,response{statusCode(bool), message(string)}.
- İstek (HTTP):
{"code":"SAVE10","price":100.0,"externalCategoryId":1}- gRPC yanıtı (içeriden):
{"code":"SAVE10","newPrice":90.0,"oldPrice":100.0,"response":{"statusCode":true,"message":"Discount applied successfully"}}- HTTP yanıtı (müşteriye):
{"code":"SAVE10","newPrice":90.0,"oldPrice":100.0,"response":{"statusCode":true,"message":"Discount applied successfully"}}-
Üst seviye akış:
- Client → product-service (HTTP) → DTO → gRPC
DiscountRequest→ - product-service → discount-service (gRPC) → DB sorguları → indirim hesaplama →
- discount-service → product-service (gRPC yanıt) → HTTP DTO →
- product-service → Client (HTTP yanıt).
- Client → product-service (HTTP) → DTO → gRPC
-
Bu mimaride indirim “uygulanmış fiyat” yanıt olarak döner; kalıcı fiyat değişikliği yapılmaz. Kalıcılaştırma gerekiyorsa, o iş mantığı sepet/checkout/sipariş servisinde yapılır.