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 connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.