# Kafka רב-שפתי — C# ו-Python על אותו Topic📖 שיעור

## מה זה?

בדיוק כמו ש-JSON (מיחידת ה-C# הבסיסית) לא תלוי בשפה — Kafka גם לא. שירות ה-Order (C#) יכול לפרסם ל-Topic, ושירות ה-Analytics (Python, השפה שכבר עבדתם איתה ביחידת ה-Pandas) יכול לצרוך ממנו, **בלי שאף אחד מהם יודע** באיזו שפה השני כתוב. זה בדיוק המודל שיהיה בשימוש בפרויקטים המסכמים בהמשך הקורס — צוותים שכותבים שירותים בשפות שונות, מחוברים דרך Kafka.

## מילות מפתח שחשוב לזכור

• Language-Agnostic Protocol — פרוטוקול שלא תלוי בשפת מימוש; Kafka (ו-JSON שבתוך ההודעות) הם כאלה

• `confluent-kafka` (Python) — הספרייה המקבילה ל-`Confluent.Kafka` (C#) לעבודה עם Kafka מ-Python

• Message Contract — ה"הסכם" על מבנה ההודעה (שדות ה-JSON) ששני הצדדים — בכל שפה — מכבדים

```csharp
// C# — Producer (from the previous lesson, unchanged)
var order = new { OrderId = 123, CustomerId = 5, Total = 250, Currency = "ILS" };
string json = JsonSerializer.Serialize(order);
await producer.ProduceAsync("order-created", new Message<Null, string> { Value = json });
```

```python
# Python — a second Consumer, on the exact same Topic
from confluent_kafka import Consumer
import json

consumer = Consumer({
    'bootstrap.servers': 'localhost:9092',
    'group.id': 'analytics-service',   # different Consumer Group from Inventory — it gets the message too
    'auto.offset.reset': 'earliest'
})
consumer.subscribe(['order-created'])

while True:
    msg = consumer.poll(1.0)
    if msg is None:
        continue
    order = json.loads(msg.value())   # the exact same JSON, decoded in Python
    print(f"Analytics: order {order['OrderId']} for a total of {order['Total']} {order['Currency']}")
```

## הסבר עיקרי

Message Contract הוא ה"חוזה" האמיתי, לא שפת המימוש — שני הצדדים (C# Producer, Python Consumer) לא צריכים לדעת כלום זה על זה מבחינת קוד — הם רק צריכים להסכים על **מבנה ה-JSON** (אילו שדות, אילו טיפוסים). זה בדיוק כמו `IRepository<T>` מיחידת ASP.NET Core — חוזה משותף, מימושים שונים לגמרי — רק שכאן ה"מימושים השונים" הם אפילו בשפות תכנות שונות.

Consumer Group שונה = גם מקבל את ההודעה — Consumer ה-Python נרשם עם `group.id` **שונה** (`analytics-service`, לא `inventory-service` מהדוגמה הקודמת) — זו הסיבה שהוא מקבל את **אותה** הודעה גם הוא, בנפרד מ-Inventory Service. כל Consumer Group "רואה" את זרם ההודעות בשלמותו, ללא תלות בקבוצות אחרות.

זו הכנה ישירה לפרויקט המסכם של הקורס — הפרויקטים הצוותיים בהמשך (Intelligence System, Data Pipeline) בנויים בדיוק על הדפוס הזה: שירותי C# ו-Python עצמאיים, מחוברים דרך Kafka, בלי תלות ישירה בקוד אחד בשני — רק בחוזה ה-JSON המשותף.

## יתרונות

Kafka מאפשר לצוותים לעבוד בשפות שונות במקביל, בלי לתאם קוד ביניהם; Message Contract (JSON) הוא נקודת התיאום היחידה הנדרשת; קל להוסיף Consumer נוסף (בכל שפה) בלי לגעת ב-Producer הקיים בכלל.

## חסרונות

בלי תיעוד ברור של ה-Message Contract, שינוי מבנה ב-JSON מצד אחד (הוספת/הסרת שדה) עלול לשבור צדדים אחרים בשקט; דיבוג בין-שפתי (מעקב אחרי הודעה מ-C# ל-Python) מורכב יותר מדיבוג בתוך שפה אחת.

## נקודות חשובות

• Kafka הוא Language-Agnostic — Producer/Consumer יכולים להיות בכל שפה עם ספריית לקוח מתאימה

• Message Contract (מבנה JSON) הוא נקודת התיאום, לא שפת המימוש

• Consumer Group שונה = מקבל את כל ההודעות בנפרד, גם אם קבוצה אחרת כבר "צרכה" אותן

• זהו הבסיס לארכיטקטורת מיקרוסרביסים רב-שפתית — נושא מרכזי בהמשך הקורס

## טעויות נפוצות

• לשנות שם/טיפוס שדה ב-JSON מצד ה-Producer בלי לתאם עם כל הConsumers שתלויים בו

• לשכוח שDeserialize ב-Python (`msg.value()` כ-bytes) דורש `.decode()`/`json.loads` מתאים, לא זהה לתחביר C#

• להניח ששני Consumers עם אותו group.id יקבלו את כל ההודעות כל אחד — הם מתחלקים, לא משוכפלים (כמו בשיעור הקודם)

## סיכום

Kafka הוא Language-Agnostic — הודעות עם JSON כMessage Contract מאפשרות ל-Producer ב-C# ו-Consumer ב-Python לתקשר דרך אותו Topic, בלי תלות ישירה בקוד אחד בשני. Consumer Group שונה מבטיח שכל שירות (בכל שפה) מקבל את מלוא זרם ההודעות בנפרד. זהו בדיוק המודל שישמש בפרויקטים הצוותיים בהמשך הקורס — שירותים בשפות שונות, מחוברים דרך Kafka וחוזה JSON משותף.

## דוקומנטציה רשמית

[Confluent — Python Client](https://docs.confluent.io/kafka-clients/python/current/overview.html)

---

## תרגילים

### תרגיל 1 — Consumer שני ב-Python

**המשימה:** קחו את ה-Producer מהשיעור הקודם (C#), וכתבו Consumer חדש בPython (עם `group.id` שונה) שמאזין לאותו Topic ומדפיס את ההודעות.

**בדיקה:** שליחת הודעה מה-Producer ב-C# מופיעה גם אצל ה-Consumer הקיים (C#, אם עדיין רץ) וגם אצל הConsumer החדש (Python) — שניהם מקבלים אותה במלואה.

### תרגיל 2 — שינוי Message Contract מתואם

**המשימה:** הוסיפו שדה חדש (`Region`) להודעת ה-JSON בProducer (C#), ועדכנו את שני הConsumers (C# ו-Python) לקרוא ולהדפיס אותו.

**בדיקה:** שני הConsumers מציגים את השדה החדש נכון אחרי העדכון התואם משני הצדדים.

---

## פרויקט מסכם

**המשימה:** הרחיבו את פרויקט ה-`task-created` (מהשיעור הקודם) עם Consumer שני בPython.

**דרישות:**
1. Consumer Python חדש (`group.id` נפרד) שמאזין ל-Topic `task-created` ומדפיס סטטיסטיקה רצה (כמה משימות נוצרו עד כה בהרצה הנוכחית)
2. תיעוד קצר (README) של ה-Message Contract — אילו שדות קיימים בהודעת JSON, ואילו טיפוסים
3. הרצה בו-זמנית של: ה-API (C#), הConsumer המקורי (C#), והConsumer החדש (Python) — כל יצירת משימה מעדכנת את כולם

**בדיקה:** יצירת 3 משימות ברצף מעדכנת את המונה בConsumer הPython ל-3, במקביל להתנהגות הConsumer המקורי ב-C#, בלי שום תלות קוד ישירה בין השניים.

## מה בפרק הבא

בפרק הבא נעבור ליחידה חדשה — **Elasticsearch ו-Kibana**: מנוע חיפוש שמריצים בקונטיינר בדיוק כמו MongoDB ו-Kafka, ומאפשר חיפוש טקסט חופשי מהיר וניתוח סטטיסטי של הנתונים.
