MeshWeaver lets you wire domain-specific business logic directly into reactive, chart-driven UI without any special framework scaffolding. Your calculation engine stays as plain C#; MeshWeaver handles the subscription plumbing and re-rendering.

This guide walks through a reinsurance Excess-of-Loss cession end to end. The same three-layer pattern — domain model, pure engine, reactive layout area — applies to any domain.

The complete working example lives at the @Cession node type, including source files, sample data, and a chart layout area:

For the full production implementation see the MeshWeaver.Reinsurance repository.


The Three-Layer Pattern

Every business rules module in MeshWeaver follows the same structure:

Layer What it contains Key trait
Domain Model Immutable records for cashflows, contracts, results Safe to use in reactive pipelines
Business Rules Pure C# functions with no side effects Easy to unit-test in isolation
Layout Areas IObservable<UiControl> views bound to workspace streams Re-render automatically when data changes

The layers are intentionally decoupled. The calculation engine has zero framework imports; the layout area knows nothing about persistence. Domain Model Immutable records Cashflow · Layer · Result Business Rules Pure static functions CessionEngine.CedeIntoLayer Layout Area IObservable<UiControl> CombineLatest → Chart typed data results Workspace streams feed typed data; reactive subscriptions propagate changes end-to-end

Three-layer pattern: immutable domain model feeds a pure calculation engine whose results are consumed by a reactive layout area.


1. Domain Model

Cashflows and layers are simple records. Immutability makes them safe to pass through reactive pipelines without defensive copies.

/// <summary>
/// A single claim cashflow with a gross amount.
/// </summary>
public record Cashflow(string ClaimId, string LineOfBusiness, double GrossAmount);

/// <summary>
/// Excess-of-Loss layer: cedes the portion of each claim between
/// AttachmentPoint and AttachmentPoint + Limit.
/// </summary>
public record ExcessOfLossLayer(
    string Id,
    string Name,
    double AttachmentPoint,
    double Limit
);

/// <summary>
/// Result of applying a layer to a single cashflow.
/// </summary>
public record CededCashflow(
    string ClaimId,
    string LayerId,
    double GrossAmount,
    double CededAmount,
    double RetainedAmount
);

2. Business Rules — Cession into Layer

The core engine is a pure static class. No framework dependencies, no I/O — just math. This makes it trivially testable with standard xUnit assertions.

The formula per claim is: Ceded = min(Limit, max(0, Gross − AttachmentPoint))

public static class CessionEngine
{
    /// <summary>
    /// Applies an Excess-of-Loss layer to a set of cashflows.
    /// </summary>
    public static IReadOnlyList<CededCashflow> CedeIntoLayer(
        IEnumerable<Cashflow> cashflows,
        ExcessOfLossLayer layer)
    {
        return cashflows.Select(cf =>
        {
            var ceded = Math.Min(layer.Limit,
                                 Math.Max(0, cf.GrossAmount - layer.AttachmentPoint));
            return new CededCashflow(
                cf.ClaimId, layer.Id,
                cf.GrossAmount, ceded,
                cf.GrossAmount - ceded);
        }).ToList();
    }

    /// <summary>
    /// Computes summary statistics for a set of ceded cashflows.
    /// </summary>
    public static CessionSummary Summarize(IReadOnlyList<CededCashflow> results)
    {
        var totalGross = results.Sum(r => r.GrossAmount);
        var totalCeded = results.Sum(r => r.CededAmount);
        return new CessionSummary(
            ClaimCount: results.Count,
            TotalGross: totalGross,
            TotalCeded: totalCeded,
            TotalRetained: totalGross - totalCeded,
            CessionRatio: totalGross > 0 ? totalCeded / totalGross : 0
        );
    }
}

public record CessionSummary(
    int ClaimCount,
    double TotalGross,
    double TotalCeded,
    double TotalRetained,
    double CessionRatio
);

3. Layout Area — Ceded Distribution Chart

Layout areas subscribe to workspace data and re-render whenever the underlying streams emit. The layout function returns IObservable<UiControl> — MeshWeaver drives the lifecycle.

using MeshWeaver.Charting;
using MeshWeaver.Layout;
using MeshWeaver.Layout.Composition;

public static class CessionResultsArea
{
    public static LayoutDefinition AddCessionResults(this LayoutDefinition layout)
        => layout.WithView(nameof(CessionResults), CessionResults);

    public static IObservable<UiControl> CessionResults(
        LayoutAreaHost host, RenderingContext ctx)
    {
        return host.Workspace.GetStream<Cashflow>()
            .CombineLatest(
                host.Workspace.GetStream<ExcessOfLossLayer>(),
                (cashflows, layers) =>
                {
                    var layer = layers?.FirstOrDefault();
                    if (cashflows == null || layer == null)
                        return Controls.Markdown("Loading...");

                    var results = CessionEngine.CedeIntoLayer(cashflows, layer);
                    var summary = CessionEngine.Summarize(results);
                    return BuildView(host, results, summary, layer);
                });
    }

    private static UiControl BuildView(
        LayoutAreaHost host,
        IReadOnlyList<CededCashflow> results,
        CessionSummary summary,
        ExcessOfLossLayer layer)
    {
        // Summary statistics card
        var stats = Controls.Markdown(
            $"**Layer:** {layer.Name} (xs {layer.AttachmentPoint:N0} / {layer.Limit:N0})  \n" +
            $"**Claims:** {summary.ClaimCount} | " +
            $"**Gross:** {summary.TotalGross:N0} | " +
            $"**Ceded:** {summary.TotalCeded:N0} | " +
            $"**Retained:** {summary.TotalRetained:N0} | " +
            $"**Ratio:** {summary.CessionRatio:P1}");

        // Histogram of ceded amounts
        var ceded = results.Select(r => r.CededAmount).OrderBy(x => x).ToArray();
        var nonZero = ceded.Where(x => x > 0).ToArray();

        if (nonZero.Length == 0)
            return Controls.Stack
                .WithView(stats)
                .WithView(Controls.Markdown("No claims penetrate this layer."));

        var nBins = Math.Min(50, nonZero.Length);
        var min = nonZero.Min();
        var max = nonZero.Max();
        var binWidth = (max - min) / nBins;
        var histogram = new double[nBins];
        foreach (var v in nonZero)
        {
            var bin = Math.Min((int)((v - min) / binWidth), nBins - 1);
            histogram[bin]++;
        }
        var labels = Enumerable.Range(0, nBins)
            .Select(i => (min + (i + 0.5) * binWidth).ToString("N0"));

        var chart = Chart.Create(DataSet.Bar(histogram))
            .WithLabels(labels)
            .WithTitle($"Ceded Distribution — {layer.Name}")
            .ToControl()
            .WithStyle("width: 100%; height: 400px;");

        return Controls.Stack
            .WithView(Controls.Title("Cession Results", 3))
            .WithView(stats)
            .WithView(chart);
    }
}

Reactive re-rendering. CombineLatest means the chart rebuilds automatically any time cashflows or the layer definition changes — a filter toolbar just needs to update the workspace stream and the chart follows.


4. Wire It Together

Register domain types, initial data, and the layout area in the hub configuration:

public static MessageHubConfiguration AddReinsuranceExample(
    this MessageHubConfiguration config)
{
    return config
        .AddData(data => data
            .AddSource(source => source
                .WithType<Cashflow>(t => t.WithInitialData(SampleData.Claims))
                .WithType<ExcessOfLossLayer>(t => t.WithInitialData([SampleData.Layer]))
            ))
        .AddLayout(layout => layout
            .WithDefaultArea(nameof(CessionResultsArea.CessionResults))
            .AddCessionResults()
        );
}

5. Sample Data

Ten motor claims spanning all three cession outcomes — below attachment, partial, and full-limit hits:

public static class SampleData
{
    public static readonly ExcessOfLossLayer Layer = new(
        Id: "XL1",
        Name: "Motor XL 500k xs 200k",
        AttachmentPoint: 200_000,
        Limit: 500_000
    );

    public static readonly Cashflow[] Claims =
    [
        new("C001", "Motor", 150_000),   // Below attachment — fully retained
        new("C002", "Motor", 350_000),   // Partially ceded: 150k ceded
        new("C003", "Motor", 800_000),   // Hits limit: 500k ceded
        new("C004", "Motor", 50_000),    // Below attachment
        new("C005", "Motor", 1_200_000), // Hits limit: 500k ceded
        new("C006", "Motor", 250_000),   // Partially ceded: 50k
        new("C007", "Motor", 400_000),   // Partially ceded: 200k
        new("C008", "Motor", 180_000),   // Below attachment
        new("C009", "Motor", 700_000),   // Hits limit: 500k ceded
        new("C010", "Motor", 300_000),   // Partially ceded: 100k
    ];
}

Live Demo — Cession Calculation

The interactive cell below runs the same CessionEngine logic inline. It shows how a Motor XL 500k xs 200k layer splits ten claims into ceded, retained, and below-attachment buckets.

var attachmentPoint = 200_000.0;
var limit = 500_000.0;

var claims = new[]
{
    ("C001", 150_000.0),
    ("C002", 350_000.0),
    ("C003", 800_000.0),
    ("C004",  50_000.0),
    ("C005", 1_200_000.0),
    ("C006", 250_000.0),
    ("C007", 400_000.0),
    ("C008", 180_000.0),
    ("C009", 700_000.0),
    ("C010", 300_000.0),
};

var rows = claims.Select(c =>
{
    var ceded   = Math.Min(limit, Math.Max(0, c.Item2 - attachmentPoint));
    var retained = c.Item2 - ceded;
    var outcome = ceded == 0   ? "Below attachment"
                : ceded == limit ? "Full limit"
                :                  "Partial";
    return $"| {c.Item1} | {c.Item2,12:N0} | {ceded,10:N0} | {retained,12:N0} | {outcome} |";
});

var totalGross    = claims.Sum(c => c.Item2);
var totalCeded    = claims.Sum(c => Math.Min(limit, Math.Max(0, c.Item2 - attachmentPoint)));
var totalRetained = totalGross - totalCeded;
var ratio         = totalCeded / totalGross;

var header = "| Claim | Gross | Ceded | Retained | Outcome |\n|---|---:|---:|---:|---|";
var footer = $"\n**Totals — Gross: {totalGross:N0} | Ceded: {totalCeded:N0} | Retained: {totalRetained:N0} | Ratio: {ratio:P1}**";

MeshWeaver.Layout.Controls.Markdown(
    $"### Motor XL 500k xs 200k\n\n{header}\n{string.Join("\n", rows)}\n{footer}"
)

Pattern Reference

Pattern When to use
Immutable records All domain types — safe for reactive pipelines
Pure static functions Business logic with no side effects — trivial to unit-test
IObservable<UiControl> Layout areas that re-render when data changes
Chart.Create(DataSet.Bar(...)) Histograms, scatter plots, line charts
host.Workspace.GetStream<T>() Subscribe to typed data collections in the workspace
.CombineLatest() Merge multiple streams into a single computed view

Further Reading

For the full production implementation — Monte Carlo simulation, time series, proportional/non-proportional covers, and aggregate layers:

Reconnecting…
The server was updated. Reloading the page to pick up the latest version.