Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Structurizr for .Net

DZone's Guide to

Structurizr for .Net

Create detailed architecture models by writing Java or C# code with a web-based software architecture tool called Structurizr.

· Web Dev Zone
Free Resource

Discover how to focus on operators for Reactive Programming and how they are essential to react to data in your application.  Brought to you in partnership with Wakanda

Structurizr - create web-based software architecture diagrams using code

The initial version of Structurizr was targeted at the Java ecosystem (see "Structurizr for Java"), for no other reason than it's what I'm most familiar with. Although this works for a good portion of the organizations that I visit when doing training/consulting, an equally sized portion use the Microsoft stack. For this reason, I've put together Structurizr for .NET, which is more or less a direct port of the Java version, with some automatically generated code from Swagger used as a starting point. It's by no means "feature complete" yet, especially since none of the component finder code (the part that extracts components automatically from a codebase) is present, but there's enough to create some basic diagrams. Here's some example code that creates a software model for the "Financial Risk System" case study that I use in my workshops.

using Structurizr.Client;
using Structurizr.IO.Json;
using Structurizr.Model;
using Structurizr.View;
using System.IO;
using System.Linq;
namespace Structurizr.Examples
{
/// <summary>
/// This is a simple example of how to create a software architecture model using Structurizr. The model
/// represents a sample solution for the "Financial Risk System" architecture kata, included in
/// "The Art of Visualising Software Architecture" book (available FREE from Leanpub).
/// 
/// The live version of the diagrams can be found at https://structurizr.com/public/9481
/// </summary>
class FinancialRiskSystem
    {
private const string AlertTag = "Alert";
static void Main(string[] args)
        {
            Workspace workspace = new Workspace("Financial Risk System", "A simple example C4 model based upon the financial risk system architecture kata, created using Structurizr for .NET");
            Model.Model model = workspace.Model;
// create the basic model
            SoftwareSystem financialRiskSystem = model.AddSoftwareSystem(Location.Internal, "Financial Risk System", "Calculates the bank's exposure to risk for product X");
            Person businessUser = model.AddPerson(Location.Internal, "Business User", "A regular business user");
            businessUser.Uses(financialRiskSystem, "Views reports using");
            Person configurationUser = model.AddPerson(Location.Internal, "Configuration User", "A regular business user who can also configure the parameters used in the risk calculations");
            configurationUser.Uses(financialRiskSystem, "Configures parameters using");
            SoftwareSystem tradeDataSystem = model.AddSoftwareSystem(Location.Internal, "Trade Data System", "The system of record for trades of type X");
            financialRiskSystem.Uses(tradeDataSystem, "Gets trade data from");
            SoftwareSystem referenceDataSystem = model.AddSoftwareSystem(Location.Internal, "Reference Data System", "Manages reference data for all counterparties the bank interacts with");
            financialRiskSystem.Uses(referenceDataSystem, "Gets counterparty data from");
            SoftwareSystem emailSystem = model.AddSoftwareSystem(Location.Internal, "E-mail system", "Microsoft Exchange");
            financialRiskSystem.Uses(emailSystem, "Sends a notification that a report is ready to");
            emailSystem.Delivers(businessUser, "Sends a notification that a report is ready to", "E-mail message", InteractionStyle.Asynchronous);
            SoftwareSystem centralMonitoringService = model.AddSoftwareSystem(Location.Internal, "Central Monitoring Service", "The bank-wide monitoring and alerting dashboard");
            financialRiskSystem.Uses(centralMonitoringService, "Sends critical failure alerts to", "SNMP", InteractionStyle.Asynchronous).AddTags(AlertTag);
            SoftwareSystem activeDirectory = model.AddSoftwareSystem(Location.Internal, "Active Directory", "Manages users and security roles across the bank");
            financialRiskSystem.Uses(activeDirectory, "Uses for authentication and authorisation");
            Container webApplication = financialRiskSystem.AddContainer("Web Application", "Allows users to view reports and modify risk calculation parameters", "ASP.NET MVC");
            businessUser.Uses(webApplication, "Views reports using");
            configurationUser.Uses(webApplication, "Modifies risk calculation parameters using");
            webApplication.Uses(activeDirectory, "Uses for authentication and authorisation");
            Container batchProcess = financialRiskSystem.AddContainer("Batch Process", "Calculates the risk", "Windows Service");
            batchProcess.Uses(emailSystem, "Sends a notification that a report is ready to");
            batchProcess.Uses(tradeDataSystem, "Gets trade data from");
            batchProcess.Uses(referenceDataSystem, "Gets counterparty data from");
            batchProcess.Uses(centralMonitoringService, "Sends critical failure alerts to", "SNMP", InteractionStyle.Asynchronous).AddTags(AlertTag);
            Container fileSystem = financialRiskSystem.AddContainer("File System", "Stores risk reports", "Network File Share");
            webApplication.Uses(fileSystem, "Consumes risk reports from");
            batchProcess.Uses(fileSystem, "Publishes risk reports to");
            Component scheduler = batchProcess.AddComponent("Scheduler", "Starts the risk calculation process at 5pm New York time", "Quartz.NET");
            Component orchestrator = batchProcess.AddComponent("Orchestrator", "Orchestrates the risk calculation process", "C#");
            Component tradeDataImporter = batchProcess.AddComponent("Trade data importer", "Imports data from the Trade Data System", "C#");
            Component referenceDataImporter = batchProcess.AddComponent("Reference data importer", "Imports data from the Reference Data System", "C#");
            Component riskCalculator = batchProcess.AddComponent("Risk calculator", "Calculates risk", "C#");
            Component reportGenerator = batchProcess.AddComponent("Report generator", "Generates a Microsoft Excel compatible risk report", "C# and Microsoft.Office.Interop.Excel");
            Component reportPublisher = batchProcess.AddComponent("Report distributor", "Publishes the report to the web application", "C#");
            Component emailComponent = batchProcess.AddComponent("E-mail component", "Sends e-mails", "C#");
            Component reportChecker = batchProcess.AddComponent("Report checker", "Checks that the report has been generated by 9am singapore time", "C#");
            Component alertComponent = batchProcess.AddComponent("Alert component", "Sends SNMP alerts", "C# and #SNMP Library");
            scheduler.Uses(orchestrator, "Starts");
            scheduler.Uses(reportChecker, "Starts");
            orchestrator.Uses(tradeDataImporter, "Imports data using");
            tradeDataImporter.Uses(tradeDataSystem, "Imports data from");
            orchestrator.Uses(referenceDataImporter, "Imports data using");
            referenceDataImporter.Uses(referenceDataSystem, "Imports data from");
            orchestrator.Uses(riskCalculator, "Calculates the risk using");
            orchestrator.Uses(reportGenerator, "Generates the risk report using");
            orchestrator.Uses(reportPublisher, "Publishes the risk report using");
            reportPublisher.Uses(fileSystem, "Publishes the risk report to");
            orchestrator.Uses(emailComponent, "Sends e-mail using");
            emailComponent.Uses(emailSystem, "Sends a notification that a report is ready to");
            reportChecker.Uses(alertComponent, "Sends alerts using");
            alertComponent.Uses(centralMonitoringService, "Sends alerts using", "SNMP", InteractionStyle.Asynchronous).AddTags(AlertTag);
// create some views
            ViewSet viewSet = workspace.Views;
            SystemContextView contextView = viewSet.CreateContextView(financialRiskSystem);
            contextView.PaperSize = PaperSize.A4_Landscape;
            contextView.AddAllSoftwareSystems();
            contextView.AddAllPeople();
            ContainerView containerView = viewSet.CreateContainerView(financialRiskSystem);
            contextView.PaperSize = PaperSize.A4_Landscape;
            containerView.AddAllElements();
            ComponentView componentViewForBatchProcess = viewSet.CreateComponentView(batchProcess);
            contextView.PaperSize = PaperSize.A3_Landscape;
            componentViewForBatchProcess.AddAllElements();
            componentViewForBatchProcess.Remove(configurationUser);
            componentViewForBatchProcess.Remove(webApplication);
            componentViewForBatchProcess.Remove(activeDirectory);
// tag and style some elements
            Styles styles = viewSet.Configuration.Styles;
            financialRiskSystem.AddTags("Risk System");
            styles.Add(new ElementStyle(Tags.Element) { Color = "#ffffff", FontSize = 34 });
            styles.Add(new ElementStyle("Risk System") { Background = "#8a458a" });
            styles.Add(new ElementStyle(Tags.SoftwareSystem) { Width = 650, Height = 400, Background = "#510d51", Shape = Shape.Box });
            styles.Add(new ElementStyle(Tags.Person) { Width = 550, Background = "#62256e", Shape = Shape.Person });
            styles.Add(new ElementStyle(Tags.Container) { Width = 650, Height = 400, Background = "#a46ba4", Shape = Shape.Box });
            styles.Add(new ElementStyle(Tags.Component) { Width = 550, Background = "#c9a1c9", Shape = Shape.Box });
            styles.Add(new RelationshipStyle(Tags.Relationship) { Thickness = 4, Dashed = false, FontSize = 32, Width = 400 });
            styles.Add(new RelationshipStyle(Tags.Synchronous) { Dashed = false });
            styles.Add(new RelationshipStyle(Tags.Asynchronous) { Dashed = true });
            styles.Add(new RelationshipStyle(AlertTag) { Color = "#ff0000" });
            businessUser.Relationships.ToList().ForEach(r => r.AddTags("HTTPS"));
// and upload the model to structurizr.com
            StructurizrClient structurizrClient = new StructurizrClient("key", "secret");
            structurizrClient.MergeWorkspace(9481, workspace);
        }
    }
}

It creates the following Context, Container, and Component diagrams, which you can see in Structurizr.

Image title

Image title

Image title

If you want to take a look or try it out, the source code can be found on GitHub and there's an initial version of the package on NuGet. Have fun!

Learn how divergent branches can appear in your repository and how to better understand why they are called “branches".  Brought to you in partnership with Wakanda

Topics:
architecture ,modeling ,.net

Published at DZone with permission of Simon Brown, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}