# Zero PII Kundenintelligenz Teil 2:® Profile Signale Segmente

<!--category-- Product, Privacy, Segmentation, C# -->
<datetime class="hidden">2025-12-31T20:00</datetime>

Im [Teil](/blog/zero-pii-customer-intelligence-part1) wir deckten die Philosophie und die Serie Übersicht [Teil](/blog/zero-pii-customer-intelligence-part1-1) Wir bauten die Probendatengenerator .. .

Jetzt lassen Sie let-' s bauen das Kernsystem. Dieses Teil konzentriert sich auf...

1. **Zero---PII-Profilarchitektur** Ephemerale Sitzungen gegen persistente Profile
2. **Signale und Gewichte** Was wir verfolgen und wie es sich ansammelt
3. **Segmentdefinitionen** Fuzzy-Mitgliedschaft mit gewichteten Regeln Das Hauptereignis
4. **Voransicht des Outbox-Musters** Verlässliche Veröffentlichung von Ereignissen

Das ist die **Beispielprojekt** (`Mostlylucid.SegmentCommerce`Eine kleine E-Commerce-Demo, die das Kernmuster zeigt, ., es wird absichtlich vereinfacht, um Konzepte klar zu demonstrieren ,, aber es enthält auch reale Infrastruktur-Muster (outbox .- based Message Handling . , Hintergrund Job Processing . ,, JSONB Indexierung .), dass Sie ' skalieren horizontal in der Produktion .

Betrachten Sie dies als "Produktionsmuster in einem einzigen -app-Formfaktor. Die gleiche Codestruktur funktioniert, ob Sie einen Prozess ausführen oder über Dienstleistungen verteilen möchten.

[TOC]

## Technologiewahl

Wir wählten eine **Einfacher, zusammenhängender Stack** um die Probe klar zu halten, während sie Produktionsmuster zeigt

### PostgreSQL Eine Datenbank

Anstelle von separaten Vektordatenbanken verwenden wir Nachrichtenqueues und Caching Layers. **PostgreSQL**:

```mermaid
flowchart LR
    App[ASP.NET Core] --> PG[(PostgreSQL)]
    
    PG --> JSONB[JSONB<br/>interests]
    PG --> Vector[pgvector<br/>embeddings]
    PG --> Queue[Queue tables<br/>jobs, outbox]
    PG --> FTS[Full-text<br/>tsvector]
    
    style PG stroke:#2f9e44,stroke-width:3px
    style JSONB stroke:#1971c2,stroke-width:2px
    style Vector stroke:#1971c2,stroke-width:2px
    style Queue stroke:#1971c2,stroke-width:2px
    style FTS stroke:#1971c2,stroke-width:2px
```

- **JSONB** für flexible Schemata
- **pgvector** für die Einbettung von (,no Qdrant-/,Pinecone-),
- **LEBENSMITTEL** für Jobs .( .no Redis . / .RabbitMQ .) .
- **tsvector** für Volltext---Text - Elastische Suche - )-

Eine Verbindung Pool-Verbindungspool-Verbindung 1 Backup-Verbindung

### HTMX-Datenverarbeitungsgeräte für Alpine-., :, Server, UI

```html
<!-- Instant search (no page reload) -->
<input 
    hx-get="/api/search" 
    hx-trigger="keyup changed delay:300ms" 
    hx-target="#results" />
```

- **HTMX**Server gibt Teilwerte wieder ,( ,no JSON ,→ ,templatierend ,)
- **Alpen-.-js**Reaktivität ohne Bauschritt
- **Fortschrittliche Verbesserung**Funktioniert ohne JS.,, besser mit ihm

SPA---ähnliche UX mit Server---Rendering Einfachheit

### ASP-.-NET-Kern ,:-Verteilbare Muster

Einzel-App, aber Musterskala, :

- **Auslauf** Auswechseln für Nachrichtenbus
- **Job-Warteschlange** ](-LISTEN-/-NOTIFY-)-Swap für Arbeitnehmerpool
- **Sitzungssammler** Swap für separate API

Einfach starten - .-Verteilen, wenn erforderlich -.-

### Was wir vermieden haben

- Keine separate Vektordatenbank ist kolodiert.
- Kein JS-Framework (-HTMX +-Alpine ist einfacher
- Noch keine Mikroservices arbeiten in Monolith oder verteilt
- Keine Docker-Kompositausbreitung

## Die Zero---PII-Herausforderung

Traditionelles Benutzer-Tracking speichert identifizierbare Informationen.:-Namen.,-E-Mails.MS K2-Benutzer-IDs, die mit dem Verhalten verbunden sind..-DSGVO und Datenschutzbestimmungen machen diese zunehmend problematischer.

**Wir speichern Verhaltensmuster... nicht Identitäten...**

```mermaid
flowchart LR
    subgraph "Traditional Approach"
        User1[User: john@email.com] --> Behavior1[Bought headphones]
        Behavior1 --> PII1[(PII Database)]
    end
    
    subgraph "Zero-PII Approach"
        User2[Anonymous Session] --> Behavior2[Bought headphones]
        Behavior2 --> Pattern[(Category: tech<br/>Signal weight: 1.0)]
    end
    
    style PII1 stroke:#c92a2a,stroke-width:3px
    style Pattern stroke:#2f9e44,stroke-width:3px
```

Die wichtigsten Erkenntnisse **du musst nicht wissen, wer jemand ist zu wissen, WAS sie interessiert?**.

## Architektur im Überblick

Das System hat drei Kernschichten.

```mermaid
flowchart TB
    Browser[Browser] --> Session[Session Collector]
    Session --> Profile[Persistent Profile]
    Profile --> Segments[Segment Service]
    
    Session -->|in-memory only| Cache[(IMemoryCache)]
    Profile -->|elevated signals| DB[(PostgreSQL + JSONB)]
    Segments -->|memberships| UI[Segment Explorer UI]
    
    style Session stroke:#1971c2,stroke-width:3px
    style Profile stroke:#2f9e44,stroke-width:3px
    style Segments stroke:#fab005,stroke-width:3px
    style Cache stroke:#e64980,stroke-width:2px
```

1. **Sitzungssammler** --Erfasst Verhaltenssignale (-Ansichten,-Klicks,-Warenkorb fügt hinzu)---Aufnahmen **Nur in Erinnerung**
2. **Persistentes Profil** -, erhöht hohe Wertesignale ,, berechnet Interessen **Datenbank ,- , unterstützt**
3. **Segmentdienst** Bewertet Profile gegen RegelnBewertet Mitgliedschaften

**Kritische Unterscheidung**Die Sitzungen sind ephemer, .. Profile sind persistent,.. Dies ist kein Implementierungs-Detail.-.it' ist eine Entscheidung für die Privatsphäre-Architektur.

## Sitzungsprofile Strict In--Memory-Cache von (LFU)

Sitzungen sind **ausschließlich in der --Speicher**Sie leben in `IMemoryCache` mit gleitendem Ablauf und **nie die Datenbank berühren**.

Dies ist ein harter architektonischer Constraint.:-Session-Daten können nicht persistent werden..-Es sammelt Signale während eines Besuchs und elikt nach Minuten der Inaktivität über LFU.MSC3-Least Häufig verwendete Cache-Richtlinien unter Speicherdruck.

### Sitzungsprofil - Das In-Memory-Modell

```csharp
// Mostlylucid.SegmentCommerce/Models/SessionProfile.cs
public class SessionProfile
{
    public string SessionKey { get; set; } = string.Empty;

    // Category interest scores: { "tech": 0.75, "fashion": 0.25 }
    public Dictionary<string, double> Interests { get; set; } = new();

    // Detailed signal counts: { "tech": { "product_view": 5, "add_to_cart": 1 } }
    public Dictionary<string, Dictionary<string, int>> Signals { get; set; } = new();

    // Products viewed this session
    public List<int> ViewedProducts { get; set; } = new();

    // Session context (device, referrer domain, time-of-day)
    public SessionContext? Context { get; set; }

    // Aggregates
    public double TotalWeight { get; set; }
    public int SignalCount { get; set; }
    public int PageViews { get; set; }
    public int ProductViews { get; set; }
    public int CartAdds { get; set; }

    // Timestamps
    public DateTime StartedAt { get; set; } = DateTime.UtcNow;
    public DateTime LastActivityAt { get; set; } = DateTime.UtcNow;

    // Link to persistent profile (if fingerprint resolved)
    public Guid? PersistentProfileId { get; set; }
}
```

### Gespeichert in IMemoryCache oder IDistributedCache mit Sliding Expiration

```csharp
// Mostlylucid.SegmentCommerce/Services/Profiles/SessionCollector.cs
_cache.Set(sessionKey, sessionProfile, new MemoryCacheEntryOptions
{
    SlidingExpiration = TimeSpan.FromMinutes(30),
    Priority = CacheItemPriority.Normal, // LFU eviction under memory pressure
    
    // CRITICAL: Eviction callback decides whether to elevate to persistent profile
    PostEvictionCallbacks =
    {
        new PostEvictionCallbackRegistration
        {
            EvictionCallback = async (key, value, reason, state) =>
            {
                if (value is SessionProfile session && ShouldElevate(session))
                {
                    // Only NOW do we write to database (persistent profile)
                    await ElevateToProfileAsync(session);
                }
                // Otherwise: session is gone forever
            }
        }
    }
});
```

**Starke Einschränkungen**

- **Keine versehentliche Persistenz**Live-Sitzungen **Nur** im Cache -(-IMemoryCache oder IDistributedCache wie Redis-)-
- **LFU-Vertreibung**Unter dem Druck des Gedächtnisses zuerst die Sitzungen evakuieren
- **Ablauf des Gleitvorgangs**Minuten vor der letzten Aktivität
- **Räumungsaufruf**Letzte Chance, hohe - Wertesignale zu erhöhen bevor sie verloren gehen
- **Keine Wiedereinziehung**Die App startet alle Sitzungen neu, es sei denn, sie verwendet IDistributedCache, ,, aber immer noch ephemeral.

### Elevationsentscheidung über die Räumung

```csharp
private bool ShouldElevate(SessionProfile session)
{
    // Elevate if:
    // - User added to cart (high intent)
    // - User completed purchase (conversion)
    // - Fingerprint was resolved (identity established)
    // - Session weight exceeds threshold (engaged visitor)
    
    return session.CartAdds > 0 
        || session.TotalWeight > 5.0 
        || session.PersistentProfileId.HasValue;
}
```

**Warum die Räumung zurückruft**

Das ist die **Nur** Sicherer Punkt, um Persistenz zu entscheiden.

1. Wir kennen den gesamten Sitzungsverlauf
2. Wir können das Gesamtengagement bewerten
3. Wir vermeiden die Speicherung von Low---Wert-Sitzungen
4. Wir garantieren, dass die Sitzungen nicht versehentlich fortbestehen

Wenn wir nicht während der Räumung erhöhen, ist die Sitzung **fort für immer**Dies ist das Design.

### SitzungKontext, was wir verfolgen

```csharp
public class SessionContext
{
    public string? DeviceType { get; set; }        // "mobile", "desktop"
    public string? EntryPath { get; set; }         // "/products/tech" (no query params)
    public string? ReferrerDomain { get; set; }    // "google.com" (domain only, not full URL)
    public string? TimeOfDay { get; set; }         // "morning", "afternoon"
    public string? DayType { get; set; }           // "weekday", "weekend"
}
```

Beachten Sie was, ' **nicht** Hier werden die IP-Adressen der , user agents, volle URLs, Messpixel der, tracking erfasst **Kontextmuster**Nicht identifizierbare Informationen

## Was sind Signale?

A **Signal** ist ein Verhalten Tatsache während einer Benutzer-Action erfasst, statt Tracking-"hessen, die ,"h wir verfolgen, was passierte, ."h

Dieses Konzept kommt von [ephemere Signale](/blog/ephemeral-signals)- Lived Fakten, die in einem begrenzten Fenster existieren und aus natürlichem Alter .. . In diesem System ., Operationen senden Signale wie `"api.rate_limited"` oder `"gateway.slow"` Verhalten ohne enge Kopplung zu koordinieren

Hier wenden wir das gleiche Muster auf das Benutzerverhalten an.

- **Produktansicht** → `product_view` Signal Gewicht :
- **In den Warenkorb legen** → `add_to_cart` Signal Gewicht :
- **Kauf** → `purchase` Signal Gewicht :

Signale sind **Vergänglich** Auslöschen mit der Sitzung **Null---PII** Nichtzutreffendes streichen. **gewichtet** (-Intent--Bewusst

### Kunden-Fingerabdruck-Methoden zur Identifizierung von Cookies

Um Sitzungen zu verlinken, ohne Cookies zu verfolgen, verwenden wir **client--'side Fingerabdruck**Der Browser berechnet einen Hash aus Signalen , ( timezone ,, , Bildschirmauflösung , , , WebGL Renderer, , , Canvas-Fingerabdruck , und sendet **nur der Hash** zu `/api/fingerprint`.

Der Server dann **HMACs, die Hasch** mit einem geheimen Schlüssel, der es an anderer Stelle unbrauchbar macht

Dieser Code ist von meinem [Bot-Detektionsprojekt](/blog/botdetection-introduction),, wo es verwendet wird, um Schaber zu identifizieren; ., gleiche Technik; ,, verschiedene Zwecke; MSC4,

```javascript
// Mostlylucid.SegmentCommerce/ClientFingerprint/fingerprint.js
// Collect signals (browser capabilities, not PII)
var signals = [
    Intl.DateTimeFormat().resolvedOptions().timeZone,
    navigator.language,
    screen.width + 'x' + screen.height,
    // ... (see full code)
];

// Hash locally
var hash = hash(signals.join('|'));

// Send only the hash via sendBeacon
navigator.sendBeacon('/api/fingerprint', JSON.stringify({ h: hash }));
```

**Server---Seite:-**

```csharp
// Server HMACs the client hash with a secret key
var profileKey = HMACSHA256(clientHash + secretKey);
```

Jetzt haben wir einen stabilen usersite-Bezeichner ohne Cookies oder lokaleStorage. [Vollständige Fingerabdruckquelle](https://github.com/scottgal/mostlylucidweb/blob/main/Mostlylucid.SegmentCommerce/ClientFingerprint/fingerprint.js) Angepasst aus meistluzider ., Botdetektion, ).

## Signaltypen und Gewichte

Verschiedene Aktionen haben unterschiedliche Intent-Levels. **Grundgewichte**.

### SignalTypen: Die Gewichtshierarchie

```csharp
// Mostlylucid.SegmentCommerce/Data/Entities/Profiles/SignalEntity.cs
public static class SignalTypes
{
    // Passive signals (low intent)
    public const string PageView = "page_view";              // 0.01
    public const string CategoryBrowse = "category_browse";  // 0.03
    public const string ProductImpression = "product_impression"; // 0.02

    // Active signals (medium intent)
    public const string ProductView = "product_view";        // 0.10
    public const string ProductClick = "product_click";      // 0.08
    public const string Search = "search";                   // 0.05

    // High-intent signals
    public const string AddToCart = "add_to_cart";           // 0.35
    public const string AddToWishlist = "add_to_wishlist";   // 0.25
    public const string ViewCart = "view_cart";              // 0.15
    public const string BeginCheckout = "begin_checkout";    // 0.40

    // Conversion signals (highest intent)
    public const string Purchase = "purchase";               // 1.00
    public const string Review = "review";                   // 0.60
    public const string Share = "share";                     // 0.50

    public static readonly Dictionary<string, double> BaseWeights = new()
    {
        { PageView, 0.01 },
        { ProductView, 0.10 },
        { AddToCart, 0.35 },
        { Purchase, 1.00 },
        // ... (see full code for complete list)
    };

    public static double GetBaseWeight(string signalType)
    {
        return BaseWeights.GetValueOrDefault(signalType, 0.05);
    }
}
```

**Warum das so wichtig ist**

- Eine einzelne Seitenansicht`0.01`Das Signal wird beherrscht
- In den Warenkorb legen`0.35`Ein starkes Intent-Signal
- Kauf von (`1.00`Das Signal ist das stärkste.

Diese Hierarchie verhindert, dass das Profil verschmutzt wird.

## Sitzungssammler: Recording-Signale ( Editieren- Nur )

```csharp
// Mostlylucid.SegmentCommerce/Services/Profiles/SessionCollector.cs
public async Task<SessionProfile> RecordSignalAsync(
    SessionSignalInput input, CancellationToken ct = default)
{
    var sessionKey = input.SessionKey;
    
    // Get or create session FROM CACHE (never DB)
    var session = _cache.Get<SessionProfile>(sessionKey);
    
    if (session == null)
    {
        session = new SessionProfile
        {
            SessionKey = sessionKey,
            StartedAt = DateTime.UtcNow
        };
    }

    session.LastActivityAt = DateTime.UtcNow;

    var weight = input.Weight ?? SignalTypes.GetBaseWeight(input.SignalType);

    // Update in-memory aggregates
    session.TotalWeight += weight;
    session.SignalCount++;

    if (!string.IsNullOrEmpty(input.Category))
    {
        session.Interests.TryGetValue(input.Category, out var currentScore);
        session.Interests[input.Category] = currentScore + weight;
    }

    if (input.SignalType == SignalTypes.AddToCart)
    {
        session.CartAdds++;
    }

    // Put back in cache with sliding expiration
    _cache.Set(sessionKey, session, new MemoryCacheEntryOptions
    {
        SlidingExpiration = TimeSpan.FromMinutes(30),
        Priority = CacheItemPriority.Normal,
        PostEvictionCallbacks = { /* elevation callback */ }
    });

    return session;
}
```

**Schnell, weil**

- Rein in: -, Memory, keine DB schreibt:),
- Kein Serialisierungsüberkopf
- Keine Netzwerk-Aufrufe Lokaler Cache

## Persistente Profile, erhöhte Signale

Wenn eine Sitzung zeigt, hohe Intent-(-cart fügt hinzu: ,, Käufe, ),, wir erhöhen Signale zu einem **Persistentes Profil**.

### PersistentProfileEntity-ProfileDas lange Profil von -

```csharp
// Mostlylucid.SegmentCommerce/Data/Entities/Profiles/PersistentProfileEntity.cs
[Table("persistent_profiles")]
public class PersistentProfileEntity
{
    [Key]
    public Guid Id { get; set; } = Guid.NewGuid();

    [Required]
    [MaxLength(256)]
    public string ProfileKey { get; set; } = string.Empty;

    // How this profile is identified (Fingerprint, Cookie, Identity)
    public ProfileIdentificationMode IdentificationMode { get; set; }

    // Behavioral data (all JSONB)
    [Column("interests", TypeName = "jsonb")]
    public Dictionary<string, double> Interests { get; set; } = new();

    [Column("affinities", TypeName = "jsonb")]
    public Dictionary<string, double> Affinities { get; set; } = new();

    [Column("brand_affinities", TypeName = "jsonb")]
    public Dictionary<string, double> BrandAffinities { get; set; } = new();

    [Column("price_preferences", TypeName = "jsonb")]
    public PricePreferences? PricePreferences { get; set; }

    [Column("traits", TypeName = "jsonb")]
    public Dictionary<string, bool> Traits { get; set; } = new();

    // Computed segments
    public ProfileSegments Segments { get; set; } = ProfileSegments.None;

    [Column("llm_segments", TypeName = "jsonb")]
    public Dictionary<string, double>? LlmSegments { get; set; }

    // Vector embedding for similarity matching
    [Column("embedding", TypeName = "vector(384)")]
    public Vector? Embedding { get; set; }

    // Statistics
    public int TotalSessions { get; set; }
    public int TotalSignals { get; set; }
    public int TotalPurchases { get; set; }
    public int TotalCartAdds { get; set; }

    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
    public DateTime LastSeenAt { get; set; } = DateTime.UtcNow;
    public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
}
```

**Immer noch Null PII**:

- `ProfileKey` ist ein HMAC-Hash, nicht reversibel
- `IdentificationMode` erklärt uns, wie es identifiziert wurde.
- Alle Daten sind **Verhaltenssignale**Nicht personenbezogene Daten

### Erhöhte Anzahl der Sitzungen (MSK)

```csharp
public async Task ElevateToProfileAsync(
    SessionProfileEntity session, 
    PersistentProfileEntity profile, 
    CancellationToken ct = default)
{
    if (session.IsElevated)
        return;

    // Merge interests (use higher value)
    foreach (var (category, score) in session.Interests)
    {
        if (!profile.Interests.ContainsKey(category) || 
            profile.Interests[category] < score)
        {
            profile.Interests[category] = score;
        }
    }

    // Update stats
    profile.TotalSessions++;
    profile.TotalSignals += session.SignalCount;
    profile.TotalCartAdds += session.CartAdds;
    profile.LastSeenAt = DateTime.UtcNow;
    profile.UpdatedAt = DateTime.UtcNow;

    // Mark session as elevated
    session.IsElevated = true;
    session.PersistentProfileId = profile.Id;

    // Clear segment cache (will be recomputed)
    profile.SegmentsComputedAt = null;
    profile.EmbeddingComputedAt = null;

    await _db.SaveChangesAsync(ct);
}
```

**Wenn eine Erhöhung eintritt**

- Nach einem Warenkorb fügen Sie hinzu:
- Nach einem Kauf
- Wenn Benutzer sich für Persistenz entscheidet

## Segmentdefinitionen – Die Ausschüttung

Die Segmente für zwei Teile sind die **Durchführbare Ausgabe** von allen, die Signalsammlung----sie antworten, was für eine Art Shopper ist diese-MSC2-

### Warum Fuzzy Mitgliedschaft?

Traditionelle Segmentierung ist binär, :-you-'-yout entweder im Segment oder youou-'-iout nicht.-iOut Dies erzeugt Probleme

```mermaid
flowchart LR
    subgraph "Binary Segmentation"
        Profile1[3 purchases] -->|"Threshold: 5"| Out[NOT High-Value]
        Profile2[5 purchases] -->|"Threshold: 5"| In[High-Value]
    end
```

Ein Kunde mit Käufen von 4 wird identisch mit einem mit 0. behandelt, dass ' falsch ist.

**Fuzzy Segmentierung** gibt jedem Profil eine Punktzahl

Käufe Binärer |, Fuzzy Score
|-----------|--------|-------------|
Nicht verfügbar bei:
Nicht verfügbar bei:
Nicht verfügbar bei:
,| , 5+, |, Ja ,| , 1.0 , | ,

Jetzt können Sie proportional personalisieren ,: , , " , fast hoch , - , Wert , " , Kunden erhalten etwas andere Behandlung als ," , Nirgendwo in der Nähe

### Die Segmentdefinitionsstruktur

```csharp
// Mostlylucid.SegmentCommerce/Services/Segments/SegmentDefinition.cs
public class SegmentDefinition
{
    public string Id { get; set; }           // "tech-enthusiast"
    public string Name { get; set; }          // "Tech Enthusiasts"
    public string Description { get; set; }   // "Users with strong interest in technology"
    public string Icon { get; set; }          // "🔧"
    public string Color { get; set; }         // "#3b82f6"

    public List<SegmentRule> Rules { get; set; } = [];

    // How rules combine: All (AND), Any (OR), Weighted (sum)
    public RuleCombination Combination { get; set; } = RuleCombination.Weighted;

    // Minimum score to be "a member" (0-1)
    public double MembershipThreshold { get; set; } = 0.3;

    public List<string> Tags { get; set; } = [];  // For filtering/grouping
}
```

### Regel-Typen, die Sie überprüfen können

Jede Regel wertet eine Dimension des Profils aus

```csharp
public enum RuleType
{
    CategoryInterest,  // Check interests.tech, interests.fashion, etc.
    BrandAffinity,     // Check brandAffinities.Sony, brandAffinities.Nike, etc.
    PriceRange,        // Check price preferences (budget vs luxury)
    Trait,             // Check boolean traits (prefersDeals, browsesExtensively)
    Statistic,         // Check totalPurchases, totalSessions, totalCartAdds
    TagAffinity,       // Check affinities.gadgets, affinities.organic, etc.
    Recency,           // Check days since last activity
    Expression         // Custom expressions (advanced)
}
```

### Regeloperatoren

```csharp
public enum RuleOperator
{
    GreaterThan,       // value > threshold
    GreaterOrEqual,    // value >= threshold
    LessThan,          // value < threshold
    LessOrEqual,       // value <= threshold
    Equal,             // value == threshold
    NotEqual,          // value != threshold
    Contains,          // for array/string checks
    Between,           // for ranges
    In, NotIn          // for set membership
}
```

### Regelkombinationsmethoden

Wie sich mehrere Regeln zu einem Endergebnis zusammensetzen

```csharp
public enum RuleCombination
{
    All,      // AND logic: Score = min(all rule scores). All rules must pass.
    Any,      // OR logic: Score = max(all rule scores). Any rule can pass.
    Weighted  // Weighted sum: Score = Σ(rule.weight × rule.score) / Σ(rule.weight)
}
```

**Gewichtet** ist am häufigsten, lässt Sie sagen, " category interest matters 60%, recency mattings 30%, brand affinity matters

### Reale Segmentbeispiele

Hier sind die Standard-Segmente im Beispielprojekt.

#### Tech-Enthusiasten

```csharp
new SegmentDefinition
{
    Id = "tech-enthusiast",
    Name = "Tech Enthusiasts",
    Description = "Users with strong interest in technology products",
    Icon = "🔧",
    Color = "#3b82f6",
    MembershipThreshold = 0.35,
    Rules =
    [
        new() { 
            Type = RuleType.CategoryInterest, 
            Field = "interests.tech", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.4, 
            Weight = 0.6,  // 60% of score
            Description = "Tech interest > 40%" 
        },
        new() { 
            Type = RuleType.TagAffinity, 
            Field = "affinities.gadgets", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.2, 
            Weight = 0.2,  // 20% of score
            Description = "Likes gadgets" 
        },
        new() { 
            Type = RuleType.TagAffinity, 
            Field = "affinities.electronics", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 0.2, 
            Weight = 0.2,  // 20% of score
            Description = "Likes electronics" 
        }
    ]
}
```

**Beispiel für die Bewertung**

- Profil hat `interests.tech = 0.72`, `affinities.gadgets = 0.31`, `affinities.electronics = 0.15`
- Regel 1:0.72>= -0.4- →Score 1.0- - (Überschreitungsschwelle
- Regel 2:0.31>= -0.2- →Score 1.0- - (Überschreitungsschwelle
- Regel-3: 0.15 <- - 0.2 , →-Score 0.75, (-partial-:
- Schlussbemerkung **0.95**
- MSK-Schwellenwert **Mitglied mit Vertrauen**

#### Abtrünnige Wagen

```csharp
new SegmentDefinition
{
    Id = "cart-abandoner",
    Name = "Cart Abandoners",
    Description = "Users who add items to cart but don't complete purchase",
    Icon = "🛒",
    Color = "#ef4444",
    MembershipThreshold = 0.4,
    Rules =
    [
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalCartAdds", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 3, 
            Weight = 0.5, 
            Description = "3+ cart adds" 
        },
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalPurchases", 
            Operator = RuleOperator.LessThan, 
            Value = 2, 
            Weight = 0.5, 
            Description = "Few purchases" 
        }
    ]
}
```

Dieses fängt die -"...adds zum Warenkorb, aber tut es nicht kaufen -"... pattern -mesK3... perfekt für Wiederherstellungskampagnen...

#### Kunden mit hohem Wert

```csharp
new SegmentDefinition
{
    Id = "high-value",
    Name = "High-Value Customers",
    Description = "Customers who make frequent purchases and spend above average",
    Icon = "💎",
    Color = "#8b5cf6",
    MembershipThreshold = 0.4,
    Rules =
    [
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalPurchases", 
            Operator = RuleOperator.GreaterOrEqual, 
            Value = 3, 
            Weight = 0.4, 
            Description = "3+ purchases" 
        },
        new() { 
            Type = RuleType.PriceRange, 
            Field = "priceRange", 
            Value = "100-10000",  // High-end shoppers
            Weight = 0.3, 
            Description = "High price range" 
        },
        new() { 
            Type = RuleType.Recency, 
            Field = "lastSeen", 
            Operator = RuleOperator.LessThan, 
            Value = 30, 
            Weight = 0.3, 
            Description = "Active in last 30 days" 
        }
    ]
}
```

#### Jäger von Schnäppchen

```csharp
new SegmentDefinition
{
    Id = "bargain-hunter",
    Name = "Bargain Hunters",
    Description = "Price-sensitive shoppers who love deals and discounts",
    Icon = "🏷️",
    Color = "#22c55e",
    MembershipThreshold = 0.3,
    Rules =
    [
        new() { 
            Type = RuleType.PriceRange, 
            Field = "priceRange", 
            Value = "0-75",  // Budget shoppers
            Weight = 0.5, 
            Description = "Low price preference" 
        },
        new() { 
            Type = RuleType.Trait, 
            Field = "traits.prefersDeals", 
            Value = true, 
            Weight = 0.3, 
            Description = "Prefers deals" 
        },
        new() { 
            Type = RuleType.Statistic, 
            Field = "totalCartAdds", 
            Operator = RuleOperator.GreaterThan, 
            Value = 5, 
            Weight = 0.2, 
            Description = "Shops around" 
        }
    ]
}
```

### Alle Standardsegmente

Die Stichprobe umfasst Segmente, die allgemeine e-- commerce-Muster abdecken.

Abschnitt „|" Symbol Hauptregeln Anwendungsfall
|---------|------|-----------|----------|
Kunden mit hohem Wert , | , 💎 ,| , 3+ , Käufe von , , hohe Ausgaben , vor kurzem ,| , VIP-Behandlung , CRMK9 , Treueprogramme , | ,
Technologische Enthusiasten – | – MSC2 – MSV3 – MKK4 – Gadget-Affinität – MKS5 – Technische Produktempfehlungen – MSR6 –
Mode nach vorn – | – 👗 – | – Mode-Interessen –, – mehrfache Besuche – | – Style-Empfehlungen – | –
Verhandeln Jäger Vergünstigungen Vergünstigung Angebote Vergünstigungs-Meldungen Verbilligte Preisspanne
Neue Besucher |, neue Besucher |, neue Mitglieder ≤2, keine Einkäufe |, Onboarding ,, erste Angebote -, Kauf
Warenkorb Abtrünnige Waren , | , 🛒 ,| , 3+ , Warenkorb fügt hinzu ,, , wenige Einkäufe , E| , Wiederherstellung von E-Mails ,
Home-Enthusiasten Home-Begeisterte Home-Produkt cross--, verkaufen
Sportliches Interesse an der Gesundheit von ,e Eigenschaften des Fitness-Produktfokus
Loyal Customers Loyale Kunden – |, ⭐, |, MSC4, Käufe ,, 10+, Sitzungen – Retention – MSC8, Belohnungen |,
Hochsignale , surft extensive Vergleichswerkzeuge , Detaillierte Informationen |

## SegmentService®:-Computing Mitgliedschaften

Das `SegmentService` wertet Profile gegen alle Segmentregeln aus

```csharp
// Mostlylucid.SegmentCommerce/Services/Segments/SegmentService.cs
public SegmentMembership EvaluateSegment(ProfileData profile, SegmentDefinition segment)
{
    var ruleScores = new List<RuleScore>();
    
    foreach (var rule in segment.Rules)
    {
        var (score, actualValue) = EvaluateRule(profile, rule);
        ruleScores.Add(new RuleScore
        {
            RuleDescription = rule.Description,
            Score = score,
            Weight = rule.Weight,
            ActualValue = actualValue  // For transparency
        });
    }

    // Combine based on segment's combination method
    double finalScore = segment.Combination switch
    {
        RuleCombination.All => ruleScores.Min(r => r.Score),
        RuleCombination.Any => ruleScores.Max(r => r.Score),
        RuleCombination.Weighted => ComputeWeightedScore(ruleScores),
        _ => 0
    };

    return new SegmentMembership
    {
        SegmentId = segment.Id,
        SegmentName = segment.Name,
        Score = Math.Round(finalScore, 3),
        IsMember = finalScore >= segment.MembershipThreshold,
        RuleScores = ruleScores,
        Confidence = score switch  // Human-readable
        {
            >= 0.8 => "Very High",
            >= 0.6 => "High",
            >= 0.4 => "Medium",
            >= 0.2 => "Low",
            _ => "Very Low"
        }
    };
}
```

### Erklärbarkeit Built---In

Jedes Mitgliedsergebnis beinhaltet die **tatsächliche Werte** die zum Score geführt haben

```csharp
// What the UI receives:
{
    "segmentId": "tech-enthusiast",
    "segmentName": "Tech Enthusiasts",
    "score": 0.95,
    "isMember": true,
    "confidence": "Very High",
    "ruleScores": [
        { "description": "Tech interest > 40%", "score": 1.0, "actualValue": "0.72" },
        { "description": "Likes gadgets", "score": 1.0, "actualValue": "0.31" },
        { "description": "Likes electronics", "score": 0.75, "actualValue": "0.15" }
    ]
}
```

Benutzer können **sehen Sie genau, warum** there-'-Re in einem Segment.Dies ist entscheidend für Transparenz und GDPR-Compliance.

## Signalfluss (MSK)

Hier wird eine Produktansicht zu einer Segmentmitgliedschaft.

```mermaid
sequenceDiagram
    participant Browser
    participant Cache as SessionCache
    participant Outbox as Outbox
    participant Segment as SegmentService

    Browser->>Cache: Product view (category: "tech")
    Cache->>Cache: Update in-memory session
    Note over Cache: interests.tech += 0.10
    
    Browser->>Cache: Add to cart (high intent)
    Cache->>Outbox: Publish elevation event
    Outbox->>Outbox: Write to outbox table
    
    Note over Outbox: Background worker processes
    Outbox->>Segment: Elevate to PersistentProfile
    
    Segment->>Segment: Evaluate segment rules
    Note over Segment: interests.tech: 0.72 >= 0.40 ✓<br/>Score: 0.95, IsMember: true
    Segment-->>Browser: Segment memberships + explanations
```

## Das Outbox-Muster

Alle wichtigen Aktionen fließen durch die **outbox-Muster**Unser primärer Orchestrierungsmechanismus

```mermaid
flowchart LR
    Action[Cart Add] --> TX[Single Transaction]
    TX --> DB[(Save + Outbox)]
    DB --> Worker[Background Worker]
    Worker --> Route[Route to Handlers]
    
    style TX stroke:#2f9e44,stroke-width:3px
```

**Warum?** Geschäftsdaten und Ereignisse werden in einer einzigen Transaktion geschrieben..-Events können verloren gehen!.-Fälle versuchen automatisch mit exponentiellem Backoff

```csharp
// Every action publishes to outbox in the same transaction
await using var transaction = await _db.Database.BeginTransactionAsync(ct);

cart.Items.Add(new CartItem { ProductId = productId });
await _db.SaveChangesAsync(ct);

_outbox.Publish(OutboxEventTypes.ProductAddedToCart, new { ProductId = productId });
await _db.SaveChangesAsync(ct);

await transaction.CommitAsync(ct);
// Event is now guaranteed to be processed
```

**Teil** deckt die volle Outbox-Implementierung ab ,: , die Job Warteschlange , , , LISTEN , / , NOTIFY für sofortige Pickup , , , retry Logik und Skalierung Muster

## Was kommt als nächstes?

Dieser Teil umfasste ,:'

- **Zero---PII-Profilarchitektur** Vergängliche Sitzungen gegen persistente Profile
- **Segmentdefinitionen** mit fuzzy Mitgliedschaft und gewichteten Regeln
- **Der SegmentService** mit eingebauter ,-'in Erklärbarkeit

**Teil** geht tiefer ins :

- **Implementierung des Outbox-Musters** Zuverlässiges Event Publishing und der einzelne Router
- **Job-Warteschlange** mit PostgreSQL `SKIP LOCKED` für die verteilte Verarbeitung
- **LEBENSMITTEL** für sofortigen Job Pickup
- **Benutzeroberfläche und Transparenz** Das Armaturenbrett und der Segment-Explorer

---


*Segmente sind die durchführbare Ausgabe dieses Systems..-Sie antworten "-Welche Art von Shopper ist das??"- mit fuzzy Scores.,-nicht binären Eimern..-Und jeder Benutzer kann genau sehen, warum sie sich in einem Segment befinden.*