diff --git a/Structurizr.ActiveDirectory/Structurizr.ActiveDirectory.csproj b/Structurizr.ActiveDirectory/Structurizr.ActiveDirectory.csproj index 1d7b98d..581ff58 100644 --- a/Structurizr.ActiveDirectory/Structurizr.ActiveDirectory.csproj +++ b/Structurizr.ActiveDirectory/Structurizr.ActiveDirectory.csproj @@ -8,12 +8,12 @@ Exe - netcoreapp1.1 + netcoreapp2.1 - + \ No newline at end of file diff --git a/Structurizr.AdrTools.Tests/AdrTools/AdrToolsImporterTests.cs b/Structurizr.AdrTools.Tests/AdrTools/AdrToolsImporterTests.cs index b0122bf..f3849c7 100644 --- a/Structurizr.AdrTools.Tests/AdrTools/AdrToolsImporterTests.cs +++ b/Structurizr.AdrTools.Tests/AdrTools/AdrToolsImporterTests.cs @@ -106,7 +106,8 @@ public void Test_ImportArchitectureDecisionRecords_RewritesLinksBetweenADRsWhenT importer.ImportArchitectureDecisionRecords(); Decision decision5 = _documentation.Decisions.Where(d => d.Id == "5").First(); - Assert.True(decision5.Content.Contains("Amended by [9. Help scripts](#/:9)")); + // sync with java impl %2F instead of / + Assert.True(decision5.Content.Contains("Amended by [9. Help scripts](#%2F:9)")); } [Fact] @@ -127,7 +128,8 @@ public void Test_ImportArchitectureDecisionRecords_SupportsTheIncorrectSpellingO Decision decision4 = _documentation.Decisions.Where(d => d.Id == "4").First(); Assert.Equal(DecisionStatus.Superseded, decision4.Status); - Assert.True(decision4.Content.Contains("Superceded by [10. AsciiDoc format](#/:10)")); + // sync with java impl %2F instead of / + Assert.True(decision4.Content.Contains("Superceded by [10. AsciiDoc format](#%2F:10)")); } } diff --git a/Structurizr.AdrTools.Tests/Structurizr.AdrTools.Tests.csproj b/Structurizr.AdrTools.Tests/Structurizr.AdrTools.Tests.csproj index 9f13f19..e939dbb 100644 --- a/Structurizr.AdrTools.Tests/Structurizr.AdrTools.Tests.csproj +++ b/Structurizr.AdrTools.Tests/Structurizr.AdrTools.Tests.csproj @@ -1,6 +1,6 @@ - netcoreapp1.1 + netcoreapp2.1 false diff --git a/Structurizr.AdrTools/AdrTools/AdrToolsImporter.cs b/Structurizr.AdrTools/AdrTools/AdrToolsImporter.cs index 0bfa2db..81676fb 100644 --- a/Structurizr.AdrTools/AdrTools/AdrToolsImporter.cs +++ b/Structurizr.AdrTools/AdrTools/AdrToolsImporter.cs @@ -90,11 +90,15 @@ public ISet ImportArchitectureDecisionRecords(SoftwareSystem softwareS private string CalculateUrl(SoftwareSystem softwareSystem, string id) { if (softwareSystem == null) { - return "#/:" + UrlEncode(id); + // sync with java impl + return "#" + UrlEncode("/") + ":" + UrlEncode(id); } else { - return "#" + UrlEncode(softwareSystem.CanonicalName) + ":" + UrlEncode(id); + //CanonicalName impl changed: old "/", new "SoftwareSystem://" (CanonicalNameGenerator.SoftwareSystemType) + var name = softwareSystem.CanonicalName; + name = name.Substring("SoftwareSystem:/".Length); // last "/" is reused + return "#" + UrlEncode(name) + ":" + UrlEncode(id); } } diff --git a/Structurizr.AdrTools/Structurizr.AdrTools.csproj b/Structurizr.AdrTools/Structurizr.AdrTools.csproj index 38c9414..c9c8733 100644 --- a/Structurizr.AdrTools/Structurizr.AdrTools.csproj +++ b/Structurizr.AdrTools/Structurizr.AdrTools.csproj @@ -7,11 +7,11 @@ - netstandard1.3;net45 + netstandard2.0;net45 - + diff --git a/Structurizr.Analysis/Structurizr.Analysis.csproj b/Structurizr.Analysis/Structurizr.Analysis.csproj index 3bfbc40..f536c8b 100644 --- a/Structurizr.Analysis/Structurizr.Analysis.csproj +++ b/Structurizr.Analysis/Structurizr.Analysis.csproj @@ -7,11 +7,11 @@ - netstandard1.3;net45 + netstandard2.0;net45 - + \ No newline at end of file diff --git a/Structurizr.Annotations/Structurizr.Annotations.csproj b/Structurizr.Annotations/Structurizr.Annotations.csproj index 2395b0c..b0aab59 100644 --- a/Structurizr.Annotations/Structurizr.Annotations.csproj +++ b/Structurizr.Annotations/Structurizr.Annotations.csproj @@ -17,7 +17,12 @@ + + netstandard2.0;net20 bin\$(Configuration)\$(TargetFramework)\Structurizr.Annotations.xml 1.6.0 @@ -48,4 +53,11 @@ 4.1.0 + + + + 4.1.0 + + + diff --git a/Structurizr.Cecil.Examples/Structurizr.Cecil.Examples.csproj b/Structurizr.Cecil.Examples/Structurizr.Cecil.Examples.csproj index 9d7a008..2a94992 100644 --- a/Structurizr.Cecil.Examples/Structurizr.Cecil.Examples.csproj +++ b/Structurizr.Cecil.Examples/Structurizr.Cecil.Examples.csproj @@ -8,7 +8,7 @@ - + diff --git a/Structurizr.Cecil/Structurizr.Cecil.csproj b/Structurizr.Cecil/Structurizr.Cecil.csproj index ffa5ec8..4a98d97 100644 --- a/Structurizr.Cecil/Structurizr.Cecil.csproj +++ b/Structurizr.Cecil/Structurizr.Cecil.csproj @@ -16,12 +16,12 @@ - netstandard1.3;net45 + netstandard2.0;net45 - + diff --git a/Structurizr.Examples/BigBankPlc.cs b/Structurizr.Examples/BigBankPlc.cs index 005e661..b9e7d32 100644 --- a/Structurizr.Examples/BigBankPlc.cs +++ b/Structurizr.Examples/BigBankPlc.cs @@ -5,7 +5,7 @@ namespace Structurizr.Examples { - + /// /// This is an example workspace to illustrate the key features of Structurizr, /// based around a fictional Internet Banking System for Big Bank plc. @@ -28,238 +28,270 @@ public class BigBankPlc public static Workspace Create() { - Workspace workspace = new Workspace("Big Bank plc", "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system."); - Model model = workspace.Model; - ViewSet views = workspace.Views; - - model.Enterprise = new Enterprise("Big Bank plc"); - - // people and software systems - Person customer = model.AddPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); - - SoftwareSystem internetBankingSystem = model.AddSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); - customer.Uses(internetBankingSystem, "Uses"); - - SoftwareSystem mainframeBankingSystem = model.AddSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); - mainframeBankingSystem.AddTags(ExistingSystemTag); - internetBankingSystem.Uses(mainframeBankingSystem, "Uses"); - - SoftwareSystem emailSystem = model.AddSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); - internetBankingSystem.Uses(emailSystem, "Sends e-mail using"); - emailSystem.AddTags(ExistingSystemTag); - emailSystem.Delivers(customer, "Sends e-mails to"); - - SoftwareSystem atm = model.AddSoftwareSystem(Location.Internal, "ATM", "Allows customers to withdraw cash."); - atm.AddTags(ExistingSystemTag); - atm.Uses(mainframeBankingSystem, "Uses"); - customer.Uses(atm, "Withdraws cash using"); - - Person customerServiceStaff = model.AddPerson(Location.Internal, "Customer Service Staff", "Customer service staff within the bank."); - customerServiceStaff.AddTags(BankStaffTag); - customerServiceStaff.Uses(mainframeBankingSystem, "Uses"); - customer.InteractsWith(customerServiceStaff, "Asks questions to", "Telephone"); - - Person backOfficeStaff = model.AddPerson(Location.Internal, "Back Office Staff", "Administration and support staff within the bank."); - backOfficeStaff.AddTags(BankStaffTag); - backOfficeStaff.Uses(mainframeBankingSystem, "Uses"); - - // containers - Container singlePageApplication = internetBankingSystem.AddContainer("Single-Page Application", "Provides all of the Internet banking functionality to customers via their web browser.", "JavaScript and Angular"); - singlePageApplication.AddTags(WebBrowserTag); - Container mobileApp = internetBankingSystem.AddContainer("Mobile App", "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", "Xamarin"); - mobileApp.AddTags(MobileAppTag); - Container webApplication = internetBankingSystem.AddContainer("Web Application", "Delivers the static content and the Internet banking single page application.", "Java and Spring MVC"); - Container apiApplication = internetBankingSystem.AddContainer("API Application", "Provides Internet banking functionality via a JSON/HTTPS API.", "Java and Spring MVC"); - Container database = internetBankingSystem.AddContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Relational Database Schema"); - database.AddTags(DatabaseTag); - - customer.Uses(webApplication, "Uses", "HTTPS"); - customer.Uses(singlePageApplication, "Uses", ""); - customer.Uses(mobileApp, "Uses", ""); - webApplication.Uses(singlePageApplication, "Delivers to the customer's web browser", ""); - apiApplication.Uses(database, "Reads from and writes to", "JDBC"); - apiApplication.Uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); - apiApplication.Uses(emailSystem, "Sends e-mail using", "SMTP"); - - // components - // - for a real-world software system, you would probably want to extract the components using - // - static analysis/reflection rather than manually specifying them all - Component signinController = apiApplication.AddComponent("Sign In Controller", "Allows users to sign in to the Internet Banking System.", "Spring MVC Rest Controller"); - Component accountsSummaryController = apiApplication.AddComponent("Accounts Summary Controller", "Provides customers with a summary of their bank accounts.", "Spring MVC Rest Controller"); - Component resetPasswordController = apiApplication.AddComponent("Reset Password Controller", "Allows users to reset their passwords with a single use URL.", "Spring MVC Rest Controller"); - Component securityComponent = apiApplication.AddComponent("Security Component", "Provides functionality related to signing in, changing passwords, etc.", "Spring Bean"); - Component mainframeBankingSystemFacade = apiApplication.AddComponent("Mainframe Banking System Facade", "A facade onto the mainframe banking system.", "Spring Bean"); - Component emailComponent = apiApplication.AddComponent("E-mail Component", "Sends e-mails to users.", "Spring Bean"); - - apiApplication.Components.Where(c => "Spring MVC Rest Controller".Equals(c.Technology)).ToList().ForEach(c => singlePageApplication.Uses(c, "Makes API calls to", "JSON/HTTPS")); - apiApplication.Components.Where(c => "Spring MVC Rest Controller".Equals(c.Technology)).ToList().ForEach(c => mobileApp.Uses(c, "Makes API calls to", "JSON/HTTPS")); - signinController.Uses(securityComponent, "Uses"); - accountsSummaryController.Uses(mainframeBankingSystemFacade, "Uses"); - resetPasswordController.Uses(securityComponent, "Uses"); - resetPasswordController.Uses(emailComponent, "Uses"); - securityComponent.Uses(database, "Reads from and writes to", "JDBC"); - mainframeBankingSystemFacade.Uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); - emailComponent.Uses(emailSystem, "Sends e-mail using"); - - model.AddImplicitRelationships(); - - // deployment nodes and container instances - DeploymentNode developerLaptop = model.AddDeploymentNode("Development", "Developer Laptop", "A developer laptop.", "Microsoft Windows 10 or Apple macOS"); - DeploymentNode apacheTomcat = developerLaptop.AddDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") - .AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")); - apacheTomcat.Add(webApplication); - apacheTomcat.Add(apiApplication); - - developerLaptop.AddDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") - .AddDeploymentNode("Database Server", "A development database.", "Oracle 12c") - .Add(database); - - developerLaptop.AddDeploymentNode("Web Browser", "", "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge").Add(singlePageApplication); - - DeploymentNode customerMobileDevice = model.AddDeploymentNode("Live", "Customer's mobile device", "", "Apple iOS or Android"); - customerMobileDevice.Add(mobileApp); - - DeploymentNode customerComputer = model.AddDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); - customerComputer.AddDeploymentNode("Web Browser", "", "Google Chrome, Mozilla Firefox, Apple Safari or Microsoft Edge").Add(singlePageApplication); - - DeploymentNode bigBankDataCenter = model.AddDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); - - DeploymentNode liveWebServer = bigBankDataCenter.AddDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, DictionaryUtils.Create("Location=London and Reading")); - liveWebServer.AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .Add(webApplication); - - DeploymentNode liveApiServer = bigBankDataCenter.AddDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, DictionaryUtils.Create("Location=London and Reading")); - liveApiServer.AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")) - .Add(apiApplication); - - DeploymentNode primaryDatabaseServer = bigBankDataCenter.AddDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, DictionaryUtils.Create("Location=London")) - .AddDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); - primaryDatabaseServer.Add(database); - - DeploymentNode secondaryDatabaseServer = bigBankDataCenter.AddDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, DictionaryUtils.Create("Location=Reading")) - .AddDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); - ContainerInstance secondaryDatabase = secondaryDatabaseServer.Add(database); - - model.Relationships.Where(r=>r.Destination.Equals(secondaryDatabase)).ToList().ForEach(r=>r.AddTags(FailoverTag)); - Relationship dataReplicationRelationship = primaryDatabaseServer.Uses(secondaryDatabaseServer, "Replicates data to", ""); - secondaryDatabase.AddTags(FailoverTag); - - // views/diagrams - SystemLandscapeView systemLandscapeView = views.CreateSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); - systemLandscapeView.AddAllElements(); - systemLandscapeView.PaperSize = PaperSize.A5_Landscape; - - SystemContextView systemContextView = views.CreateSystemContextView(internetBankingSystem, "SystemContext", "The system context diagram for the Internet Banking System."); - systemContextView.EnterpriseBoundaryVisible = false; - systemContextView.AddNearestNeighbours(internetBankingSystem); - systemContextView.PaperSize = PaperSize.A5_Landscape; - - ContainerView containerView = views.CreateContainerView(internetBankingSystem, "Containers", "The container diagram for the Internet Banking System."); - containerView.Add(customer); - containerView.AddAllContainers(); - containerView.Add(mainframeBankingSystem); - containerView.Add(emailSystem); - containerView.PaperSize = PaperSize.A5_Landscape; - - ComponentView componentView = views.CreateComponentView(apiApplication, "Components", "The component diagram for the API Application."); - componentView.Add(mobileApp); - componentView.Add(singlePageApplication); - componentView.Add(database); - componentView.AddAllComponents(); - componentView.Add(mainframeBankingSystem); - componentView.Add(emailSystem); - componentView.PaperSize = PaperSize.A5_Landscape; - - systemLandscapeView.AddAnimation(internetBankingSystem, customer, mainframeBankingSystem, emailSystem); - systemLandscapeView.AddAnimation(atm); - systemLandscapeView.AddAnimation(customerServiceStaff, backOfficeStaff); - - systemContextView.AddAnimation(internetBankingSystem); - systemContextView.AddAnimation(customer); - systemContextView.AddAnimation(mainframeBankingSystem); - systemContextView.AddAnimation(emailSystem); - - containerView.AddAnimation(customer, mainframeBankingSystem, emailSystem); - containerView.AddAnimation(webApplication); - containerView.AddAnimation(singlePageApplication); - containerView.AddAnimation(mobileApp); - containerView.AddAnimation(apiApplication); - containerView.AddAnimation(database); - - componentView.AddAnimation(singlePageApplication, mobileApp); - componentView.AddAnimation(signinController, securityComponent, database); - componentView.AddAnimation(accountsSummaryController, mainframeBankingSystemFacade, mainframeBankingSystem); - componentView.AddAnimation(resetPasswordController, emailComponent, database); - - // dynamic diagrams and deployment diagrams are not available with the Free Plan - DynamicView dynamicView = views.CreateDynamicView(apiApplication, "SignIn", "Summarises how the sign in feature works in the single-page application."); - dynamicView.Add(singlePageApplication, "Submits credentials to", signinController); - dynamicView.Add(signinController, "Calls isAuthenticated() on", securityComponent); - dynamicView.Add(securityComponent, "select * from users where username = ?", database); - dynamicView.PaperSize = PaperSize.A5_Landscape; - - DeploymentView developmentDeploymentView = views.CreateDeploymentView(internetBankingSystem, "DevelopmentDeployment", "An example development deployment scenario for the Internet Banking System."); - developmentDeploymentView.Environment = "Development"; - developmentDeploymentView.Add(developerLaptop); - developmentDeploymentView.PaperSize = PaperSize.A5_Landscape; - - DeploymentView liveDeploymentView = views.CreateDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); - liveDeploymentView.Environment = "Live"; - liveDeploymentView.Add(bigBankDataCenter); - liveDeploymentView.Add(customerMobileDevice); - liveDeploymentView.Add(customerComputer); - liveDeploymentView.Add(dataReplicationRelationship); - liveDeploymentView.PaperSize = PaperSize.A5_Landscape; - - // colours, shapes and other diagram styling - Styles styles = views.Configuration.Styles; - styles.Add(new ElementStyle(Tags.Element) { Color = "#ffffff" }); - styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd" }); - styles.Add(new ElementStyle(Tags.Container) { Background = "#438dd5" }); - styles.Add(new ElementStyle(Tags.Component) { Background = "#85bbf0", Color = "#000000" }); - styles.Add(new ElementStyle(Tags.Person) { Background = "#08427b", Shape = Shape.Person, FontSize = 22}); - styles.Add(new ElementStyle(ExistingSystemTag) { Background = "#999999"}); - styles.Add(new ElementStyle(BankStaffTag) { Background = "#999999" }); - styles.Add(new ElementStyle(WebBrowserTag) { Shape = Shape.WebBrowser }); - styles.Add(new ElementStyle(MobileAppTag) { Shape = Shape.MobileDeviceLandscape }); - styles.Add(new ElementStyle(DatabaseTag) { Shape = Shape.Cylinder }); - styles.Add(new ElementStyle(FailoverTag) { Opacity = 25 }); - styles.Add(new RelationshipStyle(FailoverTag) { Opacity = 25, Position = 70}); - - // documentation - // - usually the documentation would be included from separate Markdown/AsciiDoc files, but this is just an example - StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); - template.AddContextSection(internetBankingSystem, Format.Markdown, - "Here is some context about the Internet Banking System...\n" + - "![](embed:SystemLandscape)\n" + - "![](embed:SystemContext)\n" + - "### Internet Banking System\n...\n" + - "### Mainframe Banking System\n...\n"); - template.AddContainersSection(internetBankingSystem, Format.Markdown, - "Here is some information about the containers within the Internet Banking System...\n" + - "![](embed:Containers)\n" + - "### Web Application\n...\n" + - "### Database\n...\n"); - template.AddComponentsSection(webApplication, Format.Markdown, - "Here is some information about the API Application...\n" + - "![](embed:Components)\n" + - "### Sign in process\n" + - "Here is some information about the Sign In Controller, including how the sign in process works...\n" + - "![](embed:SignIn)"); - template.AddDevelopmentEnvironmentSection(internetBankingSystem, Format.AsciiDoc, - "Here is some information about how to set up a development environment for the Internet Banking System...\n" + - "image::embed:DevelopmentDeployment[]"); - template.AddDeploymentSection(internetBankingSystem, Format.AsciiDoc, - "Here is some information about the live deployment environment for the Internet Banking System...\n" + - "image::embed:LiveDeployment[]"); - - return workspace; + Workspace workspace = new Workspace("Big Bank plc", "This is an example workspace to illustrate the key features of Structurizr, based around a fictional online banking system."); + Model model = workspace.Model; + model.ImpliedRelationshipsStrategy = new CreateImpliedRelationshipsUnlessAnyRelationshipExistsStrategy(); + ViewSet views = workspace.Views; + + model.Enterprise = new Enterprise("Big Bank plc"); + + // people and software systems + SoftwareSystem internetBankingSystem = model.AddSoftwareSystem(Location.Internal, "Internet Banking System", "Allows customers to view information about their bank accounts, and make payments."); + Person customer = model.AddPerson(Location.External, "Personal Banking Customer", "A customer of the bank, with personal bank accounts."); + + customer.Uses(internetBankingSystem, "Views account balances, and makes payments using"); + + SoftwareSystem mainframeBankingSystem = model.AddSoftwareSystem(Location.Internal, "Mainframe Banking System", "Stores all of the core banking information about customers, accounts, transactions, etc."); + mainframeBankingSystem.AddTags(ExistingSystemTag); + internetBankingSystem.Uses(mainframeBankingSystem, "Gets account information from, and makes payments using"); + + SoftwareSystem emailSystem = model.AddSoftwareSystem(Location.Internal, "E-mail System", "The internal Microsoft Exchange e-mail system."); + internetBankingSystem.Uses(emailSystem, "Sends e-mail using"); + emailSystem.AddTags(ExistingSystemTag); + emailSystem.Delivers(customer, "Sends e-mails to"); + + SoftwareSystem atm = model.AddSoftwareSystem(Location.Internal, "ATM", "Allows customers to withdraw cash."); + atm.AddTags(ExistingSystemTag); + atm.Uses(mainframeBankingSystem, "Uses"); + customer.Uses(atm, "Withdraws cash using"); + + Person customerServiceStaff = model.AddPerson(Location.Internal, "Customer Service Staff", "Customer service staff within the bank."); + customerServiceStaff.AddTags(BankStaffTag); + customerServiceStaff.Uses(mainframeBankingSystem, "Uses"); + customer.InteractsWith(customerServiceStaff, "Asks questions to", "Telephone"); + + Person backOfficeStaff = model.AddPerson(Location.Internal, "Back Office Staff", "Administration and support staff within the bank."); + backOfficeStaff.AddTags(BankStaffTag); + backOfficeStaff.Uses(mainframeBankingSystem, "Uses"); + + // containers + Container singlePageApplication = internetBankingSystem.AddContainer("Single-Page Application", "Provides all of the Internet banking functionality to customers via their web browser.", "JavaScript and Angular"); + singlePageApplication.AddTags(WebBrowserTag); + Container mobileApp = internetBankingSystem.AddContainer("Mobile App", "Provides a limited subset of the Internet banking functionality to customers via their mobile device.", "Xamarin"); + mobileApp.AddTags(MobileAppTag); + Container webApplication = internetBankingSystem.AddContainer("Web Application", "Delivers the static content and the Internet banking single page application.", "Java and Spring MVC"); + Container apiApplication = internetBankingSystem.AddContainer("API Application", "Provides Internet banking functionality via a JSON/HTTPS API.", "Java and Spring MVC"); + Container database = internetBankingSystem.AddContainer("Database", "Stores user registration information, hashed authentication credentials, access logs, etc.", "Relational Database Schema"); + database.AddTags(DatabaseTag); + + customer.Uses(webApplication, "Visits bigbank.com/ib using", "HTTPS"); + customer.Uses(singlePageApplication, "Views account balances, and makes payments using", ""); + customer.Uses(mobileApp, "Views account balances, and makes payments using", ""); + webApplication.Uses(singlePageApplication, "Delivers to the customer's web browser", ""); + apiApplication.Uses(database, "Reads from and writes to", "JDBC"); + apiApplication.Uses(mainframeBankingSystem, "Makes API calls to", "XML/HTTPS"); + apiApplication.Uses(emailSystem, "Sends e-mail using", "SMTP"); + + // components + // - for a real-world software system, you would probably want to extract the components using + // - static analysis/reflection rather than manually specifying them all + Component signinController = apiApplication.AddComponent("Sign In Controller", "Allows users to sign in to the Internet Banking System.", "Spring MVC Rest Controller"); + Component accountsSummaryController = apiApplication.AddComponent("Accounts Summary Controller", "Provides customers with a summary of their bank accounts.", "Spring MVC Rest Controller"); + Component resetPasswordController = apiApplication.AddComponent("Reset Password Controller", "Allows users to reset their passwords with a single use URL.", "Spring MVC Rest Controller"); + Component securityComponent = apiApplication.AddComponent("Security Component", "Provides functionality related to signing in, changing passwords, etc.", "Spring Bean"); + Component mainframeBankingSystemFacade = apiApplication.AddComponent("Mainframe Banking System Facade", "A facade onto the mainframe banking system.", "Spring Bean"); + Component emailComponent = apiApplication.AddComponent("E-mail Component", "Sends e-mails to users.", "Spring Bean"); + + apiApplication.Components.Where(c => "Spring MVC Rest Controller".Equals(c.Technology)).ToList().ForEach(c => singlePageApplication.Uses(c, "Makes API calls to", "JSON/HTTPS")); + apiApplication.Components.Where(c => "Spring MVC Rest Controller".Equals(c.Technology)).ToList().ForEach(c => mobileApp.Uses(c, "Makes API calls to", "JSON/HTTPS")); + signinController.Uses(securityComponent, "Uses"); + accountsSummaryController.Uses(mainframeBankingSystemFacade, "Uses"); + resetPasswordController.Uses(securityComponent, "Uses"); + resetPasswordController.Uses(emailComponent, "Uses"); + securityComponent.Uses(database, "Reads from and writes to", "JDBC"); + mainframeBankingSystemFacade.Uses(mainframeBankingSystem, "Uses", "XML/HTTPS"); + emailComponent.Uses(emailSystem, "Sends e-mail using"); + + // deployment nodes and container instances + DeploymentNode developerLaptop = model.AddDeploymentNode("Development", "Developer Laptop", "A developer laptop.", "Microsoft Windows 10 or Apple macOS"); + DeploymentNode apacheTomcat = developerLaptop + .AddDeploymentNode("Docker Container - Web Server", "A Docker container.", "Docker") + .AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")); + ContainerInstance developmentWebApplication = apacheTomcat.Add(webApplication); + ContainerInstance developmentApiApplication = apacheTomcat.Add(apiApplication); + + DeploymentNode bigBankDataCenterForDevelopment = model.AddDeploymentNode("Development", "Big Bank plc", "", "Big Bank plc data center"); + SoftwareSystemInstance developmentMainframeBankingSystem = bigBankDataCenterForDevelopment + .AddDeploymentNode("bigbank-dev001").Add(mainframeBankingSystem); + + ContainerInstance developmentDatabase = developerLaptop + .AddDeploymentNode("Docker Container - Database Server", "A Docker container.", "Docker") + .AddDeploymentNode("Database Server", "A development database.", "Oracle 12c") + .Add(database); + + ContainerInstance developmentSinglePageApplication = developerLaptop + .AddDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge") + .Add(singlePageApplication); + + DeploymentNode customerMobileDevice = model.AddDeploymentNode("Live", "Customer's mobile device", "", "Apple iOS or Android"); + ContainerInstance liveMobileApp = customerMobileDevice.Add(mobileApp); + + DeploymentNode customerComputer = model.AddDeploymentNode("Live", "Customer's computer", "", "Microsoft Windows or Apple macOS"); + ContainerInstance liveSinglePageApplication = customerComputer + .AddDeploymentNode("Web Browser", "", "Chrome, Firefox, Safari, or Edge") + .Add(singlePageApplication); + + DeploymentNode bigBankDataCenterForLive = + model.AddDeploymentNode("Live", "Big Bank plc", "", "Big Bank plc data center"); + SoftwareSystemInstance liveMainframeBankingSystem = bigBankDataCenterForLive + .AddDeploymentNode("bigbank-prod001").Add(mainframeBankingSystem); + + DeploymentNode liveWebServer = bigBankDataCenterForLive.AddDeploymentNode("bigbank-web***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 4, DictionaryUtils.Create("Location=London and Reading")); + ContainerInstance liveWebApplication = liveWebServer.AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")) + .Add(webApplication); + + DeploymentNode liveApiServer = bigBankDataCenterForLive.AddDeploymentNode("bigbank-api***", "A web server residing in the web server farm, accessed via F5 BIG-IP LTMs.", "Ubuntu 16.04 LTS", 8, DictionaryUtils.Create("Location=London and Reading")); + ContainerInstance liveApiApplication = liveApiServer.AddDeploymentNode("Apache Tomcat", "An open source Java EE web server.", "Apache Tomcat 8.x", 1, DictionaryUtils.Create("Xmx=512M", "Xms=1024M", "Java Version=8")) + .Add(apiApplication); + + DeploymentNode primaryDatabaseServer = bigBankDataCenterForLive + .AddDeploymentNode("bigbank-db01", "The primary database server.", "Ubuntu 16.04 LTS", 1, DictionaryUtils.Create("Location=London")) + .AddDeploymentNode("Oracle - Primary", "The primary, live database server.", "Oracle 12c"); + ContainerInstance livePrimaryDatabase = primaryDatabaseServer.Add(database); + + DeploymentNode bigBankdb02 = bigBankDataCenterForLive.AddDeploymentNode("bigbank-db02", "The secondary database server.", "Ubuntu 16.04 LTS", 1, DictionaryUtils.Create("Location=Reading")); + bigBankdb02.AddTags(FailoverTag); + DeploymentNode secondaryDatabaseServer = bigBankdb02.AddDeploymentNode("Oracle - Secondary", "A secondary, standby database server, used for failover purposes only.", "Oracle 12c"); + secondaryDatabaseServer.AddTags(FailoverTag); + ContainerInstance liveSecondaryDatabase = secondaryDatabaseServer.Add(database); + + model.Relationships.Where(r => r.Destination.Equals(liveSecondaryDatabase)).ToList().ForEach(r => r.AddTags(FailoverTag)); + Relationship dataReplicationRelationship = primaryDatabaseServer.Uses(secondaryDatabaseServer, "Replicates data to", ""); + liveSecondaryDatabase.AddTags(FailoverTag); + + // views/diagrams + SystemLandscapeView systemLandscapeView = views.CreateSystemLandscapeView("SystemLandscape", "The system landscape diagram for Big Bank plc."); + systemLandscapeView.AddAllElements(); + systemLandscapeView.PaperSize = PaperSize.A5_Landscape; + + SystemContextView systemContextView = views.CreateSystemContextView(internetBankingSystem, "SystemContext", "The system context diagram for the Internet Banking System."); + systemContextView.EnterpriseBoundaryVisible = false; + systemContextView.AddNearestNeighbours(internetBankingSystem); + systemContextView.PaperSize = PaperSize.A5_Landscape; + + ContainerView containerView = views.CreateContainerView(internetBankingSystem, "Containers", "The container diagram for the Internet Banking System."); + containerView.Add(customer); + containerView.AddAllContainers(); + containerView.Add(mainframeBankingSystem); + containerView.Add(emailSystem); + containerView.PaperSize = PaperSize.A5_Landscape; + + ComponentView componentView = views.CreateComponentView(apiApplication, "Components", "The component diagram for the API Application."); + componentView.Add(mobileApp); + componentView.Add(singlePageApplication); + componentView.Add(database); + componentView.AddAllComponents(); + componentView.Add(mainframeBankingSystem); + componentView.Add(emailSystem); + componentView.PaperSize = PaperSize.A5_Landscape; + + systemLandscapeView.AddAnimation(internetBankingSystem, customer, mainframeBankingSystem, emailSystem); + systemLandscapeView.AddAnimation(atm); + systemLandscapeView.AddAnimation(customerServiceStaff, backOfficeStaff); + + systemContextView.AddAnimation(internetBankingSystem); + systemContextView.AddAnimation(customer); + systemContextView.AddAnimation(mainframeBankingSystem); + systemContextView.AddAnimation(emailSystem); + + containerView.AddAnimation(customer, mainframeBankingSystem, emailSystem); + containerView.AddAnimation(webApplication); + containerView.AddAnimation(singlePageApplication); + containerView.AddAnimation(mobileApp); + containerView.AddAnimation(apiApplication); + containerView.AddAnimation(database); + + componentView.AddAnimation(singlePageApplication, mobileApp); + componentView.AddAnimation(signinController, securityComponent, database); + componentView.AddAnimation(accountsSummaryController, mainframeBankingSystemFacade, mainframeBankingSystem); + componentView.AddAnimation(resetPasswordController, emailComponent, database); + + // dynamic diagrams and deployment diagrams are not available with the Free Plan + DynamicView dynamicView = views.CreateDynamicView(apiApplication, "SignIn", "Summarises how the sign in feature works in the single-page application."); + dynamicView.Add(singlePageApplication, "Submits credentials to", signinController); + dynamicView.Add(signinController, "Validates credentials using", securityComponent); + dynamicView.Add(securityComponent, "select * from users where username = ?", database); + dynamicView.Add(database, "Returns user data to", securityComponent); + dynamicView.Add(securityComponent, "Returns true if the hashed password matches", signinController); + dynamicView.Add(signinController, "Sends back an authentication token to", singlePageApplication); + dynamicView.PaperSize = PaperSize.A5_Landscape; + + DeploymentView developmentDeploymentView = views.CreateDeploymentView(internetBankingSystem, "DevelopmentDeployment", "An example development deployment scenario for the Internet Banking System."); + developmentDeploymentView.Environment = "Development"; + developmentDeploymentView.Add(developerLaptop); + developmentDeploymentView.Add(bigBankDataCenterForDevelopment); + developmentDeploymentView.PaperSize = PaperSize.A5_Landscape; + + developmentDeploymentView.AddAnimation(developmentSinglePageApplication); + developmentDeploymentView.AddAnimation(developmentWebApplication, developmentApiApplication); + developmentDeploymentView.AddAnimation(developmentDatabase); + developmentDeploymentView.AddAnimation(developmentMainframeBankingSystem); + + DeploymentView liveDeploymentView = views.CreateDeploymentView(internetBankingSystem, "LiveDeployment", "An example live deployment scenario for the Internet Banking System."); + liveDeploymentView.Environment = "Live"; + liveDeploymentView.Add(bigBankDataCenterForLive); + liveDeploymentView.Add(customerMobileDevice); + liveDeploymentView.Add(customerComputer); + liveDeploymentView.Add(dataReplicationRelationship); + liveDeploymentView.PaperSize = PaperSize.A5_Landscape; + + liveDeploymentView.AddAnimation(liveSinglePageApplication); + liveDeploymentView.AddAnimation(liveMobileApp); + liveDeploymentView.AddAnimation(liveWebApplication, liveApiApplication); + liveDeploymentView.AddAnimation(livePrimaryDatabase); + liveDeploymentView.AddAnimation(liveSecondaryDatabase); + liveDeploymentView.AddAnimation(liveMainframeBankingSystem); + + // colours, shapes and other diagram styling + Styles styles = views.Configuration.Styles; + styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd", Color = "#ffffff" }); + styles.Add(new ElementStyle(Tags.Container) { Background = "#438dd5", Color = "#ffffff" }); + styles.Add(new ElementStyle(Tags.Component) { Background = "#85bbf0", Color = "#000000" }); + styles.Add(new ElementStyle(Tags.Person) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person, FontSize = 22 }); + styles.Add(new ElementStyle(ExistingSystemTag) { Background = "#999999", Color = "#ffffff" }); + styles.Add(new ElementStyle(BankStaffTag) { Background = "#999999", Color = "#ffffff" }); + styles.Add(new ElementStyle(WebBrowserTag) { Shape = Shape.WebBrowser }); + styles.Add(new ElementStyle(MobileAppTag) { Shape = Shape.MobileDeviceLandscape }); + styles.Add(new ElementStyle(DatabaseTag) { Shape = Shape.Cylinder }); + styles.Add(new ElementStyle(FailoverTag) { Opacity = 25 }); + styles.Add(new RelationshipStyle(FailoverTag) {Opacity = 25, Position = 70 }); + + // documentation + // - usually the documentation would be included from separate Markdown/AsciiDoc files, but this is just an example + StructurizrDocumentationTemplate template = new StructurizrDocumentationTemplate(workspace); + template.AddContextSection(internetBankingSystem, Format.Markdown, + "Here is some context about the Internet Banking System...\n" + + "![](embed:SystemLandscape)\n" + + "![](embed:SystemContext)\n" + + "### Internet Banking System\n...\n" + + "### Mainframe Banking System\n...\n"); + template.AddContainersSection(internetBankingSystem, Format.Markdown, + "Here is some information about the containers within the Internet Banking System...\n" + + "![](embed:Containers)\n" + + "### Web Application\n...\n" + + "### Database\n...\n"); + template.AddComponentsSection(webApplication, Format.Markdown, + "Here is some information about the API Application...\n" + + "![](embed:Components)\n" + + "### Sign in process\n" + + "Here is some information about the Sign In Controller, including how the sign in process works...\n" + + "![](embed:SignIn)"); + template.AddDevelopmentEnvironmentSection(internetBankingSystem, Format.AsciiDoc, + "Here is some information about how to set up a development environment for the Internet Banking System...\n" + + "image::embed:DevelopmentDeployment[]"); + template.AddDeploymentSection(internetBankingSystem, Format.AsciiDoc, + "Here is some information about the live deployment environment for the Internet Banking System...\n" + + "image::embed:LiveDeployment[]"); + + return workspace; } - + static void Main() { StructurizrClient structurizrClient = new StructurizrClient(ApiKey, ApiSecret); structurizrClient.PutWorkspace(WorkspaceId, Create()); } + } + } \ No newline at end of file diff --git a/Structurizr.Examples/Structurizr.Examples.csproj b/Structurizr.Examples/Structurizr.Examples.csproj index 6de67c5..29b95ca 100644 --- a/Structurizr.Examples/Structurizr.Examples.csproj +++ b/Structurizr.Examples/Structurizr.Examples.csproj @@ -1,12 +1,12 @@  Exe - netcoreapp1.1 + netcoreapp2.1 Structurizr.Examples.PlantUML false - + diff --git a/Structurizr.PlantUML.Tests/IO/C4PlantUML/C4PlantUmlWriterTests.cs b/Structurizr.PlantUML.Tests/IO/C4PlantUML/C4PlantUmlWriterTests.cs index 36fc4f1..22b43f4 100644 --- a/Structurizr.PlantUML.Tests/IO/C4PlantUML/C4PlantUmlWriterTests.cs +++ b/Structurizr.PlantUML.Tests/IO/C4PlantUML/C4PlantUmlWriterTests.cs @@ -29,14 +29,14 @@ public void test_writeBigBankPlcWorkspace() _workspace = BigBankPlc.Create(); AddLayoutDetails(_workspace); -/* - using (var writer = new StringWriter()) - { - new Structurizr.IO.Json.JsonWriter(true).Write(_workspace, writer); - var json = writer.GetStringBuilder().ToString(); - json = json; - } -*/ + /* + using (var writer = new StringWriter()) + { + new Structurizr.IO.Json.JsonWriter(true).Write(_workspace, writer); + var json = writer.GetStringBuilder().ToString(); + json = json; + } + */ _plantUMLWriter.Write(_workspace, _stringWriter); Assert.Equal( @@ -46,8 +46,6 @@ public void test_writeBigBankPlcWorkspace() ' Structurizr.SystemLandscapeView: SystemLandscape title System Landscape for Big Bank plc -LAYOUT_WITH_LEGEND() - Person_Ext(PersonalBankingCustomer__9bc576, ""Personal Banking Customer"", ""A customer of the bank, with personal bank accounts."") Enterprise_Boundary(BigBankplc, ""Big Bank plc"") { Person(BackOfficeStaff__5f761d, ""Back Office Staff"", ""Administration and support staff within the bank."") @@ -62,10 +60,12 @@ public void test_writeBigBankPlcWorkspace() Rel_Down(CustomerServiceStaff__a35be5, MainframeBankingSystem__f50ffa, ""Uses"") Rel_Up(EmailSystem__2908eb9, PersonalBankingCustomer__9bc576, ""Sends e-mails to"") Rel_Down(InternetBankingSystem__2aef74c, EmailSystem__2908eb9, ""Sends e-mail using"") -Rel_Down(InternetBankingSystem__2aef74c, MainframeBankingSystem__f50ffa, ""Uses"") +Rel_Down(InternetBankingSystem__2aef74c, MainframeBankingSystem__f50ffa, ""Gets account information from, and makes payments using"") Rel(PersonalBankingCustomer__9bc576, ATM__22fc739, ""Withdraws cash using"") Rel(PersonalBankingCustomer__9bc576, CustomerServiceStaff__a35be5, ""Asks questions to"", ""Telephone"") -Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__2aef74c, ""Uses"") +Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__2aef74c, ""Views account balances, and makes payments using"") + +SHOW_LEGEND() @enduml @startuml @@ -74,16 +74,16 @@ public void test_writeBigBankPlcWorkspace() ' Structurizr.SystemContextView: SystemContext title Internet Banking System - System Context -LAYOUT_WITH_LEGEND() - System(EmailSystem__2908eb9, ""E-mail System"", ""The internal Microsoft Exchange e-mail system."") System(InternetBankingSystem__2aef74c, ""Internet Banking System"", ""Allows customers to view information about their bank accounts, and make payments."") System(MainframeBankingSystem__f50ffa, ""Mainframe Banking System"", ""Stores all of the core banking information about customers, accounts, transactions, etc."") Person_Ext(PersonalBankingCustomer__9bc576, ""Personal Banking Customer"", ""A customer of the bank, with personal bank accounts."") Rel_Up(EmailSystem__2908eb9, PersonalBankingCustomer__9bc576, ""Sends e-mails to"") Rel_Right(InternetBankingSystem__2aef74c, EmailSystem__2908eb9, ""Sends e-mail using"") -Rel(InternetBankingSystem__2aef74c, MainframeBankingSystem__f50ffa, ""Uses"") -Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__2aef74c, ""Uses"") +Rel(InternetBankingSystem__2aef74c, MainframeBankingSystem__f50ffa, ""Gets account information from, and makes payments using"") +Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__2aef74c, ""Views account balances, and makes payments using"") + +SHOW_LEGEND() @enduml @startuml @@ -92,8 +92,6 @@ title Internet Banking System - System Context ' Structurizr.ContainerView: Containers title Internet Banking System - Containers -LAYOUT_WITH_LEGEND() - System(EmailSystem__2908eb9, ""E-mail System"", ""The internal Microsoft Exchange e-mail system."") System(MainframeBankingSystem__f50ffa, ""Mainframe Banking System"", ""Stores all of the core banking information about customers, accounts, transactions, etc."") Person_Ext(PersonalBankingCustomer__9bc576, ""Personal Banking Customer"", ""A customer of the bank, with personal bank accounts."") @@ -106,14 +104,16 @@ title Internet Banking System - Containers } Rel_Left(InternetBankingSystem__APIApplication__2c36bed, InternetBankingSystem__Database__18307f7, ""Reads from and writes to"", ""JDBC"") Rel_Up(InternetBankingSystem__APIApplication__2c36bed, EmailSystem__2908eb9, ""Sends e-mail using"", ""SMTP"") -Rel_Left(InternetBankingSystem__APIApplication__2c36bed, MainframeBankingSystem__f50ffa, ""Uses"", ""XML/HTTPS"") +Rel_Left(InternetBankingSystem__APIApplication__2c36bed, MainframeBankingSystem__f50ffa, ""Makes API calls to"", ""XML/HTTPS"") Rel_Up(EmailSystem__2908eb9, PersonalBankingCustomer__9bc576, ""Sends e-mails to"") Rel(InternetBankingSystem__MobileApp__38a070b, InternetBankingSystem__APIApplication__2c36bed, ""Makes API calls to"", ""JSON/HTTPS"") -Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__MobileApp__38a070b, ""Uses"") -Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__SinglePageApplication__1414c79, ""Uses"") -Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__WebApplication__1bb919c, ""Uses"", ""HTTPS"") +Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__MobileApp__38a070b, ""Views account balances, and makes payments using"") +Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__SinglePageApplication__1414c79, ""Views account balances, and makes payments using"") +Rel(PersonalBankingCustomer__9bc576, InternetBankingSystem__WebApplication__1bb919c, ""Visits bigbank.com/ib using"", ""HTTPS"") Rel(InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__2c36bed, ""Makes API calls to"", ""JSON/HTTPS"") Rel_Right(InternetBankingSystem__WebApplication__1bb919c, InternetBankingSystem__SinglePageApplication__1414c79, ""Delivers to the customer's web browser"") + +SHOW_LEGEND() @enduml @startuml @@ -122,8 +122,6 @@ title Internet Banking System - Containers ' Structurizr.ComponentView: Components title Internet Banking System - API Application - Components -LAYOUT_WITH_LEGEND() - ContainerDb(InternetBankingSystem__Database__18307f7, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") System(EmailSystem__2908eb9, ""E-mail System"", ""The internal Microsoft Exchange e-mail system."") System(MainframeBankingSystem__f50ffa, ""Mainframe Banking System"", ""Stores all of the core banking information about customers, accounts, transactions, etc."") @@ -150,346 +148,115 @@ title Internet Banking System - API Application - Components Rel(InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__AccountsSummaryController__3f81fb2, ""Makes API calls to"", ""JSON/HTTPS"") Rel(InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__ResetPasswordController__23f0eac, ""Makes API calls to"", ""JSON/HTTPS"") Rel(InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Makes API calls to"", ""JSON/HTTPS"") + +SHOW_LEGEND() @enduml @startuml -!include -' C4_Dynamic.puml is missing, simulate it with following definitions -' Scope: Interactions in an enterprise, software system or container. -' Primary and supporting elements: Depends on the diagram scope - -' enterprise - people and software systems related to the enterprise in scope -' software system - see system context or container diagrams, -' container - see component diagram. -' Intended audience: Technical and non-technical people, inside and outside of the software development team. - -' Dynamic diagram introduces (automatically) numbered interactions: -' Interact(): used automatic calculated index, -' Interact2(): index can be explicit defined, -' SetIndex(): set the next index, -' GetIndex(): get the index and automatically increase index - -' Index -' ################################## - -!function $inc_($value, $step=1) - !return $value + $step -!endfunction - -!$index=1 - -!function SetIndex($new_index) - !$index=$new_index -!endfunction - -!function GetIndex($auto_increase=1) - !$old = $index - !$index=$inc_($index, $auto_increase) - !return $old -!endfunction - -' Interact -' ################################## -!define Interact2(e_index, e_from, e_to, e_label) Rel(e_from, e_to, ""e_index: e_label"") -!define Interact2(e_index, e_from, e_to, e_label, e_techn) Rel(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Back(e_index, e_from, e_to, e_label) Rel_Back(e_from, e_to, ""e_index: e_label"") -!define Interact2_Back(e_index, e_from, e_to, e_label, e_techn) Rel_Back(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Neighbor(e_index, e_from, e_to, e_label) Rel_Neighbor(e_from, e_to, ""e_index: e_label"") -!define Interact2_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"") -!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_D(e_index, e_from, e_to, e_label) Rel_D(e_from, e_to, ""e_index: e_label"") -!define Interact2_D(e_index, e_from, e_to, e_label, e_techn) Rel_D(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Down(e_index, e_from, e_to, e_label) Rel_Down(e_from, e_to, ""e_index: e_label"") -!define Interact2_Down(e_index, e_from, e_to, e_label, e_techn) Rel_Down(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_U(e_index, e_from, e_to, e_label) Rel_U(e_from, e_to, ""e_index: e_label"") -!define Interact2_U(e_index, e_from, e_to, e_label, e_techn) Rel_U(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Up(e_index, e_from, e_to, e_label) Rel_Up(e_from, e_to, ""e_index: e_label"") -!define Interact2_Up(e_index, e_from, e_to, e_label, e_techn) Rel_Up(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_L(e_index, e_from, e_to, e_label) Rel_L(e_from, e_to, ""e_index: e_label"") -!define Interact2_L(e_index, e_from, e_to, e_label, e_techn) Rel_L(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Left(e_index, e_from, e_to, e_label) Rel_Left(e_from, e_to, ""e_index: e_label"") -!define Interact2_Left(e_index, e_from, e_to, e_label, e_techn) Rel_Left(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_R(e_index, e_from, e_to, e_label) Rel_R(e_from, e_to, ""e_index: e_label"") -!define Interact2_R(e_index, e_from, e_to, e_label, e_techn) Rel_R(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Right(e_index, e_from, e_to, e_label) Rel_Right(e_from, e_to, ""e_index: e_label"") -!define Interact2_Right(e_index, e_from, e_to, e_label, e_techn) Rel_Right(e_from, e_to, ""e_index: e_label"", e_techn) - -!unquoted function Interact($e_from, $e_to, $e_label) - Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact($e_from, $e_to, $e_label, $e_techn) - Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Back($e_from, $e_to, $e_label) - Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Back($e_from, $e_to, $e_label, $e_techn) - Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Neighbor($e_from, $e_to, $e_label) - Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Neighbor($e_from, $e_to, $e_label, $e_techn) - Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label) - Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label, $e_techn) - Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_D($e_from, $e_to, $e_label) - Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_D($e_from, $e_to, $e_label, $e_techn) - Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Down($e_from, $e_to, $e_label) - Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Down($e_from, $e_to, $e_label, $e_techn) - Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_U($e_from, $e_to, $e_label) - Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_U($e_from, $e_to, $e_label, $e_techn) - Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Up($e_from, $e_to, $e_label) - Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Up($e_from, $e_to, $e_label, $e_techn) - Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_L($e_from, $e_to, $e_label) - Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_L($e_from, $e_to, $e_label, $e_techn) - Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Left($e_from, $e_to, $e_label) - Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Left($e_from, $e_to, $e_label, $e_techn) - Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_R($e_from, $e_to, $e_label) - Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_R($e_from, $e_to, $e_label, $e_techn) - Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Right($e_from, $e_to, $e_label) - Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Right($e_from, $e_to, $e_label, $e_techn) - Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction +!include ' Structurizr.DynamicView: SignIn title API Application - Dynamic -LAYOUT_WITH_LEGEND() - ContainerDb(InternetBankingSystem__Database__18307f7, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") Container(InternetBankingSystem__SinglePageApplication__1414c79, ""Single-Page Application"", ""JavaScript and Angular"", ""Provides all of the Internet banking functionality to customers via their web browser."") Container_Boundary(InternetBankingSystem__APIApplication__2c36bed, ""API Application"") { Component(InternetBankingSystem__APIApplication__SecurityComponent__a4474, ""Security Component"", ""Spring Bean"", ""Provides functionality related to signing in, changing passwords, etc."") Component(InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Sign In Controller"", ""Spring MVC Rest Controller"", ""Allows users to sign in to the Internet Banking System."") } -Interact2_Right(""1"", InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Submits credentials to"", ""JSON/HTTPS"") -Interact2(""2"", InternetBankingSystem__APIApplication__SignInController__22cc62b, InternetBankingSystem__APIApplication__SecurityComponent__a4474, ""Calls isAuthenticated() on"") -Interact2_Right(""3"", InternetBankingSystem__APIApplication__SecurityComponent__a4474, InternetBankingSystem__Database__18307f7, ""select * from users where username = ?"", ""JDBC"") +RelIndex_Right(""1"", InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Submits credentials to"", ""JSON/HTTPS"") +RelIndex(""2"", InternetBankingSystem__APIApplication__SignInController__22cc62b, InternetBankingSystem__APIApplication__SecurityComponent__a4474, ""Validates credentials using"") +RelIndex_Right(""3"", InternetBankingSystem__APIApplication__SecurityComponent__a4474, InternetBankingSystem__Database__18307f7, ""select * from users where username = ?"", ""JDBC"") +RelIndex_Left(""4"", InternetBankingSystem__Database__18307f7, InternetBankingSystem__APIApplication__SecurityComponent__a4474, ""Returns user data to"", ""JDBC"") +RelIndex(""5"", InternetBankingSystem__APIApplication__SecurityComponent__a4474, InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Returns true if the hashed password matches"") +RelIndex_Left(""6"", InternetBankingSystem__APIApplication__SignInController__22cc62b, InternetBankingSystem__SinglePageApplication__1414c79, ""Sends back an authentication token to"", ""JSON/HTTPS"") + +SHOW_LEGEND() @enduml @startuml -!include -' C4_Deployment.puml is missing, simulate it with following definitions -' Scope: A single software system. -' Primary elements: Deployment nodes and containers within the software system in scope. -' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff. - -' Colors -' ################################## -!define NODE_FONT_COLOR #444444 -!define NODE_BG_COLOR #FFFFFF - -' Styling -' ################################## - -skinparam rectangle<> { - Shadowing false - StereotypeFontSize 0 - FontColor NODE_FONT_COLOR - BackgroundColor NODE_BG_COLOR - BorderColor #444444 -} - -' Layout -' ################################## - -!definelong LAYOUT_WITH_LEGEND -hide stereotype -legend right -|= |= Type | -| | deployment node | -| | deployment container | -endlegend -!enddefinelong - -' Nodes -' ################################## -' PlantUML does not support automatic line breaks of container, if e_techn is very long insert line breaks with -' ""\n"" -!define Node(e_alias, e_label, e_techn) rectangle ""==e_label\n[e_techn]"" <> as e_alias +!include ' Structurizr.DeploymentView: DevelopmentDeployment -title Internet Banking System - Deployment - -LAYOUT_WITH_LEGEND() +title Internet Banking System - Deployment - Development -Node(Deployment__Development__DeveloperLaptop__389f399, ""Developer Laptop"", ""Microsoft Windows 10 or Apple \nmacOS"") { - Node(Deployment__Development__DeveloperLaptop__DockerContainerWebServer__1b73d2e, ""Docker Container - Web Server"", ""Docker"") { - Node(Deployment__Development__DeveloperLaptop__DockerContainerWebServer__ApacheTomcat__1cc9f55, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { +Node(BigBankplc__13491b2, ""Big Bank plc"", ""Big Bank plc data center"") { + Node(bigbankdev001__2f4813f, ""bigbank-dev001"") { + System(MainframeBankingSystem1__1b2a42c, ""Mainframe Banking System"", ""Stores all of the core banking information about customers, accounts, transactions, etc."") + } +} +Node(DeveloperLaptop__389f399, ""Developer Laptop"", ""Microsoft Windows 10 or Apple macOS"") { + Node(DockerContainerWebServer__1b73d2e, ""Docker Container - Web Server"", ""Docker"") { + Node(ApacheTomcat__1cc9f55, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { Container(InternetBankingSystem__WebApplication1__28f79f6, ""Web Application"", ""Java and Spring MVC"", ""Delivers the static content and the Internet banking single page application."") Container(InternetBankingSystem__APIApplication1__1f227f4, ""API Application"", ""Java and Spring MVC"", ""Provides Internet banking functionality via a JSON/HTTPS API."") } } - Node(Deployment__Development__DeveloperLaptop__DockerContainerDatabaseServer__2eae566, ""Docker Container - Database Server"", ""Docker"") { - Node(Deployment__Development__DeveloperLaptop__DockerContainerDatabaseServer__DatabaseServer__24d13de, ""Database Server"", ""Oracle 12c"") { + Node(DockerContainerDatabaseServer__2eae566, ""Docker Container - Database Server"", ""Docker"") { + Node(DatabaseServer__24d13de, ""Database Server"", ""Oracle 12c"") { ContainerDb(InternetBankingSystem__Database1__3296ca6, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") } } - Node(Deployment__Development__DeveloperLaptop__WebBrowser__3930fd, ""Web Browser"", ""Google Chrome, Mozilla \nFirefox, Apple Safari or \nMicrosoft Edge"") { + Node(WebBrowser__3930fd, ""Web Browser"", ""Chrome, Firefox, Safari, or Edge"") { Container(InternetBankingSystem__SinglePageApplication1__bbe85d, ""Single-Page Application"", ""JavaScript and Angular"", ""Provides all of the Internet banking functionality to customers via their web browser."") } } Rel(InternetBankingSystem__APIApplication1__1f227f4, InternetBankingSystem__Database1__3296ca6, ""Reads from and writes to"", ""JDBC"") +Rel(InternetBankingSystem__APIApplication1__1f227f4, MainframeBankingSystem1__1b2a42c, ""Makes API calls to"", ""XML/HTTPS"") Rel(InternetBankingSystem__SinglePageApplication1__bbe85d, InternetBankingSystem__APIApplication1__1f227f4, ""Makes API calls to"", ""JSON/HTTPS"") Rel_Up(InternetBankingSystem__WebApplication1__28f79f6, InternetBankingSystem__SinglePageApplication1__bbe85d, ""Delivers to the customer's web browser"") + +SHOW_LEGEND() @enduml @startuml -!include -' C4_Deployment.puml is missing, simulate it with following definitions -' Scope: A single software system. -' Primary elements: Deployment nodes and containers within the software system in scope. -' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff. - -' Colors -' ################################## -!define NODE_FONT_COLOR #444444 -!define NODE_BG_COLOR #FFFFFF - -' Styling -' ################################## - -skinparam rectangle<> { - Shadowing false - StereotypeFontSize 0 - FontColor NODE_FONT_COLOR - BackgroundColor NODE_BG_COLOR - BorderColor #444444 -} - -' Layout -' ################################## - -!definelong LAYOUT_WITH_LEGEND -hide stereotype -legend right -|= |= Type | -| | deployment node | -| | deployment container | -endlegend -!enddefinelong - -' Nodes -' ################################## -' PlantUML does not support automatic line breaks of container, if e_techn is very long insert line breaks with -' ""\n"" -!define Node(e_alias, e_label, e_techn) rectangle ""==e_label\n[e_techn]"" <> as e_alias +!include ' Structurizr.DeploymentView: LiveDeployment -title Internet Banking System - Deployment - -LAYOUT_WITH_LEGEND() +title Internet Banking System - Deployment - Live -Node(Deployment__Live__BigBankplc__3ffe15e, ""Big Bank plc"", ""Big Bank plc data center"") { - Node(Deployment__Live__BigBankplc__bigbankweb***__3f92e18, ""bigbank-web*** (x4)"", ""Ubuntu 16.04 LTS"") { - Node(Deployment__Live__BigBankplc__bigbankweb***__ApacheTomcat__27b4383, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { - Container(InternetBankingSystem__WebApplication2__1720850, ""Web Application"", ""Java and Spring MVC"", ""Delivers the static content and the Internet banking single page application."") +Node(BigBankplc__3ffe15e, ""Big Bank plc"", ""Big Bank plc data center"") { + Node(bigbankprod001__f5e94d, ""bigbank-prod001"") { + System(MainframeBankingSystem1__3db6dd2, ""Mainframe Banking System"", ""Stores all of the core banking information about customers, accounts, transactions, etc."") + } + Node(bigbankweb***__3f92e18, ""bigbank-web*** (x4)"", ""Ubuntu 16.04 LTS"") { + Node(ApacheTomcat__27b4383, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { + Container(InternetBankingSystem__WebApplication1__1720850, ""Web Application"", ""Java and Spring MVC"", ""Delivers the static content and the Internet banking single page application."") } } - Node(Deployment__Live__BigBankplc__bigbankapi***__263d9e8, ""bigbank-api*** (x8)"", ""Ubuntu 16.04 LTS"") { - Node(Deployment__Live__BigBankplc__bigbankapi***__ApacheTomcat__3b84ab, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { - Container(InternetBankingSystem__APIApplication2__1408a33, ""API Application"", ""Java and Spring MVC"", ""Provides Internet banking functionality via a JSON/HTTPS API."") + Node(bigbankapi***__263d9e8, ""bigbank-api*** (x8)"", ""Ubuntu 16.04 LTS"") { + Node(ApacheTomcat__3b84ab, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { + Container(InternetBankingSystem__APIApplication1__1408a33, ""API Application"", ""Java and Spring MVC"", ""Provides Internet banking functionality via a JSON/HTTPS API."") } } - Node(Deployment__Live__BigBankplc__bigbankdb01__35ec592, ""bigbank-db01"", ""Ubuntu 16.04 LTS"") { - Node(Deployment__Live__BigBankplc__bigbankdb01__OraclePrimary__19fd8f, ""Oracle - Primary"", ""Oracle 12c"") { - ContainerDb(InternetBankingSystem__Database2__1c974ec, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") + Node(bigbankdb01__35ec592, ""bigbank-db01"", ""Ubuntu 16.04 LTS"") { + Node(OraclePrimary__19fd8f, ""Oracle - Primary"", ""Oracle 12c"") { + ContainerDb(InternetBankingSystem__Database1__1c974ec, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") } } - Node(Deployment__Live__BigBankplc__bigbankdb02__1db08a2, ""bigbank-db02"", ""Ubuntu 16.04 LTS"") { - Node(Deployment__Live__BigBankplc__bigbankdb02__OracleSecondary__1c4ec22, ""Oracle - Secondary"", ""Oracle 12c"") { - ContainerDb(InternetBankingSystem__Database3__d89394, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") + Node(bigbankdb02__1db08a2, ""bigbank-db02"", ""Ubuntu 16.04 LTS"") { + Node(OracleSecondary__1c4ec22, ""Oracle - Secondary"", ""Oracle 12c"") { + ContainerDb(InternetBankingSystem__Database1__d89394, ""Database"", ""Relational Database Schema"", ""Stores user registration information, hashed authentication credentials, access logs, etc."") } } } -Node(Deployment__Live__Customer'scomputer__2510bf3, ""Customer's computer"", ""Microsoft Windows or Apple \nmacOS"") { - Node(Deployment__Live__Customer'scomputer__WebBrowser__ba951, ""Web Browser"", ""Google Chrome, Mozilla \nFirefox, Apple Safari or \nMicrosoft Edge"") { - Container(InternetBankingSystem__SinglePageApplication2__298b31c, ""Single-Page Application"", ""JavaScript and Angular"", ""Provides all of the Internet banking functionality to customers via their web browser."") +Node(Customer'scomputer__2510bf3, ""Customer's computer"", ""Microsoft Windows or Apple macOS"") { + Node(WebBrowser__ba951, ""Web Browser"", ""Chrome, Firefox, Safari, or Edge"") { + Container(InternetBankingSystem__SinglePageApplication1__298b31c, ""Single-Page Application"", ""JavaScript and Angular"", ""Provides all of the Internet banking functionality to customers via their web browser."") } } -Node(Deployment__Live__Customer'smobiledevice__1d6bcb6, ""Customer's mobile device"", ""Apple iOS or Android"") { +Node(Customer'smobiledevice__1d6bcb6, ""Customer's mobile device"", ""Apple iOS or Android"") { Container(InternetBankingSystem__MobileApp1__d004b3, ""Mobile App"", ""Xamarin"", ""Provides a limited subset of the Internet banking functionality to customers via their mobile device."") } -Rel(InternetBankingSystem__APIApplication2__1408a33, InternetBankingSystem__Database2__1c974ec, ""Reads from and writes to"", ""JDBC"") -Rel(InternetBankingSystem__APIApplication2__1408a33, InternetBankingSystem__Database3__d89394, ""Reads from and writes to"", ""JDBC"") -Rel(InternetBankingSystem__MobileApp1__d004b3, InternetBankingSystem__APIApplication2__1408a33, ""Makes API calls to"", ""JSON/HTTPS"") -Rel(InternetBankingSystem__SinglePageApplication2__298b31c, InternetBankingSystem__APIApplication2__1408a33, ""Makes API calls to"", ""JSON/HTTPS"") -Rel_Up(InternetBankingSystem__WebApplication2__1720850, InternetBankingSystem__SinglePageApplication2__298b31c, ""Delivers to the customer's web browser"") -Rel_Left(Deployment__Live__BigBankplc__bigbankdb01__OraclePrimary__19fd8f, Deployment__Live__BigBankplc__bigbankdb02__OracleSecondary__1c4ec22, ""Replicates data to"") +Rel(InternetBankingSystem__APIApplication1__1408a33, InternetBankingSystem__Database1__1c974ec, ""Reads from and writes to"", ""JDBC"") +Rel(InternetBankingSystem__APIApplication1__1408a33, InternetBankingSystem__Database1__d89394, ""Reads from and writes to"", ""JDBC"") +Rel(InternetBankingSystem__APIApplication1__1408a33, MainframeBankingSystem1__3db6dd2, ""Makes API calls to"", ""XML/HTTPS"") +Rel(InternetBankingSystem__MobileApp1__d004b3, InternetBankingSystem__APIApplication1__1408a33, ""Makes API calls to"", ""JSON/HTTPS"") +Rel_Left(OraclePrimary__19fd8f, OracleSecondary__1c4ec22, ""Replicates data to"") +Rel(InternetBankingSystem__SinglePageApplication1__298b31c, InternetBankingSystem__APIApplication1__1408a33, ""Makes API calls to"", ""JSON/HTTPS"") +Rel_Up(InternetBankingSystem__WebApplication1__1720850, InternetBankingSystem__SinglePageApplication1__298b31c, ""Delivers to the customer's web browser"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -504,9 +271,9 @@ private void AddLayoutDetails(Workspace workspace) // all SystemLandscapeView, SystemContext, ... (update relation): // Rel_Up(EmailSystem, PersonalBankingCustomer, ""Sends e-mails to"") // Rel_Right(InternetBankingSystem, EmailSystem, ""Sends e-mail using"") - var emailSystem = workspace.Model.GetElementWithCanonicalName("/E-mail System"); - var personalBankingCustomer = workspace.Model.GetElementWithCanonicalName("/Personal Banking Customer"); - var internetBankingSystem = workspace.Model.GetElementWithCanonicalName("/Internet Banking System"); + var emailSystem = workspace.Model.GetElementWithCanonicalOrStaticalName("SoftwareSystem://E-mail System"); + var personalBankingCustomer = workspace.Model.GetElementWithCanonicalOrStaticalName("Person://Personal Banking Customer"); + var internetBankingSystem = workspace.Model.GetElementWithCanonicalOrStaticalName("SoftwareSystem://Internet Banking System"); var systemLandscapeView = workspace.Views.SystemLandscapeViews.First(); systemLandscapeView.Relationships @@ -519,7 +286,7 @@ private void AddLayoutDetails(Workspace workspace) .SetDirection(DirectionValues.Right); // but only SystemLandscapeView should use Down relations, therefore add the tags relation view specific (via AddViewTags) - var mainframeBankingSystem = workspace.Model.GetElementWithCanonicalName("/Mainframe Banking System"); + var mainframeBankingSystem = workspace.Model.GetElementWithCanonicalOrStaticalName("SoftwareSystem://Mainframe Banking System"); foreach (var relationshipView in systemLandscapeView.Relationships .Where(rv => rv.Relationship.DestinationId == mainframeBankingSystem.Id)) { @@ -534,31 +301,41 @@ private void AddLayoutDetails(Workspace workspace) .SetDirection(DirectionValues.Down); // DynamicView - // Rel_Right(InternetBankingSystem__SinglePageApplication, InternetBankingSystem__APIApplication__SignInController, ...) - // Rel_Right(InternetBankingSystem__APIApplication__SecurityComponent, InternetBankingSystem__Database, ...) + // RelIndex_Right(""1"", InternetBankingSystem__SinglePageApplication__1414c79, InternetBankingSystem__APIApplication__SignInController__22cc62b, ""Submits credentials to"", ""JSON/HTTPS"") + // RelIndex_Right(""3"", InternetBankingSystem__APIApplication__SecurityComponent__a4474, InternetBankingSystem__Database__18307f7, ""select * from users where username = ?"", ""JDBC"") + // Response switch displayed order - RelIndex_Left(""4"", InternetBankingSystem__Database__18307f7, InternetBankingSystem__APIApplication__SecurityComponent__a4474, ""Returns user data to"", ""JDBC"") + // Response switch displayed order - RelIndex_Left(""6"", InternetBankingSystem__APIApplication__SignInController__22cc62b, InternetBankingSystem__SinglePageApplication__1414c79, ""Sends back an authentication token to"", ""JSON/HTTPS"") var singlePageApplication = - workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Single-Page Application"); + workspace.Model.GetElementWithCanonicalOrStaticalName("Container://Internet Banking System.Single-Page Application"); var signInController = - workspace.Model.GetElementWithCanonicalName("/Internet Banking System/API Application/Sign In Controller"); + workspace.Model.GetElementWithCanonicalOrStaticalName("Component://Internet Banking System.API Application.Sign In Controller"); var securityComponent = - workspace.Model.GetElementWithCanonicalName("/Internet Banking System/API Application/Security Component"); - var database = workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Database") as Container; + workspace.Model.GetElementWithCanonicalOrStaticalName("Component://Internet Banking System.API Application.Security Component"); + var database = workspace.Model.GetElementWithCanonicalOrStaticalName("Container://Internet Banking System.Database") as Container; database.SetIsDatabase(true); var dynamicView = workspace.Views.DynamicViews.First(); dynamicView.Relationships - .First(r => r.Relationship.SourceId == singlePageApplication.Id && - r.Relationship.DestinationId == signInController.Id) + .First(r => + !(r.Response ?? false) && r.Relationship.SourceId == securityComponent.Id && r.Relationship.DestinationId == database.Id) .SetDirection(DirectionValues.Right); dynamicView.Relationships - .First(r => r.Relationship.SourceId == securityComponent.Id && r.Relationship.DestinationId == database.Id) + .First(r => + !(r.Response ?? false) && r.Relationship.SourceId == singlePageApplication.Id && r.Relationship.DestinationId == signInController.Id) .SetDirection(DirectionValues.Right); + // response swaps display order + dynamicView.Relationships + .First(r => + (r.Response ?? false) && r.Relationship.SourceId == securityComponent.Id && r.Relationship.DestinationId == database.Id) + .SetDirection(DirectionValues.Left); + dynamicView.Relationships + .First(r => + (r.Response ?? false) && r.Relationship.SourceId == singlePageApplication.Id && r.Relationship.DestinationId == signInController.Id) + .SetDirection(DirectionValues.Left); // ContainerView // Rel_Up(InternetBankingSystem__WebApplication, InternetBankingSystem__SinglePageApplication, "Delivers to the customer's web browser") - var apiApplication = workspace.Model.GetElementWithCanonicalName("/Internet Banking System/API Application"); - var webApplication = workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Web Application"); - - + var apiApplication = workspace.Model.GetElementWithCanonicalOrStaticalName("Container://Internet Banking System.API Application"); + var webApplication = workspace.Model.GetElementWithCanonicalOrStaticalName("Container://Internet Banking System.Web Application"); var containerView = workspace.Views.ContainerViews.First(); containerView.Relationships .First(r => r.Relationship.SourceId == apiApplication.Id && @@ -584,29 +361,34 @@ private void AddLayoutDetails(Workspace workspace) // DeploymentView´(with already copied relations): DevelopmentDeployment, LiveDeployment // Rel_Up(InternetBankingSystem__WebApplication1, InternetBankingSystem__SinglePageApplication1, "Delivers to the customer's web browser") // Rel_Up(InternetBankingSystem__WebApplication2, InternetBankingSystem__SinglePageApplication2, "Delivers to the customer's web browser") - // Rel_Left(Deployment__Live__BigBankplc__bigbankdb01__OraclePrimary, Deployment__Live__BigBankplc__bigbankdb02__OracleSecondary, "Replicates data to") - var webApplication1 = workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Web Application[1]"); - var webApplication2 = workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Web Application[2]"); - var singlePageApplication1 = - workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Single-Page Application[1]"); - var singlePageApplication2 = - workspace.Model.GetElementWithCanonicalName("/Internet Banking System/Single-Page Application[2]"); - var oraclePrimary = - workspace.Model.GetElementWithCanonicalName("/Deployment/Live/Big Bank plc/bigbank-db01/Oracle - Primary"); - var oracleSecondary = - workspace.Model.GetElementWithCanonicalName("/Deployment/Live/Big Bank plc/bigbank-db02/Oracle - Secondary"); + // Rel_Left(Live__BigBankplc__bigbankdb01__OraclePrimary, Live__BigBankplc__bigbankdb02__OracleSecondary, "Replicates data to") + + // Model is changed that instances are counted per parent orig ...[2] cannot be used anymore, separate per view, full names have to be used + var developmentWebApplication = + workspace.Model.GetElementWithCanonicalOrStaticalName("ContainerInstance://Development/Developer Laptop/Docker Container - Web Server/Apache Tomcat/Internet Banking System.Web Application[1]"); + var developmentSinglePageApplication = + workspace.Model.GetElementWithCanonicalOrStaticalName("ContainerInstance://Development/Developer Laptop/Web Browser/Internet Banking System.Single-Page Application[1]"); var developmentDeploymentView = workspace.Views.DeploymentViews.First(); - var liveDeploymentView = workspace.Views.DeploymentViews.Last(); developmentDeploymentView.Relationships - .First(r => r.Relationship.SourceId == webApplication1.Id && - r.Relationship.DestinationId == singlePageApplication1.Id) + .First(r => r.Relationship.SourceId == developmentWebApplication.Id && + r.Relationship.DestinationId == developmentSinglePageApplication.Id) .SetDirection(DirectionValues.Up); + + var liveWebApplication = + workspace.Model.GetElementWithCanonicalOrStaticalName("ContainerInstance://Live/Big Bank plc/bigbank-web***/Apache Tomcat/Internet Banking System.Web Application[1]"); + var liveSinglePageApplication = + workspace.Model.GetElementWithCanonicalOrStaticalName("ContainerInstance://Live/Customer's computer/Web Browser/Internet Banking System.Single-Page Application[1]"); + var liveOraclePrimary = + workspace.Model.GetElementWithCanonicalOrStaticalName("DeploymentNode://Live/Big Bank plc/bigbank-db01/Oracle - Primary"); + var liveOracleSecondary = + workspace.Model.GetElementWithCanonicalOrStaticalName("DeploymentNode://Live/Big Bank plc/bigbank-db02/Oracle - Secondary"); + var liveDeploymentView = workspace.Views.DeploymentViews.Last(); liveDeploymentView.Relationships - .First(r => r.Relationship.SourceId == webApplication2.Id && - r.Relationship.DestinationId == singlePageApplication2.Id) + .First(r => r.Relationship.SourceId == liveWebApplication.Id && + r.Relationship.DestinationId == liveSinglePageApplication.Id) .SetDirection(DirectionValues.Up); liveDeploymentView.Relationships - .First(r => r.Relationship.SourceId == oraclePrimary.Id && r.Relationship.DestinationId == oracleSecondary.Id) + .First(r => r.Relationship.SourceId == liveOraclePrimary.Id && r.Relationship.DestinationId == liveOracleSecondary.Id) .SetDirection(DirectionValues.Left); } @@ -615,17 +397,15 @@ public void test_writeWorkspace_WithCustomBaseUrl() { PopulateWorkspace(); - _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/"; + _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/"; _plantUMLWriter.Write(_workspace, _stringWriter); Assert.Equal( @"@startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Context.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Context.puml ' Structurizr.SystemLandscapeView: enterpriseContext title System Landscape for Some Enterprise -LAYOUT_WITH_LEGEND() - System_Ext(EmailSystem__1127701, ""E-mail System"") Enterprise_Boundary(SomeEnterprise, ""Some Enterprise"") { Person(User__387cc75, ""User"") @@ -634,16 +414,16 @@ public void test_writeWorkspace_WithCustomBaseUrl() Rel(EmailSystem__1127701, User__387cc75, ""Delivers e-mails to"") Rel(SoftwareSystem__31d545b, EmailSystem__1127701, ""Sends e-mail using"") Rel(User__387cc75, SoftwareSystem__31d545b, ""Uses"") + +SHOW_LEGEND() @enduml @startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Context.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Context.puml ' Structurizr.SystemContextView: systemContext title Software System - System Context -LAYOUT_WITH_LEGEND() - Enterprise_Boundary(SomeEnterprise, ""Some Enterprise"") { System_Ext(EmailSystem__1127701, ""E-mail System"") System(SoftwareSystem__31d545b, ""Software System"") @@ -652,16 +432,16 @@ title Software System - System Context Rel(SoftwareSystem__31d545b, EmailSystem__1127701, ""Sends e-mail using"") Rel(User__387cc75, SoftwareSystem__31d545b, ""Uses"") } + +SHOW_LEGEND() @enduml @startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Container.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Container.puml ' Structurizr.ContainerView: containers title Software System - Containers -LAYOUT_WITH_LEGEND() - System_Ext(EmailSystem__1127701, ""E-mail System"") Person(User__387cc75, ""User"") System_Boundary(SoftwareSystem__31d545b, ""Software System"") { @@ -672,16 +452,16 @@ title Software System - Containers Rel(User__387cc75, SoftwareSystem__WebApplication__d2a342, """", ""HTTP"") Rel(SoftwareSystem__WebApplication__d2a342, SoftwareSystem__Database__39bccb8, ""Reads from and writes to"", ""JDBC"") Rel(SoftwareSystem__WebApplication__d2a342, EmailSystem__1127701, ""Sends e-mail using"") + +SHOW_LEGEND() @enduml @startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Component.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Component.puml ' Structurizr.ComponentView: components title Software System - Web Application - Components -LAYOUT_WITH_LEGEND() - ContainerDb(SoftwareSystem__Database__39bccb8, ""Database"", ""Relational Database Schema"", ""Stores information"") System_Ext(EmailSystem__1127701, ""E-mail System"") Person(User__387cc75, ""User"") @@ -696,46 +476,48 @@ title Software System - Web Application - Components Rel(SoftwareSystem__WebApplication__SomeController__341621c, SoftwareSystem__WebApplication__SomeRepository__6d9009, ""Uses"") Rel(SoftwareSystem__WebApplication__SomeRepository__6d9009, SoftwareSystem__Database__39bccb8, ""Reads from and writes to"", ""JDBC"") Rel(User__387cc75, SoftwareSystem__WebApplication__SomeController__341621c, ""Uses"", ""HTTP"") + +SHOW_LEGEND() @enduml @startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Dynamic.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Dynamic.puml ' Structurizr.DynamicView: dynamic title Web Application - Dynamic -LAYOUT_WITH_LEGEND() - ContainerDb(SoftwareSystem__Database__39bccb8, ""Database"", ""Relational Database Schema"", ""Stores information"") Person(User__387cc75, ""User"") Container_Boundary(SoftwareSystem__WebApplication__d2a342, ""Web Application"") { Component(SoftwareSystem__WebApplication__SomeController__341621c, ""SomeController"", ""Spring MVC Controller"") Component(SoftwareSystem__WebApplication__SomeRepository__6d9009, ""SomeRepository"", ""Spring Data"") } -Interact2(""1"", User__387cc75, SoftwareSystem__WebApplication__SomeController__341621c, ""Requests /something"", ""HTTP"") -Interact2(""2"", SoftwareSystem__WebApplication__SomeController__341621c, SoftwareSystem__WebApplication__SomeRepository__6d9009, """") -Interact2(""3"", SoftwareSystem__WebApplication__SomeRepository__6d9009, SoftwareSystem__Database__39bccb8, ""select * from something"", ""JDBC"") +RelIndex(""1"", User__387cc75, SoftwareSystem__WebApplication__SomeController__341621c, ""Requests /something"", ""HTTP"") +RelIndex(""2"", SoftwareSystem__WebApplication__SomeController__341621c, SoftwareSystem__WebApplication__SomeRepository__6d9009, """") +RelIndex(""3"", SoftwareSystem__WebApplication__SomeRepository__6d9009, SoftwareSystem__Database__39bccb8, ""select * from something"", ""JDBC"") + +SHOW_LEGEND() @enduml @startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Deployment.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Deployment.puml ' Structurizr.DeploymentView: deployment -title Software System - Deployment +title Software System - Deployment - Default -LAYOUT_WITH_LEGEND() - -Node(Deployment__Default__DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__DatabaseServer__MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { +Node(DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { + Node(MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { ContainerDb(SoftwareSystem__Database1__bb9c73, ""Database"", ""Relational Database Schema"", ""Stores information"") } } -Node(Deployment__Default__WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__WebServer__ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { +Node(WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { + Node(ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { Container(SoftwareSystem__WebApplication1__31f1f25, ""Web Application"", ""Java and spring MVC"", ""Delivers content"") } } Rel(SoftwareSystem__WebApplication1__31f1f25, SoftwareSystem__Database1__bb9c73, ""Reads from and writes to"", ""JDBC"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -756,8 +538,6 @@ public void test_writeEnterpriseContextView() ' Structurizr.SystemLandscapeView: enterpriseContext title System Landscape for Some Enterprise -LAYOUT_WITH_LEGEND() - System_Ext(EmailSystem__1934cbe, ""E-mail System"") Enterprise_Boundary(SomeEnterprise, ""Some Enterprise"") { Person(User__3b843b5, ""User"") @@ -766,6 +546,8 @@ public void test_writeEnterpriseContextView() Rel(EmailSystem__1934cbe, User__3b843b5, ""Delivers e-mails to"") Rel(SoftwareSystem__7134f, EmailSystem__1934cbe, ""Sends e-mail using"") Rel(User__3b843b5, SoftwareSystem__7134f, ""Uses"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -778,18 +560,16 @@ public void test_writeEnterpriseContextView_WithCustomBaseUrl() PopulateWorkspace(); SystemLandscapeView systemLandscapeView = _workspace.Views.SystemLandscapeViews.First(); - _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/"; + _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/"; _plantUMLWriter.Write(systemLandscapeView, _stringWriter); Assert.Equal( @"@startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Context.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Context.puml ' Structurizr.SystemLandscapeView: enterpriseContext title System Landscape for Some Enterprise -LAYOUT_WITH_LEGEND() - System_Ext(EmailSystem__1934cbe, ""E-mail System"") Enterprise_Boundary(SomeEnterprise, ""Some Enterprise"") { Person(User__3b843b5, ""User"") @@ -798,6 +578,8 @@ public void test_writeEnterpriseContextView_WithCustomBaseUrl() Rel(EmailSystem__1934cbe, User__3b843b5, ""Delivers e-mails to"") Rel(SoftwareSystem__7134f, EmailSystem__1934cbe, ""Sends e-mail using"") Rel(User__3b843b5, SoftwareSystem__7134f, ""Uses"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -819,8 +601,6 @@ public void test_writeSystemContextView() ' Structurizr.SystemContextView: systemContext title Software System - System Context -LAYOUT_WITH_LEGEND() - Enterprise_Boundary(SomeEnterprise, ""Some Enterprise"") { System_Ext(EmailSystem__1127701, ""E-mail System"") System(SoftwareSystem__31d545b, ""Software System"") @@ -829,6 +609,8 @@ title Software System - System Context Rel(SoftwareSystem__31d545b, EmailSystem__1127701, ""Sends e-mail using"") Rel(User__387cc75, SoftwareSystem__31d545b, ""Uses"") } + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -849,8 +631,6 @@ public void test_writeContainerView() ' Structurizr.ContainerView: containers title Software System - Containers -LAYOUT_WITH_LEGEND() - System_Ext(EmailSystem__1934cbe, ""E-mail System"") Person(User__3b843b5, ""User"") System_Boundary(SoftwareSystem__7134f, ""Software System"") { @@ -861,6 +641,8 @@ title Software System - Containers Rel(User__3b843b5, SoftwareSystem__WebApplication__1cc1659, """", ""HTTP"") Rel(SoftwareSystem__WebApplication__1cc1659, SoftwareSystem__Database__270f9f2, ""Reads from and writes to"", ""JDBC"") Rel(SoftwareSystem__WebApplication__1cc1659, EmailSystem__1934cbe, ""Sends e-mail using"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -881,8 +663,6 @@ public void test_writeComponentsView() ' Structurizr.ComponentView: components title Software System - Web Application - Components -LAYOUT_WITH_LEGEND() - ContainerDb(SoftwareSystem__Database__270f9f2, ""Database"", ""Relational Database Schema"", ""Stores information"") System_Ext(EmailSystem__1934cbe, ""E-mail System"") Person(User__3b843b5, ""User"") @@ -897,6 +677,8 @@ title Software System - Web Application - Components Rel(SoftwareSystem__WebApplication__SomeController__327a713, SoftwareSystem__WebApplication__SomeRepository__23f6823, ""Uses"") Rel(SoftwareSystem__WebApplication__SomeRepository__23f6823, SoftwareSystem__Database__270f9f2, ""Reads from and writes to"", ""JDBC"") Rel(User__3b843b5, SoftwareSystem__WebApplication__SomeController__327a713, ""Uses"", ""HTTP"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -913,192 +695,22 @@ public void test_writeDynamicView() // Dynamic diagrams can be drawn with Components Assert.Equal( @"@startuml -!include -' C4_Dynamic.puml is missing, simulate it with following definitions -' Scope: Interactions in an enterprise, software system or container. -' Primary and supporting elements: Depends on the diagram scope - -' enterprise - people and software systems related to the enterprise in scope -' software system - see system context or container diagrams, -' container - see component diagram. -' Intended audience: Technical and non-technical people, inside and outside of the software development team. - -' Dynamic diagram introduces (automatically) numbered interactions: -' Interact(): used automatic calculated index, -' Interact2(): index can be explicit defined, -' SetIndex(): set the next index, -' GetIndex(): get the index and automatically increase index - -' Index -' ################################## - -!function $inc_($value, $step=1) - !return $value + $step -!endfunction - -!$index=1 - -!function SetIndex($new_index) - !$index=$new_index -!endfunction - -!function GetIndex($auto_increase=1) - !$old = $index - !$index=$inc_($index, $auto_increase) - !return $old -!endfunction - -' Interact -' ################################## -!define Interact2(e_index, e_from, e_to, e_label) Rel(e_from, e_to, ""e_index: e_label"") -!define Interact2(e_index, e_from, e_to, e_label, e_techn) Rel(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Back(e_index, e_from, e_to, e_label) Rel_Back(e_from, e_to, ""e_index: e_label"") -!define Interact2_Back(e_index, e_from, e_to, e_label, e_techn) Rel_Back(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Neighbor(e_index, e_from, e_to, e_label) Rel_Neighbor(e_from, e_to, ""e_index: e_label"") -!define Interact2_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"") -!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_D(e_index, e_from, e_to, e_label) Rel_D(e_from, e_to, ""e_index: e_label"") -!define Interact2_D(e_index, e_from, e_to, e_label, e_techn) Rel_D(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Down(e_index, e_from, e_to, e_label) Rel_Down(e_from, e_to, ""e_index: e_label"") -!define Interact2_Down(e_index, e_from, e_to, e_label, e_techn) Rel_Down(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_U(e_index, e_from, e_to, e_label) Rel_U(e_from, e_to, ""e_index: e_label"") -!define Interact2_U(e_index, e_from, e_to, e_label, e_techn) Rel_U(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Up(e_index, e_from, e_to, e_label) Rel_Up(e_from, e_to, ""e_index: e_label"") -!define Interact2_Up(e_index, e_from, e_to, e_label, e_techn) Rel_Up(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_L(e_index, e_from, e_to, e_label) Rel_L(e_from, e_to, ""e_index: e_label"") -!define Interact2_L(e_index, e_from, e_to, e_label, e_techn) Rel_L(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Left(e_index, e_from, e_to, e_label) Rel_Left(e_from, e_to, ""e_index: e_label"") -!define Interact2_Left(e_index, e_from, e_to, e_label, e_techn) Rel_Left(e_from, e_to, ""e_index: e_label"", e_techn) - -!define Interact2_R(e_index, e_from, e_to, e_label) Rel_R(e_from, e_to, ""e_index: e_label"") -!define Interact2_R(e_index, e_from, e_to, e_label, e_techn) Rel_R(e_from, e_to, ""e_index: e_label"", e_techn) -!define Interact2_Right(e_index, e_from, e_to, e_label) Rel_Right(e_from, e_to, ""e_index: e_label"") -!define Interact2_Right(e_index, e_from, e_to, e_label, e_techn) Rel_Right(e_from, e_to, ""e_index: e_label"", e_techn) - -!unquoted function Interact($e_from, $e_to, $e_label) - Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact($e_from, $e_to, $e_label, $e_techn) - Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Back($e_from, $e_to, $e_label) - Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Back($e_from, $e_to, $e_label, $e_techn) - Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Neighbor($e_from, $e_to, $e_label) - Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Neighbor($e_from, $e_to, $e_label, $e_techn) - Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label) - Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label, $e_techn) - Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_D($e_from, $e_to, $e_label) - Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_D($e_from, $e_to, $e_label, $e_techn) - Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Down($e_from, $e_to, $e_label) - Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Down($e_from, $e_to, $e_label, $e_techn) - Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_U($e_from, $e_to, $e_label) - Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_U($e_from, $e_to, $e_label, $e_techn) - Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Up($e_from, $e_to, $e_label) - Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Up($e_from, $e_to, $e_label, $e_techn) - Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_L($e_from, $e_to, $e_label) - Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_L($e_from, $e_to, $e_label, $e_techn) - Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Left($e_from, $e_to, $e_label) - Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Left($e_from, $e_to, $e_label, $e_techn) - Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction - -!unquoted function Interact_R($e_from, $e_to, $e_label) - Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_R($e_from, $e_to, $e_label, $e_techn) - Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Right($e_from, $e_to, $e_label) - Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"") - !$index=$inc_($index) -!endfunction -!unquoted function Interact_Right($e_from, $e_to, $e_label, $e_techn) - Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn) - !$index=$inc_($index) -!endfunction +!include ' Structurizr.DynamicView: dynamic title Web Application - Dynamic -LAYOUT_WITH_LEGEND() - ContainerDb(SoftwareSystem__Database__39bccb8, ""Database"", ""Relational Database Schema"", ""Stores information"") Person(User__387cc75, ""User"") Container_Boundary(SoftwareSystem__WebApplication__d2a342, ""Web Application"") { Component(SoftwareSystem__WebApplication__SomeController__341621c, ""SomeController"", ""Spring MVC Controller"") Component(SoftwareSystem__WebApplication__SomeRepository__6d9009, ""SomeRepository"", ""Spring Data"") } -Interact2(""1"", User__387cc75, SoftwareSystem__WebApplication__SomeController__341621c, ""Requests /something"", ""HTTP"") -Interact2(""2"", SoftwareSystem__WebApplication__SomeController__341621c, SoftwareSystem__WebApplication__SomeRepository__6d9009, """") -Interact2(""3"", SoftwareSystem__WebApplication__SomeRepository__6d9009, SoftwareSystem__Database__39bccb8, ""select * from something"", ""JDBC"") +RelIndex(""1"", User__387cc75, SoftwareSystem__WebApplication__SomeController__341621c, ""Requests /something"", ""HTTP"") +RelIndex(""2"", SoftwareSystem__WebApplication__SomeController__341621c, SoftwareSystem__WebApplication__SomeRepository__6d9009, """") +RelIndex(""3"", SoftwareSystem__WebApplication__SomeRepository__6d9009, SoftwareSystem__Database__39bccb8, ""select * from something"", ""JDBC"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -1114,62 +726,24 @@ public void test_writeDeploymentView() Assert.Equal( @"@startuml -!include -' C4_Deployment.puml is missing, simulate it with following definitions -' Scope: A single software system. -' Primary elements: Deployment nodes and containers within the software system in scope. -' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff. - -' Colors -' ################################## -!define NODE_FONT_COLOR #444444 -!define NODE_BG_COLOR #FFFFFF - -' Styling -' ################################## - -skinparam rectangle<> { - Shadowing false - StereotypeFontSize 0 - FontColor NODE_FONT_COLOR - BackgroundColor NODE_BG_COLOR - BorderColor #444444 -} - -' Layout -' ################################## - -!definelong LAYOUT_WITH_LEGEND -hide stereotype -legend right -|= |= Type | -| | deployment node | -| | deployment container | -endlegend -!enddefinelong - -' Nodes -' ################################## -' PlantUML does not support automatic line breaks of container, if e_techn is very long insert line breaks with -' ""\n"" -!define Node(e_alias, e_label, e_techn) rectangle ""==e_label\n[e_techn]"" <> as e_alias +!include ' Structurizr.DeploymentView: deployment -title Software System - Deployment +title Software System - Deployment - Default -LAYOUT_WITH_LEGEND() - -Node(Deployment__Default__DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__DatabaseServer__MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { +Node(DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { + Node(MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { ContainerDb(SoftwareSystem__Database1__bb9c73, ""Database"", ""Relational Database Schema"", ""Stores information"") } } -Node(Deployment__Default__WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__WebServer__ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { +Node(WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { + Node(ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { Container(SoftwareSystem__WebApplication1__31f1f25, ""Web Application"", ""Java and spring MVC"", ""Delivers content"") } } Rel(SoftwareSystem__WebApplication1__31f1f25, SoftwareSystem__Database1__bb9c73, ""Reads from and writes to"", ""JDBC"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); @@ -1181,29 +755,29 @@ public void test_writeDeploymentView_WithCustomBaseUrl() PopulateWorkspace(); DeploymentView deploymentView = _workspace.Views.DeploymentViews.First(); - _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/"; + _plantUMLWriter.CustomBaseUrl = @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/"; _plantUMLWriter.Write(deploymentView, _stringWriter); Assert.Equal( @"@startuml -!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/C4_Deployment.puml +!includeurl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/C4_Deployment.puml ' Structurizr.DeploymentView: deployment -title Software System - Deployment +title Software System - Deployment - Default -LAYOUT_WITH_LEGEND() - -Node(Deployment__Default__DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__DatabaseServer__MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { +Node(DatabaseServer__1edef6c, ""Database Server"", ""Ubuntu 12.04 LTS"") { + Node(MySQL__1fa4f18, ""MySQL"", ""MySQL 5.5.x"") { ContainerDb(SoftwareSystem__Database1__bb9c73, ""Database"", ""Relational Database Schema"", ""Stores information"") } } -Node(Deployment__Default__WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { - Node(Deployment__Default__WebServer__ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { +Node(WebServer__1e2ffe, ""Web Server"", ""Ubuntu 12.04 LTS"") { + Node(ApacheTomcat__2b8afb4, ""Apache Tomcat"", ""Apache Tomcat 8.x"") { Container(SoftwareSystem__WebApplication1__31f1f25, ""Web Application"", ""Java and spring MVC"", ""Delivers content"") } } Rel(SoftwareSystem__WebApplication1__31f1f25, SoftwareSystem__Database1__bb9c73, ""Reads from and writes to"", ""JDBC"") + +SHOW_LEGEND() @enduml ".UnifyNewLine().UnifyHashValues(), _stringWriter.ToString().UnifyHashValues()); diff --git a/Structurizr.PlantUML.Tests/IO/PlantUML/PlantUMLWriterTests.cs b/Structurizr.PlantUML.Tests/IO/PlantUML/PlantUMLWriterTests.cs index 3d5e4c7..754debe 100644 --- a/Structurizr.PlantUML.Tests/IO/PlantUML/PlantUMLWriterTests.cs +++ b/Structurizr.PlantUML.Tests/IO/PlantUML/PlantUMLWriterTests.cs @@ -101,7 +101,7 @@ title Web Application - Dynamic @enduml @startuml -title Software System - Deployment +title Software System - Deployment - Default node ""Database Server"" <> as 23 { node ""MySQL"" <> as 24 { artifact ""Database"" <> as 25 @@ -254,7 +254,7 @@ public void test_writeDeploymentView() Assert.Equal( @"@startuml -title Software System - Deployment +title Software System - Deployment - Default node ""Database Server"" <> as 23 { node ""MySQL"" <> as 24 { artifact ""Database"" <> as 25 diff --git a/Structurizr.PlantUML.Tests/Structurizr.PlantUML.Tests.csproj b/Structurizr.PlantUML.Tests/Structurizr.PlantUML.Tests.csproj index 1152688..99d79e0 100644 --- a/Structurizr.PlantUML.Tests/Structurizr.PlantUML.Tests.csproj +++ b/Structurizr.PlantUML.Tests/Structurizr.PlantUML.Tests.csproj @@ -1,6 +1,6 @@  - netcoreapp1.1 + netcoreapp2.1 false diff --git a/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlException.cs b/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlException.cs new file mode 100644 index 0000000..71ba6d4 --- /dev/null +++ b/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Structurizr.PlantUML.IO.C4PlantUML +{ + public class C4PlantUmlException : Exception + { + public C4PlantUmlException(string message) : base(message) { } + } +} diff --git a/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlWriter.cs b/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlWriter.cs index 186f336..bb5c1ec 100644 --- a/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlWriter.cs +++ b/Structurizr.PlantUML/IO/C4PlantUML/C4PlantUmlWriter.cs @@ -6,6 +6,9 @@ // Source base version copied from https://gist.github.com/coldacid/465fa8f3a4cd3fdd7b640a65ad5b86f4 (https://github.com/structurizr/dotnet/issues/47) // kirchsth: Extended with dynamic and deployment view +// kirchsth: updated to update generated source to new C4PlantUml stdlib v2.2.0 (no additional dynamic and deployment view macros are required anymore, calls updated) +// kirchsth: Add tags/styles support +// kirchsth: next planed C4PlantUml stdlib v2.3.0 features can be used with CustomBaseUrl https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/ namespace Structurizr.IO.C4PlantUML { public class C4PlantUmlWriter : PlantUMLWriterBase @@ -23,12 +26,13 @@ public enum LayoutDirection public LayoutDirection? Layout { get; set; } /// - /// PlantUML-stdlib or https://raw.githubusercontent.com/RicardoNiepel/C4-PlantUML/release/1-0/ does not support - /// dynamic or deployment diagrams. They can be used via the PlantUML-stdlib and in the diagram added definitions - /// or use a pull-request version which is available at https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/ + /// C4PlantUml stdlib v2.2.0 () supports dynamic or deployment diagrams. They can be used via the PlantUML-stdlib and no + /// special CustomBaseUrl is required. + /// Only next stdlib features (like Person shapes) has to be defined via CustomBaseUrl=https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/ /// (if the value is empty/null then PlantUML-stdlib with added definitions is used) /// - public string CustomBaseUrl { get; set; } = ""; // @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/"; + public string CustomBaseUrl { get; set; } = ""; // @"https://raw.githubusercontent.com/kirchsth/C4-PlantUML/extended/"; + public bool EnableNextFeatures { get; set; } = false; protected override void Write(SystemLandscapeView view, TextWriter writer) { @@ -258,6 +262,11 @@ private void Write(DeploymentNode deploymentNode, TextWriter writer, int indentL Write(containerInstance, writer, indentLevel + 1); } + foreach (SoftwareSystemInstance systemInstance in deploymentNode.SoftwareSystemInstances) + { + Write(systemInstance, writer, indentLevel + 1); + } + writer.WriteLine($"{indent}}}"); } @@ -296,6 +305,11 @@ private string TypeOf(Element e) return "Container"; } + if (e is SoftwareSystemInstance) + { + return "Software System"; + } + return ""; } @@ -307,6 +321,9 @@ private bool HasValue(string s) protected override void WriteProlog(View view, TextWriter writer) { + if (view == null) throw new ArgumentNullException(nameof(view)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + writer.WriteLine("@startuml"); switch (view) @@ -325,237 +342,15 @@ protected override void WriteProlog(View view, TextWriter writer) break; case DynamicView _: - if (!string.IsNullOrWhiteSpace(CustomBaseUrl)) - { - writer.WriteLine($"!includeurl {CustomBaseUrl}C4_Dynamic.puml"); - } - else - { - writer.WriteLine(@"!include "); - // Add missing deployment nodes (until they are part of the plantuml macros) - writer.WriteLine(@"' C4_Dynamic.puml is missing, simulate it with following definitions"); - - writer.WriteLine(@"' Scope: Interactions in an enterprise, software system or container."); - writer.WriteLine(@"' Primary and supporting elements: Depends on the diagram scope - "); - writer.WriteLine(@"' enterprise - people and software systems related to the enterprise in scope "); - writer.WriteLine(@"' software system - see system context or container diagrams, "); - writer.WriteLine(@"' container - see component diagram."); - writer.WriteLine(@"' Intended audience: Technical and non-technical people, inside and outside of the software development team."); - writer.WriteLine(@""); - writer.WriteLine(@"' Dynamic diagram introduces (automatically) numbered interactions: "); - writer.WriteLine(@"' Interact(): used automatic calculated index, "); - writer.WriteLine(@"' Interact2(): index can be explicit defined,"); - writer.WriteLine(@"' SetIndex(): set the next index, "); - writer.WriteLine(@"' GetIndex(): get the index and automatically increase index"); - writer.WriteLine(@""); - writer.WriteLine(@"' Index"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@""); - writer.WriteLine(@"!function $inc_($value, $step=1)"); - writer.WriteLine(@" !return $value + $step"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!$index=1"); - writer.WriteLine(@""); - writer.WriteLine(@"!function SetIndex($new_index)"); - writer.WriteLine(@" !$index=$new_index"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!function GetIndex($auto_increase=1)"); - writer.WriteLine(@" !$old = $index"); - writer.WriteLine(@" !$index=$inc_($index, $auto_increase)"); - writer.WriteLine(@" !return $old"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"' Interact"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@"!define Interact2(e_index, e_from, e_to, e_label) Rel(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2(e_index, e_from, e_to, e_label, e_techn) Rel(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_Back(e_index, e_from, e_to, e_label) Rel_Back(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Back(e_index, e_from, e_to, e_label, e_techn) Rel_Back(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_Neighbor(e_index, e_from, e_to, e_label) Rel_Neighbor(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Back_Neighbor(e_index, e_from, e_to, e_label, e_techn) Rel_Back_Neighbor(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_D(e_index, e_from, e_to, e_label) Rel_D(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_D(e_index, e_from, e_to, e_label, e_techn) Rel_D(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@"!define Interact2_Down(e_index, e_from, e_to, e_label) Rel_Down(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Down(e_index, e_from, e_to, e_label, e_techn) Rel_Down(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_U(e_index, e_from, e_to, e_label) Rel_U(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_U(e_index, e_from, e_to, e_label, e_techn) Rel_U(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@"!define Interact2_Up(e_index, e_from, e_to, e_label) Rel_Up(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Up(e_index, e_from, e_to, e_label, e_techn) Rel_Up(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_L(e_index, e_from, e_to, e_label) Rel_L(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_L(e_index, e_from, e_to, e_label, e_techn) Rel_L(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@"!define Interact2_Left(e_index, e_from, e_to, e_label) Rel_Left(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Left(e_index, e_from, e_to, e_label, e_techn) Rel_Left(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!define Interact2_R(e_index, e_from, e_to, e_label) Rel_R(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_R(e_index, e_from, e_to, e_label, e_techn) Rel_R(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@"!define Interact2_Right(e_index, e_from, e_to, e_label) Rel_Right(e_from, e_to, ""e_index: e_label"")"); - writer.WriteLine(@"!define Interact2_Right(e_index, e_from, e_to, e_label, e_techn) Rel_Right(e_from, e_to, ""e_index: e_label"", e_techn)"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_Back($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Back($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Back($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_Neighbor($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Neighbor($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Back_Neighbor($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Back_Neighbor($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_D($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_D($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_D($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Down($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Down($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Down($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_U($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_U($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_U($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Up($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Up($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Up($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_L($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_L($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_L($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Left($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Left($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Left($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@""); - writer.WriteLine(@"!unquoted function Interact_R($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_R($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_R($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Right($e_from, $e_to, $e_label) "); - writer.WriteLine(@" Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"")"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - writer.WriteLine(@"!unquoted function Interact_Right($e_from, $e_to, $e_label, $e_techn) "); - writer.WriteLine(@" Interact2_Right($index, ""$e_from"", ""$e_to"", ""$e_label"", $e_techn)"); - writer.WriteLine(@" !$index=$inc_($index)"); - writer.WriteLine(@"!endfunction"); - } + writer.WriteLine(!string.IsNullOrWhiteSpace(CustomBaseUrl) + ? $"!includeurl {CustomBaseUrl}C4_Dynamic.puml" + : $"!include "); break; case DeploymentView _: - if (!string.IsNullOrWhiteSpace(CustomBaseUrl)) - { - writer.WriteLine($"!includeurl {CustomBaseUrl}C4_Deployment.puml"); - } - else - { - writer.WriteLine(@"!include "); - // Add missing deployment nodes (until they are part of the plantuml macros) - writer.WriteLine(@"' C4_Deployment.puml is missing, simulate it with following definitions"); - - writer.WriteLine(@"' Scope: A single software system."); - writer.WriteLine(@"' Primary elements: Deployment nodes and containers within the software system in scope."); - writer.WriteLine(@"' Intended audience: Technical people inside and outside of the software development team; including software architects, developers and operations/support staff."); - writer.WriteLine(@""); - writer.WriteLine(@"' Colors"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@"!define NODE_FONT_COLOR #444444"); - writer.WriteLine(@"!define NODE_BG_COLOR #FFFFFF"); - writer.WriteLine(@""); - writer.WriteLine(@"' Styling"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@""); - writer.WriteLine(@"skinparam rectangle<> {"); - writer.WriteLine(@" Shadowing false"); - writer.WriteLine(@" StereotypeFontSize 0"); - writer.WriteLine(@" FontColor NODE_FONT_COLOR"); - writer.WriteLine(@" BackgroundColor NODE_BG_COLOR"); - writer.WriteLine(@" BorderColor #444444"); - writer.WriteLine(@"}"); - writer.WriteLine(@""); - writer.WriteLine(@"' Layout"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@""); - writer.WriteLine(@"!definelong LAYOUT_WITH_LEGEND"); - writer.WriteLine(@"hide stereotype"); - writer.WriteLine(@"legend right"); - writer.WriteLine(@"|= |= Type |"); - writer.WriteLine(@"| | deployment node |"); - writer.WriteLine(@"| | deployment container |"); - writer.WriteLine(@"endlegend"); - writer.WriteLine(@"!enddefinelong"); - writer.WriteLine(@""); - writer.WriteLine(@"' Nodes"); - writer.WriteLine(@"' ##################################"); - writer.WriteLine(@"' PlantUML does not support automatic line breaks of container, if e_techn is very long insert line breaks with "); - writer.WriteLine(@"' ""\n"""); - writer.WriteLine(@"!define Node(e_alias, e_label, e_techn) rectangle ""==e_label\n[e_techn]"" <> as e_alias"); - } + writer.WriteLine(!string.IsNullOrWhiteSpace(CustomBaseUrl) + ? $"!includeurl {CustomBaseUrl}C4_Deployment.puml" + : $"!include "); break; default: @@ -570,8 +365,6 @@ protected override void WriteProlog(View view, TextWriter writer) writer.WriteLine("title " + GetTitle(view)); writer.WriteLine(); - if (LayoutWithLegend) - writer.WriteLine("LAYOUT_WITH_LEGEND()"); // C4 PlantUML workaround add () if (LayoutAsSketch) writer.WriteLine("LAYOUT_AS_SKETCH()"); // C4 PlantUML workaround add () if (Layout.HasValue) @@ -588,8 +381,23 @@ protected override void WriteProlog(View view, TextWriter writer) throw new InvalidOperationException($"Unknown {nameof(LayoutDirection)} value"); } } - if (LayoutWithLegend || LayoutAsSketch || Layout.HasValue) + if (LayoutAsSketch || Layout.HasValue) + writer.WriteLine(); + } + + protected virtual void WriteEpilog(View view, TextWriter writer) + { + if (view == null) throw new ArgumentNullException(nameof(view)); + if (writer == null) throw new ArgumentNullException(nameof(writer)); + + if (LayoutWithLegend) + { writer.WriteLine(); + writer.WriteLine("SHOW_LEGEND()"); // C4 PlantUML workaround add () + } + + writer.WriteLine("@enduml"); + writer.WriteLine(""); } protected virtual void Write(Element element, TextWriter writer, int indentLevel = 0, bool asBoundary = false) @@ -617,10 +425,6 @@ protected virtual void Write(Element element, TextWriter writer, int indentLevel macro = "Node"; title = deploymentNode.Name + (deploymentNode.Instances > 1 ? $" (x{deploymentNode.Instances})" : ""); technology = deploymentNode.Technology; - // PlantUML supports no automatic line breaks of titles, if it belongs to a surrounding object - // make workaround with html tags (they are not working via multiple lines too) - if (technology.Length > 30) - technology = BlockText(technology, 30, @"\n"); break; default: throw new NotSupportedException($"{element.GetType()} not supported boundary type"); @@ -659,6 +463,12 @@ protected virtual void Write(Element element, TextWriter writer, int indentLevel technology = cmp.Technology ?? ""; isDatabase = cmp.GetIsDatabase(); break; + case SoftwareSystemInstance sysIn: + macro = "System"; + title = sysIn.SoftwareSystem.Name; + description = sysIn.SoftwareSystem.Description; + external = sysIn.SoftwareSystem.Location == Location.External; + break; case ContainerInstance cntIn: macro = "Container"; title = cntIn.Container.Name; @@ -706,6 +516,13 @@ protected virtual void Write(RelationshipView relationshipView, TextWriter write label = advancedDescription ?? relationship.Description ?? "", tech = !string.IsNullOrWhiteSpace(relationship.Technology) ? relationship.Technology : null; + if (relationshipView.Response ?? false) + { + var swap = source; + source = dest; + dest = swap; + } + var macro = GetSpecificLayoutMacro(relationshipView); writer.Write($"{macro}({source}, {dest}, \"{EscapeText(label)}\""); @@ -729,8 +546,15 @@ protected virtual void WriteDynamicInteraction(RelationshipView relationshipView dest = TokenizeName(relationship.Destination), tech = !string.IsNullOrWhiteSpace(relationship.Technology) ? relationship.Technology : null; + if (relationshipView.Response ?? false) + { + var swap = source; + source = dest; + dest = swap; + } + var macro = GetSpecificLayoutMacro(relationshipView); - macro = "Interact2" + macro.Substring("Rel".Length); + macro = "RelIndex" + macro.Substring("Rel".Length); writer.Write($"{macro}(\"{order}\", {source}, {dest}, \"{EscapeText(label)}\""); if (tech != null) diff --git a/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/ModelExtensions.cs b/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/ModelExtensions.cs new file mode 100644 index 0000000..4d02454 --- /dev/null +++ b/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/ModelExtensions.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; +using Structurizr.PlantUML.IO.C4PlantUML; + +namespace Structurizr.IO.C4PlantUML.ModelExtensions +{ + public static class ModelExtensions + { + /// + /// new impl. of CanonicalName starts with "{ElementType}://" or "{ElementType}://{DeploymentName}/{DeploymentName}/" instead of "/" therefore it can be optionally ignored + /// Additional / in staticNames have to be converted to . + /// + /// the canonical name with elementType prefix (e.g. Container://SoftwareSystem/Container) or without elementType prefix (e.g. /SoftwareSystem/Container) + /// + public static Element GetElementWithCanonicalOrStaticalName(this Model model, string canonicalName, bool compareOnlyLastPart=true) + { + if (string.IsNullOrWhiteSpace(canonicalName)) + throw new ArgumentException("A canonical name must be specified."); + var found = model.GetElements().FirstOrDefault((Func) (x => + { + if (compareOnlyLastPart) + return x.CanonicalName.EndsWith(canonicalName); + else + return x.CanonicalName == canonicalName; + })); + + if (found == null) + { + var all = model.GetElements().Select(e => e.CanonicalName).ToList(); + var combined = string.Join("\n", all); + throw new C4PlantUmlException( + $"Element {canonicalName} could not be found. Following elements exist:\n{combined}"); + } + return found; + } + } +} \ No newline at end of file diff --git a/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipViewExtensions.cs b/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipViewExtensions.cs index 1d4e309..39decbd 100644 --- a/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipViewExtensions.cs +++ b/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipViewExtensions.cs @@ -1,9 +1,15 @@ +using System.Collections.Generic; + namespace Structurizr.IO.C4PlantUML.ModelExtensions { + /// + /// WORKAROUND: RelationshipView supports no properties anymore, therefore the direction is stored in Position + /// public static class RelationshipViewExtensions { /// /// Get a direction of the relation which should be used in a specific C4PlantUML views + /// (direction is stored in Position) /// /// /// returns true if it is defined via the view specific RelationshipView and false if it is defined via the underlying Relationship @@ -11,7 +17,7 @@ public static class RelationshipViewExtensions public static string GetDirection(this RelationshipView relationshipView, out bool viewSpecific) { string value = DirectionValues.NotSet; - if (relationshipView.Properties?.TryGetValue(Properties.Direction, out value) == true) + if (relationshipView.Position.HasValue && Position2Direction.TryGetValue(relationshipView.Position.Value, out value) == true) { viewSpecific = true; return value; @@ -24,15 +30,32 @@ public static string GetDirection(this RelationshipView relationshipView, out bo /// /// Set a direction of the relation which should be used in a specific C4PlantUML views + /// (direction is internal stored in Position) /// /// /// one of public static void SetDirection(this RelationshipView relationshipView, string direction) { if (string.IsNullOrWhiteSpace(direction)) // direction DirectionValues.NotSet - relationshipView.Properties.Remove(Properties.Direction); + relationshipView.Position = null; else - relationshipView.Properties[Properties.Direction] = direction; + relationshipView.Position = Direction2Position[direction]; } + + private static Dictionary Direction2Position = new Dictionary + { + [DirectionValues.Up] = 1, + [DirectionValues.Down] = 2, + [DirectionValues.Left] = 3, + [DirectionValues.Right] = 4 + }; + + private static Dictionary Position2Direction = new Dictionary + { + [1] = DirectionValues.Up, + [2] = DirectionValues.Down, + [3] = DirectionValues.Left, + [4] = DirectionValues.Right + }; } } \ No newline at end of file diff --git a/Structurizr.PlantUML/IO/C4PlantUML/PlantUMLWriterBase.cs b/Structurizr.PlantUML/IO/C4PlantUML/PlantUMLWriterBase.cs index ee2285e..262911b 100644 --- a/Structurizr.PlantUML/IO/C4PlantUML/PlantUMLWriterBase.cs +++ b/Structurizr.PlantUML/IO/C4PlantUML/PlantUMLWriterBase.cs @@ -165,13 +165,20 @@ protected string TokenizeName(string s, int? hash = null) { if (String.IsNullOrWhiteSpace(s)) return ""; - s = s - .Trim('/') - .Replace(" ", "") - .Replace("-", "") - .Replace("[", "") - .Replace("]", "") - .Replace("/", "__"); + // canonically name calculation changed + // a) instead of "/" starts with "{ElementType}://"; remove it that it is compatible with old impl. + // b) deployment namespaces are added with "/"; remove it that it is shorter (unique parts created via hash) + // c) orig "/" in static namespaces replaced with "."; replace with "__" that it is compatible with old impl. + var p = s.LastIndexOf('/'); + if (p >= 0) + s = s.Substring(p + 1); + + s = s.Replace(" ", "") + .Replace("-", "") + .Replace("[", "") + .Replace("]", "") + .Replace(".", "__"); + if (hash.HasValue) { s = s + "__" + hash.Value.ToString("x"); @@ -191,45 +198,6 @@ protected virtual string GetTitle(View view) => ? String.IsNullOrWhiteSpace(view.Title) ? view.Name : view.Title : throw new ArgumentNullException(nameof(view)); - protected string BlockText(string s, int blockWidth, string formattedLineBreak) - { - var block = s; - - if (blockWidth > 0 && !s.Contains("\n") && !s.Contains("\r")) - { - var formatted = new StringBuilder(); - int pos = 0; - string word = ""; - - foreach (var c in s) - { - word += c; - if (c == ' ') - { - if (pos != 0 && pos + word.Length > blockWidth) - { - formatted.Append(formattedLineBreak); - pos = 0; - } - formatted.Append(word); - pos += word.Length; - word = ""; - } - } - - if (word.Length > 0) - { - if (pos != 0 && pos + word.Length > blockWidth) - formatted.Append(formattedLineBreak); - formatted.Append(word); - } - - block = formatted.ToString(); - } - - return block; - } - protected string EscapeText(string s) => s.Replace("\"", """); } } \ No newline at end of file diff --git a/Structurizr.PlantUML/Structurizr.PlantUML.csproj b/Structurizr.PlantUML/Structurizr.PlantUML.csproj index 8c3e4da..2bb6043 100644 --- a/Structurizr.PlantUML/Structurizr.PlantUML.csproj +++ b/Structurizr.PlantUML/Structurizr.PlantUML.csproj @@ -16,11 +16,11 @@ - netstandard1.3;net45 + netstandard2.0;net45 - + \ No newline at end of file diff --git a/Structurizr.Reflection.Examples/Structurizr.Reflection.Examples.csproj b/Structurizr.Reflection.Examples/Structurizr.Reflection.Examples.csproj index 8be178a..a143cdf 100644 --- a/Structurizr.Reflection.Examples/Structurizr.Reflection.Examples.csproj +++ b/Structurizr.Reflection.Examples/Structurizr.Reflection.Examples.csproj @@ -7,7 +7,7 @@ false - + diff --git a/Structurizr.Reflection/Structurizr.Reflection.csproj b/Structurizr.Reflection/Structurizr.Reflection.csproj index 92fc045..2a5596f 100644 --- a/Structurizr.Reflection/Structurizr.Reflection.csproj +++ b/Structurizr.Reflection/Structurizr.Reflection.csproj @@ -20,11 +20,7 @@ - - - - - + diff --git a/docs/c4-plantuml.md b/docs/c4-plantuml.md index 0141c60..ddf30ba 100644 --- a/docs/c4-plantuml.md +++ b/docs/c4-plantuml.md @@ -1,16 +1,14 @@ # C4-PlantUML -Structurizr for .NET also includes a simple exporter that can create diagram definitions compatible with [C4-PlantUML](https://github.com/RicardoNiepel/C4-PlantUML). The following diagram types are supported: +Structurizr for .NET also includes a simple exporter that can create diagram definitions compatible with [C4-PlantUML v2.2.0](https://github.com/plantuml-stdlib/C4-PlantUML). +Following diagram types are supported: - Enterprise Context - System Context - Container - Component -- Dynamic* -- Deployment* - -*..Dynamic and Deployment diagrams are part of an open pull request (from https://github.com/kirchsth/C4-PlantUML). The diagrams can use the definitions via -CustomBaseUrl=https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/ or if it is not set then the definition is merged in each diagram) +- Dynamic +- Deployment Simply create your software architecture model and views as usual, and use the [C4PlantUMLWriter](../Structurizr.PlantUML/IO/C4PlantUML/C4PlantUMLWriter.cs) class to export the views. [For example](../Structurizr.Examples/C4PlantUML.cs): @@ -53,13 +51,13 @@ This code will generate and output a PlantUML diagram definition that looks like ' Structurizr.SystemContextView: SystemContext title Software System - System Context -LAYOUT_WITH_LEGEND() - Enterprise_Boundary(SomeEnterprise, "Some Enterprise") { System(SoftwareSystem__33c0d9d, "Software System", "My software system.") Person(User__378734a, "User", "A user of my software system.") Rel_Right(User__378734a, SoftwareSystem__33c0d9d, "Uses") } + +SHOW_LEGEND() @enduml ``` @@ -100,8 +98,6 @@ This code will generate and output a PlantUML diagram definition that looks like ' Structurizr.ContainerView: containers title Software System - Containers -LAYOUT_WITH_LEGEND() - Person(User__378734a, "User", "A user of my software system.") System_Boundary(SoftwareSystem__33c0d9d, "Software System") { ContainerDb(SoftwareSystem__Database__202c666, "Database", "Relational Database Schema", "Stores information") @@ -109,6 +105,8 @@ System_Boundary(SoftwareSystem__33c0d9d, "Software System") { } Rel(User__378734a, SoftwareSystem__WebApplication__2004eee, "uses", "HTTP") Rel_Right(SoftwareSystem__WebApplication__2004eee, SoftwareSystem__Database__202c666, "Reads from and writes to", "JDBC") + +SHOW_LEGEND() @enduml ``` diff --git a/docs/images/c4-plantuml-getting-started.png b/docs/images/c4-plantuml-getting-started.png index cd16e37..0e9afdd 100644 Binary files a/docs/images/c4-plantuml-getting-started.png and b/docs/images/c4-plantuml-getting-started.png differ diff --git a/docs/images/c4-plantuml-getting-started2.png b/docs/images/c4-plantuml-getting-started2.png index b3b63d4..dbf8db0 100644 Binary files a/docs/images/c4-plantuml-getting-started2.png and b/docs/images/c4-plantuml-getting-started2.png differ