← Gallery

Topology

Service request topology

Explore the example, download the output or use the C# source in your own project.

Preview
Service request topologyGroups, status, details and routed linksService request topologyGroups, status, details and routed linksTopology LegendAvailableReviewServiceDependencyPrimary regionSecondary regionRequestsActiveLookupPersistClient serviceRequestsState AvailableCacheProcessing serviceResult store
Service request topology
Service request topologyGroups, status, details and routed linksService request topologyGroups, status, details and routed linksTopology LegendAvailableReviewServiceDependencyPrimary regionSecondary regionRequestsActiveLookupPersistClient serviceRequestsState AvailableCacheProcessing serviceResult store
Service request topology
960 × 600 pixels

Create this example

Create a console project, add the ChartForgeX package, and replace Program.cs with this source. Run it to write SVG, PNG and HTML with the default system fonts.

For the same typography as the gallery, download the gallery-font recipe and copy the gallery’s fonts directory beside your program.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using ChartForgeX.Core;
using ChartForgeX.Primitives;
using ChartForgeX.Rendering;
using ChartForgeX.Themes;
using ChartForgeX.Topology;
using ChartForgeX.Typography;
using ChartForgeX.VisualArtifacts;
using ChartForgeX.VisualBlocks;

var mode = VisualThemeMode.Light;
var theme = VisualTheme.Graphite();
var model = TopologyRecipe.Create("expanded", mode);
var context = new VisualRenderContext(
    new VisualLayoutOptions(new VisualSize(960, 600)), theme, mode,
    new VisualFrame("Service request topology", "Groups, status, details and routed links", showLegend: true, showSurface: false));
var prepared = model.Prepare(context);
File.WriteAllText("visual.svg", prepared.ToSvg());
File.WriteAllBytes("visual.png", prepared.ToPng());
File.WriteAllText("visual.html", prepared.ToArtifact("example", VisualArtifactKind.Topology).ToHtmlPage());

/// <summary>Creates a service topology or an atlas of the available node shapes.</summary>
public static class TopologyRecipe {
    /// <summary>Creates the diagram model for an expanded, compact or shape-atlas view.</summary>
    /// <param name="variant">Use wide, expanded or compact for the service topology, or shapes for the shape atlas.</param>
    /// <param name="mode">The rendering mode; diagram colors are resolved when the model is prepared.</param>
    public static TopologyChart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
        if (variant is not ("wide" or "expanded" or "compact" or "shapes"))
            throw new ArgumentOutOfRangeException(nameof(variant), variant, "Use wide, expanded, compact or shapes.");

        var atlas = variant == "shapes";
        var chart = TopologyChart.Create();
        chart.Id = atlas ? "shape-atlas" : "service-topology";
        chart.DefaultRenderOptions = new TopologyRenderOptions {
            FitContentToViewport = true,
            WrapNodeLabels = true,
            IncludeLegend = true,
            IncludeGroupStatusDots = true
        };

        if (atlas) {
            var shapes = Enum.GetValues<TopologyNodeShape>();
            for (var index = 0; index < shapes.Length; index++) {
                chart.Nodes.Add(new TopologyNode {
                    Id = "shape-" + index,
                    Label = shapes[index].ToString(),
                    X = 24 + index % 5 * 160,
                    Y = 24 + index / 5 * 120,
                    Width = 132,
                    Height = 86,
                    Shape = shapes[index],
                    PreserveDisplayModeSize = true,
                    ShowStatusBadge = false
                });
            }
            return chart;
        }

        chart.Groups.Add(new TopologyGroup {
            Id = "primary", Label = "Primary region", X = 0, Y = 0, Width = 340, Height = 300,
            Status = TopologyHealthStatus.Healthy
        });
        chart.Groups.Add(new TopologyGroup {
            Id = "secondary", Label = "Secondary region", X = 390, Y = 0, Width = 340, Height = 300,
            Status = TopologyHealthStatus.Warning
        });

        var source = new TopologyNode {
            Id = "client", Label = "Client service", Subtitle = "Requests", X = 30, Y = 65, Width = 240, Height = 85,
            Kind = TopologyNodeKind.Server, GroupId = "primary", Status = TopologyHealthStatus.Healthy
        };
        source.Details.Add(new TopologyNodeDetail {
            Label = "State", Value = "Available", Status = TopologyHealthStatus.Healthy
        });
        chart.Nodes.Add(source);
        chart.Nodes.Add(new TopologyNode {
            Id = "cache", Label = "Cache", X = 70, Y = 210, Width = 150, Height = 65,
            Shape = TopologyNodeShape.Cylinder, GroupId = "primary"
        });
        chart.Nodes.Add(new TopologyNode {
            Id = "worker", Label = "Processing service", X = 430, Y = 65, Width = 240, Height = 85,
            Kind = TopologyNodeKind.Server, GroupId = "secondary", Status = TopologyHealthStatus.Warning
        });
        chart.Nodes.Add(new TopologyNode {
            Id = "store", Label = "Result store", X = 470, Y = 210, Width = 150, Height = 65,
            Shape = TopologyNodeShape.Cylinder, GroupId = "secondary"
        });

        chart.Edges.Add(new TopologyEdge {
            Id = "request", SourceNodeId = "client", TargetNodeId = "worker", Label = "Requests", SecondaryLabel = "Active",
            Routing = TopologyEdgeRouting.ObstacleAvoidingOrthogonal, Direction = VisualLinkDirection.Forward
        });
        chart.Edges.Add(new TopologyEdge {
            Id = "cache-read", SourceNodeId = "client", TargetNodeId = "cache", Label = "Lookup", Routing = TopologyEdgeRouting.Curved,
            LineStyle = TopologyEdgeLineStyle.Dashed, Direction = VisualLinkDirection.Bidirectional
        });
        chart.Edges.Add(new TopologyEdge {
            Id = "persist", SourceNodeId = "worker", TargetNodeId = "store", Label = "Persist",
            Routing = TopologyEdgeRouting.Orthogonal
        });
        chart.Legend = TopologyLegend.Create()
            .AddStatus("Available", TopologyHealthStatus.Healthy)
            .AddStatus("Review", TopologyHealthStatus.Warning)
            .AddNodeKind("Service", TopologyNodeKind.Server)
            .AddEdgeKind("Dependency", TopologyEdgeKind.Dependency, lineStyle: TopologyEdgeLineStyle.Dashed);
        return chart;
    }
}
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using ChartForgeX.Core;
using ChartForgeX.Primitives;
using ChartForgeX.Rendering;
using ChartForgeX.Themes;
using ChartForgeX.Topology;
using ChartForgeX.Typography;
using ChartForgeX.VisualArtifacts;
using ChartForgeX.VisualBlocks;

var mode = VisualThemeMode.Dark;
var theme = VisualTheme.Graphite();
var model = TopologyRecipe.Create("expanded", mode);
var context = new VisualRenderContext(
    new VisualLayoutOptions(new VisualSize(960, 600)), theme, mode,
    new VisualFrame("Service request topology", "Groups, status, details and routed links", showLegend: true, showSurface: false));
var prepared = model.Prepare(context);
File.WriteAllText("visual.svg", prepared.ToSvg());
File.WriteAllBytes("visual.png", prepared.ToPng());
File.WriteAllText("visual.html", prepared.ToArtifact("example", VisualArtifactKind.Topology).ToHtmlPage());

/// <summary>Creates a service topology or an atlas of the available node shapes.</summary>
public static class TopologyRecipe {
    /// <summary>Creates the diagram model for an expanded, compact or shape-atlas view.</summary>
    /// <param name="variant">Use wide, expanded or compact for the service topology, or shapes for the shape atlas.</param>
    /// <param name="mode">The rendering mode; diagram colors are resolved when the model is prepared.</param>
    public static TopologyChart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
        if (variant is not ("wide" or "expanded" or "compact" or "shapes"))
            throw new ArgumentOutOfRangeException(nameof(variant), variant, "Use wide, expanded, compact or shapes.");

        var atlas = variant == "shapes";
        var chart = TopologyChart.Create();
        chart.Id = atlas ? "shape-atlas" : "service-topology";
        chart.DefaultRenderOptions = new TopologyRenderOptions {
            FitContentToViewport = true,
            WrapNodeLabels = true,
            IncludeLegend = true,
            IncludeGroupStatusDots = true
        };

        if (atlas) {
            var shapes = Enum.GetValues<TopologyNodeShape>();
            for (var index = 0; index < shapes.Length; index++) {
                chart.Nodes.Add(new TopologyNode {
                    Id = "shape-" + index,
                    Label = shapes[index].ToString(),
                    X = 24 + index % 5 * 160,
                    Y = 24 + index / 5 * 120,
                    Width = 132,
                    Height = 86,
                    Shape = shapes[index],
                    PreserveDisplayModeSize = true,
                    ShowStatusBadge = false
                });
            }
            return chart;
        }

        chart.Groups.Add(new TopologyGroup {
            Id = "primary", Label = "Primary region", X = 0, Y = 0, Width = 340, Height = 300,
            Status = TopologyHealthStatus.Healthy
        });
        chart.Groups.Add(new TopologyGroup {
            Id = "secondary", Label = "Secondary region", X = 390, Y = 0, Width = 340, Height = 300,
            Status = TopologyHealthStatus.Warning
        });

        var source = new TopologyNode {
            Id = "client", Label = "Client service", Subtitle = "Requests", X = 30, Y = 65, Width = 240, Height = 85,
            Kind = TopologyNodeKind.Server, GroupId = "primary", Status = TopologyHealthStatus.Healthy
        };
        source.Details.Add(new TopologyNodeDetail {
            Label = "State", Value = "Available", Status = TopologyHealthStatus.Healthy
        });
        chart.Nodes.Add(source);
        chart.Nodes.Add(new TopologyNode {
            Id = "cache", Label = "Cache", X = 70, Y = 210, Width = 150, Height = 65,
            Shape = TopologyNodeShape.Cylinder, GroupId = "primary"
        });
        chart.Nodes.Add(new TopologyNode {
            Id = "worker", Label = "Processing service", X = 430, Y = 65, Width = 240, Height = 85,
            Kind = TopologyNodeKind.Server, GroupId = "secondary", Status = TopologyHealthStatus.Warning
        });
        chart.Nodes.Add(new TopologyNode {
            Id = "store", Label = "Result store", X = 470, Y = 210, Width = 150, Height = 65,
            Shape = TopologyNodeShape.Cylinder, GroupId = "secondary"
        });

        chart.Edges.Add(new TopologyEdge {
            Id = "request", SourceNodeId = "client", TargetNodeId = "worker", Label = "Requests", SecondaryLabel = "Active",
            Routing = TopologyEdgeRouting.ObstacleAvoidingOrthogonal, Direction = VisualLinkDirection.Forward
        });
        chart.Edges.Add(new TopologyEdge {
            Id = "cache-read", SourceNodeId = "client", TargetNodeId = "cache", Label = "Lookup", Routing = TopologyEdgeRouting.Curved,
            LineStyle = TopologyEdgeLineStyle.Dashed, Direction = VisualLinkDirection.Bidirectional
        });
        chart.Edges.Add(new TopologyEdge {
            Id = "persist", SourceNodeId = "worker", TargetNodeId = "store", Label = "Persist",
            Routing = TopologyEdgeRouting.Orthogonal
        });
        chart.Legend = TopologyLegend.Create()
            .AddStatus("Available", TopologyHealthStatus.Healthy)
            .AddStatus("Review", TopologyHealthStatus.Warning)
            .AddNodeKind("Service", TopologyNodeKind.Server)
            .AddEdgeKind("Dependency", TopologyEdgeKind.Dependency, lineStyle: TopologyEdgeLineStyle.Dashed);
        return chart;
    }
}