diff --git a/docs/INDEX.md b/docs/INDEX.md index fb3e918..e8a2f99 100644 --- a/docs/INDEX.md +++ b/docs/INDEX.md @@ -4,6 +4,7 @@ **Quick Navigation:** - [Core Concepts](#core-concepts) - System architecture and patterns +- [Design Principles](#design-principles) - Event Storm & Bidirectional References - [Step Development](#step-development-best-practices) - How to create new steps - [Services](#service-layer-patterns) - Reusable business logic - [Integrations](#external-integrations) - xAI, EspoCRM, Advoware @@ -47,6 +48,96 @@ Webhook (HTTP) → Queue Event → Event Handler → External APIs --- +## Design Principles + +### Event Storm over Lock Coordination + +**Core Philosophy: "Idempotent Chaos - Check Cheap, Sync Once"** + +``` +╔═══════════════════════════════════════════════════════╗ +║ Prefer: ║ +║ ✅ Multiple redundant events over coordination ║ +║ ✅ Idempotent handlers over distributed locks ║ +║ ✅ Eventually consistent over perfectly synced ║ +║ ✅ Simple duplication over complex orchestration ║ +╚═══════════════════════════════════════════════════════╝ +``` + +**Guideline:** +- Fire events liberally. 10 redundant events are cheaper than 1 complex lock coordination. +- Make every handler idempotent with early returns. +- Accept that events may trigger multiple times - handlers must be robust. +- Use locks only for expensive operations (file uploads, rate-limited APIs). + +**Example: Entity Link Event** +``` +User links Document ↔ Räumungsklage + +Webhooks fire: +├─ POST /vmh/webhook/entity/link +└─ Emits: raeumungsklage.update, cdokumente.update + +Handlers (parallel): +├─ Räumungsklage Handler +│ ├─ Creates xAI Collection (if missing) +│ └─ Fires: cdokumente.update (for all linked docs) +│ +└─ Document Handler (may run 2-3x) + ├─ Check: Already synced? → Return early (cheap!) + ├─ Check: Collection ready? → No? Return, retry later + └─ Sync: Upload to xAI + add to collections +``` + +**Result:** Overhead through multiple checks << Complexity of coordination. + +### Bidirectional Reference Pattern + +**Principle: Every sync maintains references on both sides** + +**EspoCRM as Central Hub:** +``` +┌─────────────┐ ┌──────────────┐ ┌─────────────┐ +│ Advoware │◄────────┤ EspoCRM ├────────►│ xAI │ +│ │ │ │ │ │ +│ Akte 12345 │ │ Entity │ │ Collection │ +└─────────────┘ │ - advowareId │ └─────────────┘ + │ - xaiColId │ + └──────────────┘ +``` + +**Implementation:** +- **xAI Collection** → stores `entityType` + `entityId` in metadata +- **EspoCRM Entity** → stores `xaiCollectionId` field +- **EspoCRM Document** → stores `xaiFileId` + `xaiCollections[]` fields +- **Advoware Integration** → stores `advowareAkteId` in EspoCRM + +**Benefits:** +- Bidirectional navigation without complex queries +- Easy relationship building (e.g., Advoware Akte ↔ xAI Collection via EspoCRM) +- Idempotent lookups (can verify both directions) +- Debugging: Always know where things came from + +**Example: Collection Creation** +```python +# xAI Collection +collection = await xai.create_collection( + name="CBeteiligte - Max Mustermann", + metadata={ + "espocrm_entity_type": "CBeteiligte", + "espocrm_entity_id": "abc123", + "created_at": "2026-03-08T19:00:00Z" + } +) + +# EspoCRM Entity +await espocrm.update_entity("CBeteiligte", "abc123", { + "xaiCollectionId": collection.id +}) +``` + +--- + ## Step Development Best Practices ### File Naming Convention