feat(docs): add Design Principles section with Event Storm and Bidirectional Reference patterns
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user