├── .travis.yml
├── docs
├── images
│ ├── structurizr-banner.png
│ ├── structurizr-overview.png
│ ├── plantuml-getting-started.png
│ ├── structurizr-annotations-1.png
│ ├── c4-plantuml-getting-started.png
│ └── c4-plantuml-getting-started2.png
├── binaries.md
├── changelog.md
├── plantuml.md
├── c4-plantuml.md
└── structurizr-annotations.md
├── Structurizr.Examples
├── structurizr-logo.png
├── Documentation
│ └── adr
│ │ ├── 0001-record-architecture-decisions.md
│ │ ├── 0002-implement-as-shell-scripts.md
│ │ ├── 0004-markdown-format.md
│ │ ├── 0005-help-comments.md
│ │ ├── 0007-invoke-adr-config-executable-to-get-configuration.md
│ │ ├── 0003-single-command-with-subcommands.md
│ │ ├── 0006-packaging-and-distribution-in-other-version-control-repositories.md
│ │ └── 0008-use-iso-8601-format-for-dates.md
├── PlantUML.cs
├── Structurizr.Examples.csproj
├── C4PlantUML.cs
└── AdrTools.cs
├── Structurizr.AdrTools.Tests
├── TestFailedException.cs
├── AdrTools
│ ├── adrs
│ │ ├── 0001-record-architecture-decisions.md
│ │ ├── 0010-asciidoc-format.md
│ │ ├── 0002-implement-as-shell-scripts.md
│ │ ├── 0009-help-scripts.md
│ │ ├── 0004-markdown-format.md
│ │ ├── 0005-help-comments.md
│ │ ├── 0007-invoke-adr-config-executable-to-get-configuration.md
│ │ ├── 0003-single-command-with-subcommands.md
│ │ ├── 0006-packaging-and-distribution-in-other-version-control-repositories.md
│ │ └── 0008-use-iso-8601-format-for-dates.md
│ └── AdrToolsImporterTests.cs
└── Structurizr.AdrTools.Tests.csproj
├── Structurizr.PlantUML
├── IO
│ └── C4PlantUML
│ │ ├── ModelExtensions
│ │ ├── BooleanValues.cs
│ │ ├── Properties.cs
│ │ ├── ComponentExtensions.cs
│ │ ├── ContainerExtensions.cs
│ │ ├── DirectionValues.cs
│ │ ├── RelationshipExtensions.cs
│ │ └── RelationshipViewExtensions.cs
│ │ └── IPlantUMLWriter.cs
└── Structurizr.PlantUML.csproj
├── Structurizr.Cecil.Examples
├── Annotations
│ ├── IRepository.cs
│ ├── HtmlController.cs
│ └── EfRepository.cs
├── Structurizr.Cecil.Examples.csproj
└── StructurizrAnnotations.cs
├── Structurizr.Reflection.Examples
├── Annotations
│ ├── IRepository.cs
│ ├── HtmlController.cs
│ └── EfRepository.cs
├── Structurizr.Reflection.Examples.csproj
└── StructurizrAnnotations.cs
├── README.md
├── Structurizr.Analysis
├── Structurizr.Analysis.csproj
└── Analysis
│ ├── ComponentFinderStrategy.cs
│ └── ComponentFinder.cs
├── Structurizr.AdrTools
├── Structurizr.AdrTools.csproj
└── AdrTools
│ └── AdrToolsImporter.cs
├── Structurizr.ActiveDirectory
├── Structurizr.ActiveDirectory.csproj
├── AzureActiveDirectoryStructurizrClient.cs
└── Program.cs
├── Structurizr.PlantUML.Tests
├── Structurizr.PlantUML.Tests.csproj
└── Utilities
│ └── UnifyExtension.cs
├── appveyor.yml
├── Structurizr.Annotations
├── ComponentAttribute.cs
├── CodeElementAttribute.cs
├── UsedByPersonAttribute.cs
├── UsedByContainerAttribute.cs
├── UsesContainerAttribute.cs
├── UsedBySoftwareSystemAttribute.cs
├── UsesSoftwareSystemAttribute.cs
├── UsesComponentAttribute.cs
└── Structurizr.Annotations.csproj
├── Structurizr.Reflection
├── Analysis
│ ├── SupportingTypes
│ │ ├── SupportingTypesStrategy.cs
│ │ └── ReferencedTypesSupportingTypesStrategy.cs
│ ├── ITypeMatcher.cs
│ ├── ITypeRepository.cs
│ ├── NameSuffixTypeMatcher.cs
│ ├── ExtendsClassMatcher.cs
│ ├── InterfaceImplementationTypeMatcher.cs
│ └── TypeMatcherComponentFinderStrategy.cs
└── Structurizr.Reflection.csproj
├── Structurizr.Cecil
├── Analysis
│ ├── SupportingTypes
│ │ ├── SupportingTypesStrategy.cs
│ │ └── ReferencedTypesSupportingTypesStrategy.cs
│ ├── ITypeMatcher.cs
│ ├── ITypeRepository.cs
│ ├── NameSuffixTypeMatcher.cs
│ ├── ExtendsClassTypeMatcher.cs
│ ├── InterfaceImplementationTypeMatcher.cs
│ ├── CustomAttributeTypeMatcher.cs
│ └── TypeMatcherComponentFinderStrategy.cs
├── Structurizr.Cecil.csproj
└── Util
│ ├── TypeReferenceExtensions.cs
│ ├── AssemblyDefinitionExtensions.cs
│ ├── TypeDefinitionExtensions.cs
│ └── CustomAttributeExtensions.cs
├── Structurizr.Roslyn
├── app.config
├── Structurizr.Roslyn.csproj
└── Analysis
│ └── TypeSummaryComponentFinderStrategy.cs
├── .gitattributes
└── .gitignore
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: csharp
2 | solution: Structurizr.sln
3 |
--------------------------------------------------------------------------------
/docs/images/structurizr-banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/structurizr-banner.png
--------------------------------------------------------------------------------
/docs/images/structurizr-overview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/structurizr-overview.png
--------------------------------------------------------------------------------
/Structurizr.Examples/structurizr-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/Structurizr.Examples/structurizr-logo.png
--------------------------------------------------------------------------------
/docs/images/plantuml-getting-started.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/plantuml-getting-started.png
--------------------------------------------------------------------------------
/docs/images/structurizr-annotations-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/structurizr-annotations-1.png
--------------------------------------------------------------------------------
/docs/images/c4-plantuml-getting-started.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/c4-plantuml-getting-started.png
--------------------------------------------------------------------------------
/docs/images/c4-plantuml-getting-started2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/structurizr/dotnet-extensions/HEAD/docs/images/c4-plantuml-getting-started2.png
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/TestFailedException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.AdrTools.Tests
4 | {
5 | public class TestFailedException : Exception
6 | {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/BooleanValues.cs:
--------------------------------------------------------------------------------
1 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
2 | {
3 | public class BooleanValues
4 | {
5 | public const string True = "true";
6 | public const string False = "false";
7 | }
8 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil.Examples/Annotations/IRepository.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [Component(Description = "Provides access to data stored in the database.", Technology = "C#")]
6 | public interface IRepository
7 | {
8 | string GetData(long id);
9 | }
10 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection.Examples/Annotations/IRepository.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [Component(Description = "Provides access to data stored in the database.", Technology = "C#")]
6 | public interface IRepository
7 | {
8 | string GetData(long id);
9 | }
10 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Structurizr for .NET extensions
2 |
3 | The code in this repo is unsupported and has been archived. The [Structurizr DSL](https://github.com/structurizr/dsl) is the recommended tooling for authoring Structurizr workspaces, while the [Structurizr CLI](https://github.com/structurizr/cli) can be used to push/pull workspaces and export views to PlantUML, Mermaid, etc.
--------------------------------------------------------------------------------
/Structurizr.Cecil.Examples/Annotations/HtmlController.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [Component(Description = "Serves HTML pages to users.", Technology = "ASP.NET MVC")]
6 | [UsedByPerson("User", Description = "Uses", Technology = "HTTPS")]
7 | class HtmlController
8 | {
9 | [UsesComponent("Gets data using")]
10 | private IRepository repository = new EfRepository();
11 | }
12 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil.Examples/Annotations/EfRepository.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [CodeElement(nameof(IRepository), Description = "Implementation")]
6 | [UsesContainer("Database", Description = "Reads from", Technology = "Entity Framework")]
7 | class EfRepository : IRepository
8 | {
9 | public string GetData(long id)
10 | {
11 | return "...";
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection.Examples/Annotations/HtmlController.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [Component(Description = "Serves HTML pages to users.", Technology = "ASP.NET MVC")]
6 | [UsedByPerson("User", Description = "Uses", Technology = "HTTPS")]
7 | class HtmlController
8 | {
9 | [UsesComponent("Gets data using")]
10 | private IRepository repository = new EfRepository();
11 | }
12 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection.Examples/Annotations/EfRepository.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Annotations;
2 |
3 | namespace Structurizr.Examples.Annotations
4 | {
5 | [CodeElement(nameof(IRepository), Description = "Implementation")]
6 | [UsesContainer("Database", Description = "Reads from", Technology = "Entity Framework")]
7 | class EfRepository : IRepository
8 | {
9 | public string GetData(long id)
10 | {
11 | return "...";
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/IPlantUMLWriter.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | // Source base version copied from https://gist.github.com/coldacid/465fa8f3a4cd3fdd7b640a65ad5b86f4 (https://github.com/structurizr/dotnet/issues/47)
4 | // kirchsth: Extended with dynamic and deployment view
5 | namespace Structurizr.IO.C4PlantUML
6 | {
7 | public interface IPlantUMLWriter
8 | {
9 | void Write(Workspace workspace, TextWriter writer);
10 | void Write(View view, TextWriter writer);
11 | }
12 | }
--------------------------------------------------------------------------------
/Structurizr.Analysis/Structurizr.Analysis.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | netstandard1.3;net45
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools/Structurizr.AdrTools.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | netstandard1.3;net45
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0001-record-architecture-decisions.md:
--------------------------------------------------------------------------------
1 | # 1. Record architecture decisions
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | We need to record the architectural decisions made on this project.
12 |
13 | ## Decision
14 |
15 | We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions
16 |
17 | ## Consequences
18 |
19 | See Michael Nygard's article, linked above.
20 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0010-asciidoc-format.md:
--------------------------------------------------------------------------------
1 | # 10. AsciiDoc format
2 |
3 | Date: 2018-08-11
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | Supercedes [4. Markdown format](0004-markdown-format.md)
10 |
11 | ## Context
12 |
13 | The issue motivating this decision, and any context that influences or constrains the decision.
14 |
15 | ## Decision
16 |
17 | The change that we're proposing or have agreed to implement.
18 |
19 | ## Consequences
20 |
21 | What becomes easier or more difficult to do and any risks introduced by the change that will need to be mitigated.
22 |
--------------------------------------------------------------------------------
/Structurizr.ActiveDirectory/Structurizr.ActiveDirectory.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | Exe
11 | netcoreapp1.1
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML.Tests/Structurizr.PlantUML.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp1.1
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Structurizr.Cecil.Examples/Structurizr.Cecil.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net45
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0002-implement-as-shell-scripts.md:
--------------------------------------------------------------------------------
1 | # 2. Implement as shell scripts
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | ADRs are plain text files stored in a subdirectory of the project.
12 |
13 | The tool needs to create new files and apply small edits to
14 | the Status section of existing files.
15 |
16 | ## Decision
17 |
18 | The tool is implemented as shell scripts that use standard Unix
19 | tools -- grep, sed, awk, etc.
20 |
21 | ## Consequences
22 |
23 | The tool won't support Windows. Being plain text files, ADRs can
24 | be created by hand and edited in any text editor. This tool just
25 | makes the process more convenient.
26 |
27 | Development will have to cope with differences between Unix
28 | variants, particularly Linux and MacOS X.
29 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/Properties.cs:
--------------------------------------------------------------------------------
1 |
2 |
3 | // Source base version copied from https://gist.github.com/coldacid/465fa8f3a4cd3fdd7b640a65ad5b86f4 (https://github.com/structurizr/dotnet/issues/47)
4 | // kirchsth: Extended with dynamic and deployment view
5 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
6 | {
7 | public class Properties
8 | {
9 | ///
10 | /// This property enables to mark Components and Container as database with value "true"
11 | ///
12 | public const string IsDatabase = "IsDatabase";
13 |
14 | ///
15 | /// This property enables to define a specific (default) direction of Relationships or RelationshipViews
16 | ///
17 | public const string Direction = "Direction";
18 | }
19 | }
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.0.{build}
2 | pull_requests:
3 | do_not_increment_build_number: true
4 | image: Visual Studio 2017
5 | build_script:
6 | - ps: >-
7 | dotnet restore
8 |
9 | dotnet build
10 |
11 |
12 | dotnet clean /p:Configuration=Release
13 |
14 | if ($env:APPVEYOR_REPO_TAG -Eq "true") {
15 | dotnet msbuild "/t:Restore;Pack" /p:Configuration=Release /p:Version=$env:APPVEYOR_REPO_TAG_NAME
16 | } else {
17 | dotnet msbuild "/t:Restore;Pack" /p:Configuration=Release /p:VersionSuffix=pre${env:APPVEYOR_BUILD_NUMBER}-$($env:APPVEYOR_REPO_COMMIT.Substring(0,8))
18 | }
19 | test_script:
20 | - ps: dotnet test .\Structurizr.AdrTools.Tests\Structurizr.AdrTools.Tests.csproj
21 | - ps: dotnet test .\Structurizr.PlantUML.Tests\Structurizr.PlantUML.Tests.csproj
22 | artifacts:
23 | - path: '*\bin\Release\*.nupkg'
--------------------------------------------------------------------------------
/Structurizr.Reflection.Examples/Structurizr.Reflection.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net452
6 | Structurizr.Examples.ContosoUniversity
7 | false
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0009-help-scripts.md:
--------------------------------------------------------------------------------
1 | # 9. Help scripts
2 |
3 | Date: 2018-06-26
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | Amends [5. Help comments](0005-help-comments.md)
10 |
11 | ## Context
12 |
13 | Currently help text is generated by extracting specially formatted comments from the top of the command script.
14 |
15 | This makes it easy for developers of the tool: documentation and code is all in one place.
16 |
17 | But, it means that help text cannot include calculated values, such as the location of files.
18 |
19 | ## Decision
20 |
21 | Where necessary, help text can be generated by a script.
22 |
23 | The script will be called _adr_help__
24 |
25 | ## Consequences
26 |
27 | Help scripts can include helper scripts to locate files, giving more accurate instructions to the user that reflect how the tool is deployed in their environment.
28 |
29 |
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0001-record-architecture-decisions.md:
--------------------------------------------------------------------------------
1 | # 1. Record architecture decisions
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | We need to record the architectural decisions made on this project.
12 |
13 | ## Decision
14 |
15 | We will use Architecture Decision Records, as described by Michael Nygard in this article: [http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions)
16 |
17 | ## Consequences
18 |
19 | See Michael Nygard's article, linked above.
20 |
21 | ---
22 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/ComponentExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
4 | {
5 | public static class ComponentExtensions
6 | {
7 | public static bool GetIsDatabase(this Component container)
8 | {
9 | string value = string.Empty;
10 | if (container.Properties?.TryGetValue(Properties.IsDatabase, out value) == true)
11 | return string.Compare(BooleanValues.True, value, StringComparison.OrdinalIgnoreCase) == 0;
12 |
13 | return false;
14 | }
15 |
16 | public static void SetIsDatabase(this Component container, bool isDatabase)
17 | {
18 | if (isDatabase)
19 | container.Properties[Properties.IsDatabase] = BooleanValues.True;
20 | else
21 | container.Properties.Remove(Properties.IsDatabase);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/ContainerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
4 | {
5 | public static class ContainerExtensions
6 | {
7 | public static bool GetIsDatabase(this Container container)
8 | {
9 | string value = string.Empty;
10 | if (container.Properties?.TryGetValue(Properties.IsDatabase, out value) == true)
11 | return string.Compare(BooleanValues.True, value, StringComparison.OrdinalIgnoreCase) == 0;
12 |
13 | return false;
14 | }
15 |
16 | public static void SetIsDatabase(this Container container, bool isDatabase)
17 | {
18 | if (isDatabase)
19 | container.Properties[Properties.IsDatabase] = BooleanValues.True;
20 | else
21 | container.Properties.Remove(Properties.IsDatabase);
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/Structurizr.PlantUML/Structurizr.PlantUML.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | Provides the ability to export view definitions to PlantUML diagram definitions.
11 | Structurizr Limited
12 | Copyright 2018
13 | https://opensource.org/licenses/Apache-2.0
14 | https://structurizr.com
15 | https://github.com/structurizr/dotnet
16 |
17 |
18 |
19 | netstandard1.3;net45
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/docs/binaries.md:
--------------------------------------------------------------------------------
1 | # Binaries
2 |
3 | The "Structurizr for .NET" binaries are hosted on [NuGet](https://www.nuget.org/profiles/structurizr) as follows:
4 |
5 | Name | Description
6 | --------------------- | ---------------------------------------------------------------------------------------------------------------------------
7 | Structurizr.Analysis | Provides analysis capabilities, using reflection on compiled bytecode to find components.
8 | Structurizr.AdrTools | Imports architecture decision records (ADRs) from the adr-tools tooling.
9 | Structurizr.Annotations | Annotation attribute library for descriptive architecture in code.
10 | Structurizr.Cecil | Architecture analysis library built around the Mono.Cecil library.
11 | Structurizr.PlantUML | Provides the ability to export view definitions to PlantUML diagram definitions.
12 | Structurizr.Reflection | Architecture analysis library built around .NET reflection.
13 | Structurizr.Roslyn | Architecture analysis library built around the Roslyn compiler.
14 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/DirectionValues.cs:
--------------------------------------------------------------------------------
1 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
2 | {
3 | public sealed class DirectionValues
4 | {
5 | public const string NotSet = "";
6 |
7 | public const string Back = "Back";
8 | ///
9 | /// C4 PlantUml writes without u
10 | ///
11 | public const string Neighbor = "Neighbor";
12 | ///
13 | /// C4 PlantUml writes without u
14 | ///
15 | public const string Neighbour = "Neighbour";
16 | ///
17 | /// C4 PlantUml writes without u
18 | ///
19 | public const string BackNeighbor = "BackNeighbor";
20 | ///
21 | /// C4 PlantUml writes without u
22 | ///
23 | public const string BackNeighbour = "BackNeighbour";
24 |
25 | public const string Up = "Up";
26 | public const string Down = "Down";
27 | public const string Left = "Left";
28 | public const string Right = "Right";
29 | }
30 | }
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0002-implement-as-shell-scripts.md:
--------------------------------------------------------------------------------
1 | # 2. Implement as shell scripts
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | ADRs are plain text files stored in a subdirectory of the project.
12 |
13 | The tool needs to create new files and apply small edits to
14 | the Status section of existing files.
15 |
16 | ## Decision
17 |
18 | The tool is implemented as shell scripts that use standard Unix
19 | tools -- grep, sed, awk, etc.
20 |
21 | ## Consequences
22 |
23 | The tool won't support Windows. Being plain text files, ADRs can
24 | be created by hand and edited in any text editor. This tool just
25 | makes the process more convenient.
26 |
27 | Development will have to cope with differences between Unix
28 | variants, particularly Linux and MacOS X.
29 |
30 | ---
31 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.Annotations/ComponentAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the attributed type class or interface can be considered to be a "component".
7 | ///
8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
9 | public sealed class ComponentAttribute : Attribute
10 | {
11 | ///
12 | /// Gets or sets a description of the component.
13 | ///
14 | /// A description of the component.
15 | public string Description { get; set; } = String.Empty;
16 |
17 | ///
18 | /// Gets or sets a description of the technology used to implement the component.
19 | ///
20 | /// A description of the technology used to implement the component.
21 | public string Technology { get; set; } = String.Empty;
22 |
23 | ///
24 | /// Initializes a new instance of the class.
25 | ///
26 | public ComponentAttribute() {}
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
2 | {
3 | public static class RelationshipExtensions
4 | {
5 | public static string GetDirection(this Relationship relationship)
6 | {
7 | string value = DirectionValues.NotSet;
8 | if (relationship.Properties?.TryGetValue(Properties.Direction, out value) == true)
9 | return value;
10 |
11 | return value;
12 | }
13 |
14 | ///
15 | /// Set a (default) direction of the relation which should be used in all C4PlantUML views
16 | ///
17 | ///
18 | /// one of
19 | public static void SetDirection(this Relationship relationship, string direction)
20 | {
21 | if (string.IsNullOrWhiteSpace(direction)) // direction DirectionValues.NotSet
22 | relationship.Properties.Remove(Properties.Direction);
23 | else
24 | relationship.Properties[Properties.Direction] = direction;
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0004-markdown-format.md:
--------------------------------------------------------------------------------
1 | # 4. Markdown format
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Superceded by [10. AsciiDoc format](0010-asciidoc-format.md)
8 |
9 | ## Context
10 |
11 | The decision records must be stored in a plain text format:
12 |
13 | * This works well with version control systems.
14 |
15 | * It allows the tool to modify the status of records and insert
16 | hyperlinks when one decision supercedes another.
17 |
18 | * Decisions can be read in the terminal, IDE, version control
19 | browser, etc.
20 |
21 | People will want to use some formatting: lists, code examples,
22 | and so on.
23 |
24 | People will want to view the decision records in a more readable
25 | format than plain text, and maybe print them out.
26 |
27 |
28 | ## Decision
29 |
30 | Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/).
31 |
32 | ## Consequences
33 |
34 | Decisions can be read in the terminal.
35 |
36 | Decisions will be formatted nicely and hyperlinked by the
37 | browsers of project hosting sites like GitHub and Bitbucket.
38 |
39 | Tools like [Pandoc](http://pandoc.org/) can be used to convert
40 | the decision records into HTML or PDF.
41 |
--------------------------------------------------------------------------------
/Structurizr.Analysis/Analysis/ComponentFinderStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 |
6 | ///
7 | /// The interface that all component finder strategies must implement.
8 | ///
9 | public interface ComponentFinderStrategy
10 | {
11 |
12 | ///
13 | /// A reference to the parent ComponentFinder.
14 | ///
15 | ComponentFinder ComponentFinder {set; }
16 |
17 | ///
18 | /// Called before all component finder strategies belonging to the
19 | /// same component finder are asked to find components.
20 | ///
21 | void BeforeFindComponents();
22 |
23 | ///
24 | /// Finds components.
25 | ///
26 | IEnumerable FindComponents();
27 |
28 | ///
29 | /// Called after all component finder strategies belonging to the
30 | /// same component finder have found components. This can be used
31 | /// to supplement the component with more information, such as
32 | /// dependencies.
33 | ///
34 | void AfterFindComponents();
35 |
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/SupportingTypes/SupportingTypesStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Provides a base type for strategies used to identify supporting types for components.
7 | ///
8 | ///
9 | ///
10 | public abstract class SupportingTypesStrategy
11 | {
12 |
13 | ///
14 | /// Gets the type repository to use for looking up supporting types.
15 | ///
16 | /// The type repository instance provided by the component finder strategy using this strategy.
17 | public ITypeRepository TypeRepository { get; set; }
18 |
19 | ///
20 | /// Identifies and returns supporting types of the provided .
21 | ///
22 | /// The component for which supporting types are to be found.
23 | /// A set of assembly qualified type names for types which support the component.
24 | public abstract HashSet FindSupportingTypes(Component component);
25 |
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/SupportingTypes/SupportingTypesStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Provides a base type for strategies used to identify supporting types for components.
7 | ///
8 | ///
9 | ///
10 | public abstract class SupportingTypesStrategy
11 | {
12 |
13 | ///
14 | /// Gets the type repository to use for looking up supporting types.
15 | ///
16 | /// The type repository instance provided by the component finder strategy using this strategy.
17 | protected internal ITypeRepository TypeRepository { get; internal set; }
18 |
19 | ///
20 | /// Identifies and returns supporting types of the provided .
21 | ///
22 | /// The component for which supporting types are to be found.
23 | /// A set of assembly qualified type names for types which support the component.
24 | public abstract HashSet FindSupportingTypes(Component component);
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Structurizr.Cecil.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | Mono.Cecil based software architecture analysis for Structurizr.Core
11 | Structurizr Limited
12 | Copyright 2017
13 | https://opensource.org/licenses/Apache-2.0
14 | https://structurizr.com
15 | https://github.com/structurizr/dotnet
16 |
17 |
18 |
19 | netstandard1.3;net45
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0005-help-comments.md:
--------------------------------------------------------------------------------
1 | # 5. Help comments
2 |
3 | Date: 2016-02-13
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | Amended by [9. Help scripts](0009-help-scripts.md)
10 |
11 | ## Context
12 |
13 | The tool will have a `help` subcommand to provide documentation
14 | for users.
15 |
16 | It's nice to have usage documentation in the script files
17 | themselves, in comments. When reading the code, that's the first
18 | place to look for information about how to run a script.
19 |
20 | ## Decision
21 |
22 | Write usage documentation in comments in the source file.
23 |
24 | Distinguish between documentation comments and normal comments.
25 | Documentation comments have two hash characters at the start of
26 | the line.
27 |
28 | The `adr help` command can parse comments out from the script
29 | using the standard Unix tools `grep` and `cut`.
30 |
31 | ## Consequences
32 |
33 | No need to maintain help text in a separate file.
34 |
35 | Help text can easily be kept up to date as the script is edited.
36 |
37 | There's no automated check that the help text is up to date. The
38 | tests do not work well as documentation for users, and the help
39 | text is not easily cross-checked against the code.
40 |
41 | This won't work if any subcommands are not implemented as scripts
42 | that use '#' as a comment character.
43 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Structurizr.Reflection.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | .NET Reflection based software architecture analysis for Structurizr.Core
11 | Structurizr Limited
12 | Copyright 2017
13 | https://opensource.org/licenses/Apache-2.0
14 | https://structurizr.com
15 | https://github.com/structurizr/dotnet
16 |
17 |
18 |
19 | netstandard2.0;net452
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0007-invoke-adr-config-executable-to-get-configuration.md:
--------------------------------------------------------------------------------
1 | # 7. Invoke adr-config executable to get configuration
2 |
3 | Date: 2016-12-17
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation.
12 |
13 | Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable.
14 |
15 | This name is too common.
16 |
17 | The `config.sh` file is not executable, and so doesn't belong in a bin directory.
18 |
19 | ## Decision
20 |
21 | Replace `config.sh` with an executable, named `adr-config` that outputs configuration.
22 |
23 | Each script in ADR Tools will eval the output of `adr-config` to configure itself.
24 |
25 | ## Consequences
26 |
27 | Configuration within ADR Tools is a little more complicated.
28 |
29 | Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script.
30 |
31 | To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.)
32 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/ITypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Declares an interface for component identification rules.
7 | ///
8 | public interface ITypeMatcher
9 | {
10 |
11 | ///
12 | /// Checks the given matches against the component identification rule.
13 | ///
14 | /// The of the type to match.
15 | /// if the type matches against the rule; otherwise, .
16 | bool Matches(Type type);
17 |
18 | ///
19 | /// Gets the description to use for components matching the rule.
20 | ///
21 | /// A string containing the Description property for matching components.
22 | ///
23 | string GetDescription();
24 |
25 | ///
26 | /// Gets the technology name(s) to use for components matching the rule.
27 | ///
28 | /// A string containing the Technology property for matching components.
29 | ///
30 | string GetTechnology();
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/ITypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Declares an interface for component identification rules.
7 | ///
8 | public interface ITypeMatcher
9 | {
10 | ///
11 | /// Checks the given matches against the component identification rule.
12 | ///
13 | /// The of the type to match.
14 | /// if the type matches against the rule; otherwise, .
15 | bool Matches(TypeDefinition type);
16 |
17 | ///
18 | /// Gets the description to use for components matching the rule.
19 | ///
20 | /// A string containing the Description property for matching components.
21 | ///
22 | string GetDescription();
23 |
24 | ///
25 | /// Gets the technology name(s) to use for components matching the rule.
26 | ///
27 | /// A string containing the Technology property for matching components.
28 | ///
29 | string GetTechnology();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0003-single-command-with-subcommands.md:
--------------------------------------------------------------------------------
1 | # 3. Single command with subcommands
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | The tool provides a number of related commands to create
12 | and manipulate architecture decision records.
13 |
14 | How can the user find out about the commands that are available?
15 |
16 | ## Decision
17 |
18 | The tool defines a single command, called `adr`.
19 |
20 | The first argument to `adr` (the subcommand) specifies the
21 | action to perform. Further arguments are interpreted by the
22 | subcommand.
23 |
24 | Running `adr` without any arguments lists the available
25 | subcommands.
26 |
27 | Subcommands are implemented as scripts in the same
28 | directory as the `adr` script. E.g. the subcommand `new` is
29 | implemented as the script `adr-new`, the subcommand `help`
30 | as the script `adr-help` and so on.
31 |
32 | Helper scripts that are part of the implementation but not
33 | subcommands follow a different naming convention, so that
34 | subcommands can be listed by filtering and transforming script
35 | file names.
36 |
37 | ## Consequences
38 |
39 | Users can more easily explore the capabilities of the tool.
40 |
41 | Users are already used to this style of command-line tool. For
42 | example, Git works this way.
43 |
44 | Each subcommand can be implemented in the most appropriate
45 | language.
46 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0006-packaging-and-distribution-in-other-version-control-repositories.md:
--------------------------------------------------------------------------------
1 | # 6. Packaging and distribution in other version control repositories
2 |
3 | Date: 2016-02-16
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | Users want to install adr-tools with their preferred package
12 | manager. For example, Ubuntu users use `apt`, RedHat users use
13 | `yum` and Mac OS X users use [Homebrew](http://brew.sh).
14 |
15 | The developers of `adr-tools` don't know how, nor have permissions,
16 | to use all these packaging and distribution systems. Therefore packaging
17 | and distribution must be done by "downstream" parties.
18 |
19 | The developers of the tool should not favour any one particular
20 | packaging and distribution solution.
21 |
22 | ## Decision
23 |
24 | The `adr-tools` project will not contain any packaging or
25 | distribution scripts and config.
26 |
27 | Packaging and distribution will be managed by other projects in
28 | separate version control repositories.
29 |
30 | ## Consequences
31 |
32 | The git repo of this project will be simpler.
33 |
34 | Eventually, users will not have to use Git to get the software.
35 |
36 | We will have to tag releases in the `adr-tools` repository so that
37 | packaging projects know what can be published and how it should be
38 | identified.
39 |
40 | We will document how users can install the software in this
41 | project's README file.
42 |
--------------------------------------------------------------------------------
/Structurizr.Examples/PlantUML.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using Structurizr.IO.PlantUML;
4 |
5 | namespace Structurizr.Examples
6 | {
7 |
8 | ///
9 | /// An example of how to use the PlantUML writer. Run this program and copy/paste
10 | /// the output into http://www.plantuml.com/plantuml/
11 | ///
12 | public class PlantUML
13 | {
14 |
15 | static void Main()
16 | {
17 | Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
18 | Model model = workspace.Model;
19 |
20 | Person user = model.AddPerson("User", "A user of my software system.");
21 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
22 | user.Uses(softwareSystem, "Uses");
23 |
24 | ViewSet views = workspace.Views;
25 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
26 | contextView.AddAllSoftwareSystems();
27 | contextView.AddAllPeople();
28 |
29 | StringWriter stringWriter = new StringWriter();
30 | PlantUMLWriter plantUMLWriter = new PlantUMLWriter();
31 | plantUMLWriter.Write(workspace, stringWriter);
32 | Console.WriteLine(stringWriter.ToString());
33 | }
34 |
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0004-markdown-format.md:
--------------------------------------------------------------------------------
1 | # 4. Markdown format
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | The decision records must be stored in a plain text format:
12 |
13 | * This works well with version control systems.
14 |
15 | * It allows the tool to modify the status of records and insert
16 | hyperlinks when one decision supercedes another.
17 |
18 | * Decisions can be read in the terminal, IDE, version control
19 | browser, etc.
20 |
21 | People will want to use some formatting: lists, code examples,
22 | and so on.
23 |
24 | People will want to view the decision records in a more readable
25 | format than plain text, and maybe print them out.
26 |
27 |
28 | ## Decision
29 |
30 | Record architecture decisions in [Markdown format](https://daringfireball.net/projects/markdown/).
31 |
32 | ## Consequences
33 |
34 | Decisions can be read in the terminal.
35 |
36 | Decisions will be formatted nicely and hyperlinked by the
37 | browsers of project hosting sites like GitHub and Bitbucket.
38 |
39 | Tools like [Pandoc](http://pandoc.org/) can be used to convert
40 | the decision records into HTML or PDF.
41 |
42 | ---
43 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0005-help-comments.md:
--------------------------------------------------------------------------------
1 | # 5. Help comments
2 |
3 | Date: 2016-02-13
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | The tool will have a `help` subcommand to provide documentation
12 | for users.
13 |
14 | It's nice to have usage documentation in the script files
15 | themselves, in comments. When reading the code, that's the first
16 | place to look for information about how to run a script.
17 |
18 | ## Decision
19 |
20 | Write usage documentation in comments in the source file.
21 |
22 | Distinguish between documentation comments and normal comments.
23 | Documentation comments have two hash characters at the start of
24 | the line.
25 |
26 | The `adr help` command can parse comments out from the script
27 | using the standard Unix tools `grep` and `cut`.
28 |
29 | ## Consequences
30 |
31 | No need to maintain help text in a separate file.
32 |
33 | Help text can easily be kept up to date as the script is edited.
34 |
35 | There's no automated check that the help text is up to date. The
36 | tests do not work well as documentation for users, and the help
37 | text is not easily cross-checked against the code.
38 |
39 | This won't work if any subcommands are not implemented as scripts
40 | that use '#' as a comment character.
41 |
42 | ---
43 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.Annotations/CodeElementAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the attributed type class or interface can be considered to be a "component".
7 | ///
8 | [AttributeUsage(
9 | AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
10 | Inherited = false,
11 | AllowMultiple = false)]
12 | public sealed class CodeElementAttribute : Attribute
13 | {
14 | ///
15 | /// Gets the name of the component to which the attributed type belongs.
16 | ///
17 | /// The name of the component which includes the attributed type.
18 | public string ComponentName { get; }
19 |
20 | ///
21 | /// Gets or sets a description of the code element.
22 | ///
23 | /// A description of the code element.
24 | public string Description { get; set; } = String.Empty;
25 |
26 | ///
27 | /// Initializes a new instance of the class.
28 | ///
29 | public CodeElementAttribute(string componentName)
30 | {
31 | #if NET20 || PORTABLE2
32 | if (String.IsNullOrEmpty(componentName) || componentName.Trim() == String.Empty)
33 | #else
34 | if (String.IsNullOrWhiteSpace(componentName))
35 | #endif
36 | throw new ArgumentNullException(nameof(componentName));
37 |
38 | this.ComponentName = componentName;
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0007-invoke-adr-config-executable-to-get-configuration.md:
--------------------------------------------------------------------------------
1 | # 7. Invoke adr-config executable to get configuration
2 |
3 | Date: 2016-12-17
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | Packagers (e.g. Homebrew developers) want to configure adr-tools to match the conventions of their installation.
12 |
13 | Currently, this is done by sourcing a file `config.sh`, which should sit beside the `adr` executable.
14 |
15 | This name is too common.
16 |
17 | The `config.sh` file is not executable, and so doesn't belong in a bin directory.
18 |
19 | ## Decision
20 |
21 | Replace `config.sh` with an executable, named `adr-config` that outputs configuration.
22 |
23 | Each script in ADR Tools will eval the output of `adr-config` to configure itself.
24 |
25 | ## Consequences
26 |
27 | Configuration within ADR Tools is a little more complicated.
28 |
29 | Packagers can write their own implementation of `adr-config` that outputs configuration that matches the platform's installation conventions, and deploy it next to the `adr` script.
30 |
31 | To make development easier, the implementation of `adr-config` in the project's src/ directory will output configuration that lets the tool to run from the src/ directory without any installation step. (Packagers should not include this script in a deployable package.)
32 |
33 | ---
34 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/adrs/0008-use-iso-8601-format-for-dates.md:
--------------------------------------------------------------------------------
1 | # 8. Use ISO 8601 Format for Dates
2 |
3 | Date: 2017-02-21
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | `adr-tools` seeks to communicate the history of architectural decisions of a
12 | project. An important component of the history is the time at which a decision
13 | was made.
14 |
15 | To communicate effectively, `adr-tools` should present information as
16 | unambiguously as possible. That means that culture-neutral data formats should
17 | be preferred over culture-specific formats.
18 |
19 | Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That
20 | formatting is common formatting in the United Kingdom (where the `adr-tools`
21 | project was originally written), but is easily confused with the `mm/dd/yyyy`
22 | format preferred in the United States.
23 |
24 | The default date format may be overridden by setting `ADR_DATE` in `config.sh`.
25 |
26 | ## Decision
27 |
28 | `adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd`
29 |
30 | ## Consequences
31 |
32 | Dates are displayed in a standard, culture-neutral format.
33 |
34 | The UK-style and ISO 8601 formats can be distinguished by their separator
35 | character. The UK-style dates used a slash (`/`), while the ISO dates use a
36 | hyphen (`-`).
37 |
38 | Prior to this decision, `adr-tools` was deployed using the UK format for dates.
39 | After adopting the ISO 8601 format, existing deployments of `adr-tools` must do
40 | one of the following:
41 |
42 | * Accept mixed formatting of dates within their documentation library.
43 | * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository`
44 |
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0003-single-command-with-subcommands.md:
--------------------------------------------------------------------------------
1 | # 3. Single command with subcommands
2 |
3 | Date: 2016-02-12
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | The tool provides a number of related commands to create
12 | and manipulate architecture decision records.
13 |
14 | How can the user find out about the commands that are available?
15 |
16 | ## Decision
17 |
18 | The tool defines a single command, called `adr`.
19 |
20 | The first argument to `adr` (the subcommand) specifies the
21 | action to perform. Further arguments are interpreted by the
22 | subcommand.
23 |
24 | Running `adr` without any arguments lists the available
25 | subcommands.
26 |
27 | Subcommands are implemented as scripts in the same
28 | directory as the `adr` script. E.g. the subcommand `new` is
29 | implemented as the script `adr-new`, the subcommand `help`
30 | as the script `adr-help` and so on.
31 |
32 | Helper scripts that are part of the implementation but not
33 | subcommands follow a different naming convention, so that
34 | subcommands can be listed by filtering and transforming script
35 | file names.
36 |
37 | ## Consequences
38 |
39 | Users can more easily explore the capabilities of the tool.
40 |
41 | Users are already used to this style of command-line tool. For
42 | example, Git works this way.
43 |
44 | Each subcommand can be implemented in the most appropriate
45 | language.
46 |
47 | ---
48 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
49 |
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0006-packaging-and-distribution-in-other-version-control-repositories.md:
--------------------------------------------------------------------------------
1 | # 6. Packaging and distribution in other version control repositories
2 |
3 | Date: 2016-02-16
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | Users want to install adr-tools with their preferred package
12 | manager. For example, Ubuntu users use `apt`, RedHat users use
13 | `yum` and Mac OS X users use [Homebrew](http://brew.sh).
14 |
15 | The developers of `adr-tools` don't know how, nor have permissions,
16 | to use all these packaging and distribution systems. Therefore packaging
17 | and distribution must be done by "downstream" parties.
18 |
19 | The developers of the tool should not favour any one particular
20 | packaging and distribution solution.
21 |
22 | ## Decision
23 |
24 | The `adr-tools` project will not contain any packaging or
25 | distribution scripts and config.
26 |
27 | Packaging and distribution will be managed by other projects in
28 | separate version control repositories.
29 |
30 | ## Consequences
31 |
32 | The git repo of this project will be simpler.
33 |
34 | Eventually, users will not have to use Git to get the software.
35 |
36 | We will have to tag releases in the `adr-tools` repository so that
37 | packaging projects know what can be published and how it should be
38 | identified.
39 |
40 | We will document how users can install the software in this
41 | project's README file.
42 |
43 | ---
44 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.Cecil/Util/TypeReferenceExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | using Mono.Cecil;
5 |
6 | namespace Structurizr.Cecil
7 | {
8 | ///
9 | /// Provides extension methods for .
10 | ///
11 | public static class TypeReferenceExtensions
12 | {
13 | ///
14 | /// Gets the assembly qualified name of the type.
15 | ///
16 | /// The instance representing the type.
17 | /// The assembly qualified name of the type.
18 | public static string GetAssemblyQualifiedName(this TypeReference type)
19 | {
20 | string typeName;
21 |
22 | if (type.IsGenericInstance)
23 | {
24 | var genericInstance = (GenericInstanceType)type;
25 | typeName = String.Format("{0}.{1}[{2}]",
26 | genericInstance.Namespace,
27 | type.Name,
28 | String.Join(",",
29 | genericInstance.GenericArguments.Select(p => p.GetAssemblyQualifiedName()).ToArray()
30 | ));
31 | }
32 | else
33 | {
34 | typeName = type.FullName;
35 | }
36 |
37 | var scope = type.Scope as AssemblyNameReference;
38 | if (scope != null)
39 | {
40 | return typeName + ", " + scope.FullName;
41 | }
42 | else if (type.Scope != null)
43 | {
44 | return typeName + ", " + type.Module.Assembly.FullName;
45 | }
46 |
47 | return typeName;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML/IO/C4PlantUML/ModelExtensions/RelationshipViewExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Structurizr.IO.C4PlantUML.ModelExtensions
2 | {
3 | public static class RelationshipViewExtensions
4 | {
5 | ///
6 | /// Get a direction of the relation which should be used in a specific C4PlantUML views
7 | ///
8 | ///
9 | /// returns true if it is defined via the view specific RelationshipView and false if it is defined via the underlying Relationship
10 | ///
11 | public static string GetDirection(this RelationshipView relationshipView, out bool viewSpecific)
12 | {
13 | string value = DirectionValues.NotSet;
14 | if (relationshipView.Properties?.TryGetValue(Properties.Direction, out value) == true)
15 | {
16 | viewSpecific = true;
17 | return value;
18 | }
19 |
20 | viewSpecific = false;
21 | value = relationshipView.Relationship.GetDirection();
22 | return value;
23 | }
24 |
25 | ///
26 | /// Set a direction of the relation which should be used in a specific C4PlantUML views
27 | ///
28 | ///
29 | /// one of
30 | public static void SetDirection(this RelationshipView relationshipView, string direction)
31 | {
32 | if (string.IsNullOrWhiteSpace(direction)) // direction DirectionValues.NotSet
33 | relationshipView.Properties.Remove(Properties.Direction);
34 | else
35 | relationshipView.Properties[Properties.Direction] = direction;
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/Structurizr.Roslyn/app.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Structurizr.Roslyn/Structurizr.Roslyn.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | Roslyn based software architecture analysis for Structurizr.Core
11 | Structurizr Limited
12 | Copyright 2017
13 | https://opensource.org/licenses/Apache-2.0
14 | https://structurizr.com
15 | https://github.com/structurizr/dotnet
16 |
17 |
18 |
19 | net46
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/Structurizr.Examples/Documentation/adr/0008-use-iso-8601-format-for-dates.md:
--------------------------------------------------------------------------------
1 | # 8. Use ISO 8601 Format for Dates
2 |
3 | Date: 2017-02-21
4 |
5 | ## Status
6 |
7 | Accepted
8 |
9 | ## Context
10 |
11 | `adr-tools` seeks to communicate the history of architectural decisions of a
12 | project. An important component of the history is the time at which a decision
13 | was made.
14 |
15 | To communicate effectively, `adr-tools` should present information as
16 | unambiguously as possible. That means that culture-neutral data formats should
17 | be preferred over culture-specific formats.
18 |
19 | Existing `adr-tools` deployments format dates as `dd/mm/yyyy` by default. That
20 | formatting is common formatting in the United Kingdom (where the `adr-tools`
21 | project was originally written), but is easily confused with the `mm/dd/yyyy`
22 | format preferred in the United States.
23 |
24 | The default date format may be overridden by setting `ADR_DATE` in `config.sh`.
25 |
26 | ## Decision
27 |
28 | `adr-tools` will use the ISO 8601 format for dates: `yyyy-mm-dd`
29 |
30 | ## Consequences
31 |
32 | Dates are displayed in a standard, culture-neutral format.
33 |
34 | The UK-style and ISO 8601 formats can be distinguished by their separator
35 | character. The UK-style dates used a slash (`/`), while the ISO dates use a
36 | hyphen (`-`).
37 |
38 | Prior to this decision, `adr-tools` was deployed using the UK format for dates.
39 | After adopting the ISO 8601 format, existing deployments of `adr-tools` must do
40 | one of the following:
41 |
42 | * Accept mixed formatting of dates within their documentation library.
43 | * Update existing documents to use ISO 8601 dates by running `adr upgrade-repository`
44 |
45 | ---
46 | This Architecture Decision Record (ADR) was written by Nat Pryce as a part of [adr-tools](https://github.com/npryce/adr-tools), and is reproduced here under the [Creative Commons Attribution 4.0 International (CC BY 4.0) license](https://creativecommons.org/licenses/by/4.0/).
--------------------------------------------------------------------------------
/Structurizr.ActiveDirectory/AzureActiveDirectoryStructurizrClient.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Api;
2 | using System;
3 | using System.Net.Http;
4 | using Microsoft.IdentityModel.Clients.ActiveDirectory;
5 | using System.Globalization;
6 | using System.Net.Http.Headers;
7 |
8 | namespace Structurizr.ActiveDirectory
9 | {
10 |
11 | ///
12 | /// An extension of the regular StructurizrClient that allows an Active Directory authentication context
13 | /// to be injected into the API requests. Use this if your Structurizr on-premises installation
14 | /// resides behind Active Directory.
15 | ///
16 | public class AzureActiveDirectoryStructurizrClient : StructurizrClient
17 | {
18 |
19 | private AuthenticationResult authenticationResult;
20 |
21 | public AzureActiveDirectoryStructurizrClient(string apiUrl, string apiKey, string apiSecret,
22 | string azureActiveDirectoryInstance, string activeDirectoryTenant, string clientId, string clientKey, string resourceId) : base(apiUrl, apiKey, apiSecret)
23 | {
24 | string authority = String.Format(CultureInfo.InvariantCulture, azureActiveDirectoryInstance, activeDirectoryTenant);
25 | AuthenticationContext authenticationContext = new AuthenticationContext(authority);
26 | ClientCredential clientCredential = new ClientCredential(clientId, clientKey);
27 |
28 | try
29 | {
30 | authenticationResult = authenticationContext.AcquireTokenAsync(resourceId, clientCredential).Result;
31 | }
32 | catch (AdalException ex)
33 | {
34 | Console.WriteLine(ex.ToString());
35 | }
36 |
37 | }
38 |
39 | protected override HttpClient createHttpClient()
40 | {
41 | HttpClient httpClient = base.createHttpClient();
42 | httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authenticationResult.AccessToken);
43 |
44 | return httpClient;
45 | }
46 |
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Structurizr.PlantUML.Tests/Utilities/UnifyExtension.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace Structurizr.IO.PlantUML.Tests.Utilities
6 | {
7 | public static class UnifyExtension
8 | {
9 | public static string UnifyNewLine(this string multiLineText)
10 | {
11 | var nPos = multiLineText.IndexOf('\n');
12 | var rPos = multiLineText.IndexOf('\r');
13 | if (rPos==-1 || nPos>=0 && nPos < rPos)
14 | return multiLineText
15 | .Replace("\n\r", "<>")
16 | .Replace("\n", "<>")
17 | .Replace("<>", Environment.NewLine);
18 |
19 | return multiLineText
20 | .Replace("\r\n", "<>")
21 | .Replace("\r", "<>")
22 | .Replace("<>", Environment.NewLine);
23 | }
24 |
25 | ///
26 | /// C4LibraryPlantUmlWriter create some names with hash codes (__[0-9a-f]+) which are dynamic created
27 | /// and has to be harmonized
28 | ///
29 | ///
30 | ///
31 | public static string UnifyHashValues(this string textWithMultipleHashValues)
32 | {
33 | var hashRegex = new Regex("__([0-9a-f]+)");
34 | var hashToPlaceholder = new Dictionary();
35 | var placeholderIndex = 0;
36 |
37 | var unified = hashRegex.Replace(textWithMultipleHashValues, m =>
38 | {
39 | var orig = m.Groups[1].ToString();
40 | if (!hashToPlaceholder.TryGetValue(orig, out var placeholder))
41 | {
42 | placeholder = $"HASH{placeholderIndex++}";
43 | hashToPlaceholder[orig] = placeholder;
44 | }
45 |
46 | return $"__{placeholder}";
47 | });
48 |
49 | return unified;
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/Structurizr.Examples/Structurizr.Examples.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 | netcoreapp1.1
5 | Structurizr.Examples.PlantUML
6 | false
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Always
18 |
19 |
20 | Always
21 |
22 |
23 | Always
24 |
25 |
26 | Always
27 |
28 |
29 | Always
30 |
31 |
32 | Always
33 |
34 |
35 | Always
36 |
37 |
38 | Always
39 |
40 |
41 | Always
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/Structurizr.AdrTools.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | netcoreapp1.1
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Always
17 |
18 |
19 | Always
20 |
21 |
22 | Always
23 |
24 |
25 | Always
26 |
27 |
28 | Always
29 |
30 |
31 | Always
32 |
33 |
34 | Always
35 |
36 |
37 | Always
38 |
39 |
40 | Always
41 |
42 |
43 | Always
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsedByPersonAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the named person uses the component on which this attribute is placed, creating a relationship
7 | /// from the person to the component.
8 | ///
9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
10 | public sealed class UsedByPersonAttribute : System.Attribute
11 | {
12 | ///
13 | /// Gets the name of the person who uses the attributed component.
14 | ///
15 | /// The name of the person who uses the attributed component.
16 | public string PersonName { get; }
17 |
18 | ///
19 | /// Gets or sets a description of the relationship from the named person to the attributed component.
20 | ///
21 | /// A string describing how the named person uses the attributed component.
22 | public string Description { get; set; }
23 |
24 | ///
25 | /// Gets or sets a description of the technology used to implement the relationship between the named person
26 | /// and the attributed component.
27 | ///
28 | ///
29 | /// A string describing the technology used for connecting the named person to the attributed component.
30 | ///
31 | public string Technology { get; set; }
32 |
33 | ///
34 | /// Initializes a new instance of the class with the name of the person
35 | /// using the component specified.
36 | ///
37 | /// The name of the person which uses the attributed component.
38 | public UsedByPersonAttribute(string personName)
39 | {
40 | #if NET20 || PORTABLE2
41 | if (String.IsNullOrEmpty(personName) || personName.Trim() == String.Empty)
42 | #else
43 | if (String.IsNullOrWhiteSpace(personName))
44 | #endif
45 | throw new ArgumentNullException(nameof(personName));
46 |
47 | this.PersonName = personName;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Util/AssemblyDefinitionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.Linq;
4 |
5 | using Mono.Cecil;
6 |
7 | namespace Structurizr.Cecil
8 | {
9 | ///
10 | /// Provides extension methods for .
11 | ///
12 | public static class AssemblyDefinitionExtensions
13 | {
14 | ///
15 | /// Generates a collection of assemblies directly or indirectly referenced by this assembly.
16 | ///
17 | /// The assembly whose references are to be returned.
18 | /// Whether or not to include this assembly in the returned collection.
19 | /// A generated collection of referenced assemblies.
20 | public static IEnumerable EnumerateReferencedAssemblies(this AssemblyDefinition assembly,
21 | bool includeSelf = true)
22 | {
23 | var references = new HashSet();
24 |
25 | var queue = new Queue();
26 | queue.Enqueue(assembly);
27 |
28 | if (includeSelf)
29 | {
30 | yield return assembly;
31 | }
32 |
33 | while (queue.Count > 0)
34 | {
35 | var assm = queue.Dequeue();
36 |
37 | if (references.Contains(assm.MetadataToken)) continue;
38 | references.Add(assm.MetadataToken);
39 |
40 | var refs = from m in assm.Modules from r in m.AssemblyReferences select r;
41 | foreach (var r in refs)
42 | {
43 | AssemblyDefinition refAssm;
44 | try
45 | {
46 | refAssm = assembly.MainModule.AssemblyResolver.Resolve(r);
47 | queue.Enqueue(refAssm);
48 | }
49 | catch (AssemblyResolutionException e)
50 | {
51 | Debug.WriteLine(e);
52 | refAssm = null;
53 | }
54 |
55 | if (refAssm != null)
56 | yield return refAssm;
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsedByContainerAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the named container uses the component on which this attribute is placed, creating a relationship
7 | /// from the container to the component.
8 | ///
9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
10 | public sealed class UsedByContainerAttribute : System.Attribute
11 | {
12 | ///
13 | /// Gets the name of the container which uses the attributed component.
14 | ///
15 | /// The name of the container which uses the attributed component.
16 | public string ContainerName { get; }
17 |
18 | ///
19 | /// Gets or sets a description of the relationship from the named container to the attributed component.
20 | ///
21 | /// A string describing how the named container uses the attributed component.
22 | public string Description { get; set; } = "";
23 |
24 | ///
25 | /// Gets or sets a description of the technology used to implement the relationship between the named container
26 | /// and the attributed component.
27 | ///
28 | ///
29 | /// A string describing the technology used for connecting the named container to the attributed component.
30 | ///
31 | public string Technology { get; set; } = "";
32 |
33 | ///
34 | /// Initializes a new instance of the class with the name of the
35 | /// container using the component specified.
36 | ///
37 | /// The name of the container which uses the attributed component.
38 | public UsedByContainerAttribute(string containerName)
39 | {
40 | #if NET20 || PORTABLE2
41 | if (String.IsNullOrEmpty(containerName) || containerName.Trim() == String.Empty)
42 | #else
43 | if (String.IsNullOrWhiteSpace(containerName))
44 | #endif
45 | throw new ArgumentNullException(nameof(containerName));
46 |
47 | this.ContainerName = containerName;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsesContainerAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the named container is used by the component on which this attribute is placed, creating a
7 | /// relationship from the component to the container.
8 | ///
9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
10 | public sealed class UsesContainerAttribute : System.Attribute
11 | {
12 | ///
13 | /// Gets the name of the container used by the attributed component.
14 | ///
15 | /// The name of the container which is used by the attributed component.
16 | public string ContainerName { get; }
17 |
18 | ///
19 | /// Gets or sets a description of the relationship from the attributed component to the named container.
20 | ///
21 | /// A string describing how the named container is used by the attributed component.
22 | public string Description { get; set; } = "";
23 |
24 | ///
25 | /// Gets or sets a description of the technology used to implement the relationship between the attributed
26 | /// component to the named container.
27 | ///
28 | ///
29 | /// A string describing the technology used for connecting the attributed component to the named container.
30 | ///
31 | public string Technology { get; set; } = "";
32 |
33 | ///
34 | /// Initializes a new instance of the class with the name of the
35 | /// container being used by the attributed component.
36 | ///
37 | /// The name of the container which is used by the attributed component.
38 | public UsesContainerAttribute(string containerName)
39 | {
40 | #if NET20 || PORTABLE2
41 | if (String.IsNullOrEmpty(containerName) || containerName.Trim() == String.Empty)
42 | #else
43 | if (String.IsNullOrWhiteSpace(containerName))
44 | #endif
45 | throw new ArgumentNullException(nameof(containerName));
46 |
47 | this.ContainerName = containerName;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Structurizr.ActiveDirectory/Program.cs:
--------------------------------------------------------------------------------
1 | namespace Structurizr.ActiveDirectory
2 | {
3 | class Program
4 | {
5 |
6 | private const long WorkspaceId = 1;
7 | private const string ApiUrl = "";
8 | private const string ApiKey = "";
9 | private const string ApiSecret = "";
10 |
11 | private const string AzureActiveDirectoryInstance = "https://login.windows.net/{0}";
12 | private const string AzureActiveDirectoryTenant = ""; // e.g. something.onmicrosoft.com
13 | private const string AzureActiveDirectoryClientId = ""; // the "Application ID" of the Structurizr client application
14 | private const string AzureActiveDirectoryClientKey = ""; // a key for the Structurizr client application
15 | private const string AzureActiveDirectoryResourceId = ""; // the "Application ID" of the Structurizr on-premises web application
16 |
17 | static void Main()
18 | {
19 | Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
20 | Model model = workspace.Model;
21 |
22 | Person user = model.AddPerson("User", "A user of my software system.");
23 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
24 | user.Uses(softwareSystem, "Uses");
25 |
26 | ViewSet viewSet = workspace.Views;
27 | SystemContextView contextView = viewSet.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
28 | contextView.AddAllSoftwareSystems();
29 | contextView.AddAllPeople();
30 |
31 | Styles styles = viewSet.Configuration.Styles;
32 | styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd", Color = "#ffffff" });
33 | styles.Add(new ElementStyle(Tags.Person) { Background = "#08427b", Color = "#ffffff", Shape = Shape.Person });
34 |
35 | AzureActiveDirectoryStructurizrClient structurizrClient = new AzureActiveDirectoryStructurizrClient(
36 | ApiUrl, ApiKey, ApiSecret,
37 | AzureActiveDirectoryInstance, AzureActiveDirectoryTenant, AzureActiveDirectoryClientId, AzureActiveDirectoryClientKey, AzureActiveDirectoryResourceId
38 | );
39 | structurizrClient.PutWorkspace(WorkspaceId, workspace);
40 | }
41 |
42 | }
43 | }
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsedBySoftwareSystemAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the named software system uses the component on which this attribute is placed, creating a
7 | /// relationship from the software system to the component.
8 | ///
9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
10 | public sealed class UsedBySoftwareSystemAttribute : System.Attribute
11 | {
12 | ///
13 | /// Gets the name of the software system who uses the attributed component.
14 | ///
15 | /// The name of the software system who uses the attributed component.
16 | public string SoftwareSystemName { get; }
17 |
18 | ///
19 | /// Gets or sets a description of the relationship from the named software system to the attributed component.
20 | ///
21 | /// A string describing how the named software system uses the attributed component.
22 | public string Description { get; set; }
23 |
24 | ///
25 | /// Gets or sets a description of the technology used to implement the relationship between the named software
26 | /// system and the attributed component.
27 | ///
28 | ///
29 | /// A string describing the technology used for connecting the named software system to the attributed
30 | /// component.
31 | ///
32 | public string Technology { get; set; }
33 |
34 | ///
35 | /// Initializes a new instance of the class with the name of the
36 | /// software system using the component specified.
37 | ///
38 | /// The name of the software system which uses the attributed component.
39 | public UsedBySoftwareSystemAttribute(string softwareSystemName)
40 | {
41 | #if NET20 || PORTABLE2
42 | if (String.IsNullOrEmpty(softwareSystemName) || softwareSystemName.Trim() == String.Empty)
43 | #else
44 | if (String.IsNullOrWhiteSpace(softwareSystemName))
45 | #endif
46 | throw new ArgumentNullException(nameof(softwareSystemName));
47 |
48 | this.SoftwareSystemName = softwareSystemName;
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | # Explicitly declare text files you want to always be normalized and converted
5 | # to native line endings on checkout.
6 | #*.ts text
7 |
8 | # Shell scripts should always have lf line endings
9 | *.sh text eol=lf
10 | sources.list text eol=lf
11 |
12 | #### Visual Studio specific stuff
13 |
14 | ###############################################################################
15 | # Set default behavior for command prompt diff.
16 | #
17 | # This is need for earlier builds of msysgit that does not have it on by
18 | # default for csharp files.
19 | # Note: This is only used by command line
20 | ###############################################################################
21 | *.cs diff=csharp
22 |
23 | ###############################################################################
24 | # Set the merge driver for project and solution files
25 | #
26 | # Merging from the command prompt will add diff markers to the files if there
27 | # are conflicts (Merging from VS is not affected by the settings below, in VS
28 | # the diff markers are never inserted). Diff markers may cause the following
29 | # file extensions to fail to load in VS. An alternative would be to treat
30 | # these files as binary and thus will always conflict and require user
31 | # intervention with every merge. To do so, just comment the entries below and
32 | # uncomment the group further below
33 | ###############################################################################
34 | *.sln text eol=crlf
35 | *.csproj text eol=crlf
36 | *.vbproj text eol=crlf
37 | *.vcxproj text eol=crlf
38 | *.vcproj text eol=crlf
39 | *.dbproj text eol=crlf
40 | *.fsproj text eol=crlf
41 | *.lsproj text eol=crlf
42 | *.wixproj text eol=crlf
43 | *.modelproj text eol=crlf
44 | *.sqlproj text eol=crlf
45 | *.wmaproj text eol=crlf
46 |
47 | *.xproj text eol=crlf
48 | *.props text eol=crlf
49 | *.filters text eol=crlf
50 | *.vcxitems text eol=crlf
51 |
52 | *.sln merge=binary
53 | *.csproj merge=binary
54 | *.vbproj merge=binary
55 | *.vcxproj merge=binary
56 | *.vcproj merge=binary
57 | *.dbproj merge=binary
58 | *.fsproj merge=binary
59 | *.lsproj merge=binary
60 | *.wixproj merge=binary
61 | *.modelproj merge=binary
62 | *.sqlproj merge=binary
63 | *.wmaproj merge=binary
64 |
65 | *.xproj merge=binary
66 | *.props merge=binary
67 | *.filters merge=binary
68 | *.vcxitems merge=binary
69 |
70 |
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsesSoftwareSystemAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the named software system is used by the component on which this attribute is placed, creating a
7 | /// relationship from the component to the software system.
8 | ///
9 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = true)]
10 | public sealed class UsesSoftwareSystemAttribute : System.Attribute
11 | {
12 | ///
13 | /// Gets the name of the software system used by the attributed component.
14 | ///
15 | /// The name of the software system which is used by the attributed component.
16 | public string SoftwareSystemName { get; }
17 |
18 | ///
19 | /// Gets or sets a description of the relationship from the attributed component to the named software system.
20 | ///
21 | /// A string describing how the named software system is used by the attributed component.
22 | public string Description { get; set; }
23 |
24 | ///
25 | /// Gets or sets a description of the technology used to implement the relationship between the named software
26 | /// system and the attributed component.
27 | ///
28 | ///
29 | /// A string describing the technology used for connecting the attributed component to the named software
30 | /// system.
31 | ///
32 | public string Technology { get; set; }
33 |
34 | ///
35 | /// Initializes a new instance of the class with the name of the
36 | /// software system used by the component specified.
37 | ///
38 | ///
39 | /// The name of the software system which is used by the attributed component.
40 | ///
41 | public UsesSoftwareSystemAttribute(string softwareSystemName)
42 | {
43 | #if NET20 || PORTABLE2
44 | if (String.IsNullOrEmpty(softwareSystemName) || softwareSystemName.Trim() == String.Empty)
45 | #else
46 | if (String.IsNullOrWhiteSpace(softwareSystemName))
47 | #endif
48 | throw new ArgumentNullException(nameof(softwareSystemName));
49 |
50 | this.SoftwareSystemName = softwareSystemName;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Structurizr.Annotations/UsesComponentAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Annotations
4 | {
5 | ///
6 | /// Specifies that the type of the attributed field, property, or parameter is a component used by the type to which
7 | /// the attributed field, property, or parameter belongs.
8 | ///
9 | ///
10 | ///
11 | /// In documentation for this annotation, the "attributed component" refers to the type containing the field,
12 | /// property, or parameter on which the attribute is placed. The "used component" is the component specified by the
13 | /// type of the field, property, or parameter.
14 | ///
15 | ///
16 | [AttributeUsage(
17 | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter,
18 | Inherited = false,
19 | AllowMultiple = false)]
20 | public sealed class UsesComponentAttribute : System.Attribute
21 | {
22 | ///
23 | /// Gets a description of the relationship from the attributed component to the used component.
24 | ///
25 | /// A string describing how the used component is used by the attributed component.
26 | public string Description { get; }
27 |
28 | ///
29 | /// Gets or sets a description of the technology used to implement the relationship between the attributed
30 | /// component to the used component.
31 | ///
32 | ///
33 | /// A string describing the technology used for connecting the attributed component to the used component.
34 | ///
35 | public string Technology { get; set; } = "";
36 |
37 | ///
38 | /// Initializes a new instance of the class with a description of the
39 | /// relationship from the attributed component to the used component.
40 | ///
41 | ///
42 | /// A string describing how the used component is used by the attributed component.
43 | ///
44 | public UsesComponentAttribute(string description)
45 | {
46 | #if NET20 || PORTABLE2
47 | if (String.IsNullOrEmpty(description) || description.Trim() == String.Empty)
48 | #else
49 | if (String.IsNullOrWhiteSpace(description))
50 | #endif
51 | throw new ArgumentNullException(nameof(description));
52 |
53 | this.Description = description;
54 | }
55 | }
56 | }
--------------------------------------------------------------------------------
/Structurizr.Annotations/Structurizr.Annotations.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0.9.2
7 |
8 |
9 |
10 | Annotations to maintain the C4 model for software architecture in code
11 | Structurizr Annotations lets you use .NET attributes to declare components and their relationships using the C4 model for software architecture.
12 | Structurizr Limited
13 | Copyright 2017
14 | https://opensource.org/licenses/Apache-2.0
15 | https://structurizr.com
16 | https://github.com/structurizr/dotnet
17 |
18 |
19 |
20 | netstandard1.0;net20;portable40-net40+sl4+win8+wp7;portable40-net40+sl5+win8+wp8+wpa81
21 | bin\$(Configuration)\$(TargetFramework)\Structurizr.Annotations.xml
22 | 1.6.0
23 |
24 |
25 |
26 | .NETPortable
27 | v4.0
28 | Profile2
29 | $(DefineConstants);PORTABLE2
30 |
31 |
32 |
33 | .NETPortable
34 | v4.0
35 | Profile328
36 | $(DefineConstants);PORTABLE328
37 |
38 |
39 |
40 | .NETFramework
41 | v2.0
42 | $(DefineConstants);NET20
43 | $(windir)\Microsoft.NET\Framework\v2.0.50727
44 |
45 |
46 |
47 |
48 | 4.1.0
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/ITypeRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Structurizr.Analysis
5 | {
6 |
7 | ///
8 | /// Declares an interface for a repository of type information from Mono.Cecil.
9 | ///
10 | public interface ITypeRepository
11 | {
12 |
13 | ///
14 | /// Gets the root namespace of the types stored in the repository.
15 | ///
16 | /// The name of the root namespace of the types in the repository.
17 | string Namespace { get; }
18 |
19 | ///
20 | /// Gets all of the types stored in this type repository.
21 | ///
22 | ///
23 | /// A collection of objects, or an empty enumeration if no types were found.
24 | ///
25 | IEnumerable GetAllTypes();
26 |
27 | ///
28 | /// Gets a single type from the repository by name.
29 | ///
30 | /// The name of the type to return.
31 | ///
32 | /// A for the specified type, or if the type could not
33 | /// be found.
34 | ///
35 | Type GetType(string type);
36 |
37 | ///
38 | /// Gets all of the types in the repository that are referenced by the named type.
39 | ///
40 | /// The name of the type whose references are to be returned.
41 | ///
42 | /// A collection of objects representing the types referenced by the type named by
43 | /// , or an empty enumeration if no types were found.
44 | ///
45 | IEnumerable GetReferencedTypes(string type);
46 |
47 | ///
48 | /// Gets the type category (class, interface, etc.) of the named type.
49 | ///
50 | /// The name of the type being categorized.
51 | /// The type category of the type if not "class" or "struct", otherwise .
52 | string FindCategory(string typeName);
53 |
54 | ///
55 | /// Gets the access modifier of the named type.
56 | ///
57 | /// The name of the type whose access modifier is being looked up.
58 | ///
59 | /// The type's access modifier ("public" or "internal") if the type is found, otherwise .
60 | ///
61 | string FindVisibility(string typeName);
62 |
63 | }
64 |
65 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/ITypeRepository.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | using Mono.Cecil;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 | ///
8 | /// Declares an interface for a repository of type information from Mono.Cecil.
9 | ///
10 | public interface ITypeRepository
11 | {
12 | ///
13 | /// Gets the root namespace of the types stored in the repository.
14 | ///
15 | /// The name of the root namespace of the types in the repository.
16 | string Namespace { get; }
17 |
18 | ///
19 | /// Gets all of the types stored in this type repository.
20 | ///
21 | ///
22 | /// A collection of objects, or an empty enumeration if no types were found.
23 | ///
24 | IEnumerable GetAllTypes();
25 |
26 | ///
27 | /// Gets a single type from the repository by name.
28 | ///
29 | /// The name of the type to return.
30 | ///
31 | /// A for the specified type, or if the type could not
32 | /// be found.
33 | ///
34 | TypeDefinition GetType(string typeName);
35 |
36 | ///
37 | /// Gets all of the types in the repository that are referenced by the named type.
38 | ///
39 | /// The name of the type whose references are to be returned.
40 | ///
41 | /// A collection of objects representing the types referenced by the type named by
42 | /// , or an empty enumeration if no types were found.
43 | ///
44 | IEnumerable GetReferencedTypes(string type);
45 |
46 | ///
47 | /// Gets the type category (class, interface, etc.) of the named type.
48 | ///
49 | /// The name of the type being categorized.
50 | /// The type category of the type if not "class" or "struct", otherwise .
51 | string FindCategory(string typeName);
52 |
53 | ///
54 | /// Gets the access modifier of the named type.
55 | ///
56 | /// The name of the type whose access modifier is being looked up.
57 | ///
58 | /// The type's access modifier ("public" or "internal") if the type is found, otherwise .
59 | ///
60 | string FindVisibility(string typeName);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/docs/changelog.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 0.9.3 (22nd November 2019)
4 |
5 | - Fixes a bug that allows relationships to be created between parents and children.
6 | - Added support for perspectives on elements and relationships.
7 | - Add new C4PlantUMLWriter with C4-PlantUML support (issue #47).
8 | - Added support for element stroke colours.
9 |
10 | ## 0.9.2 (16th September 2019)
11 |
12 | - Fixes a bug where element and relationship positioning is lost when workspaces are merged.
13 |
14 | ## 0.9.1 (10th September 2019)
15 |
16 | - The terminology for relationships can now be customised.
17 | - Added support for icons on element styles.
18 | - Top-level deployment nodes can now be given an environment property, to represent which deployment environment they belong to (e.g. "Development", "Live", etc).
19 | - Relationships can no longer be created between container instances (__breaking change__).
20 | - Fixed issue #40 (WorkspaceUtils does not load containers' properties).
21 | - Provided a way to customize the sort order when displaying the list of views.
22 | - Added the ability to lock and unlock workspaces, to prevent concurrent updates.
23 | - Fixes a bug with the PlantUML writer, where relationships were sorted incorrectly (alphabetically, rather than numerically).
24 |
25 | ## 0.9.0 (8th November 2018)
26 |
27 | - Added the ability to specify users who should have read-write or read-only workspace access, via the ```workspace.Configuration.AddUser(username, role)``` method.
28 |
29 | ## 0.8.3 (24th October 2018)
30 |
31 | - Fixes a bug where client-side encrypted workspaces could not be created.
32 |
33 | ## 0.8.2 (23rd October 2018)
34 |
35 | - Added name-value properties to relationships.
36 | - Added the ability to define animations on the static structure diagrams.
37 | - Removed support for colours in the corporate branding feature (__breaking change__).
38 |
39 | ## 0.8.0
40 |
41 | - Added validation for hex colour codes (on ElementStyle and RelationshipStyle)
42 | - Removed the "groups" property of documentation sections (__breaking change__).
43 | - Added support for the HTTP-based health checks feature.
44 | - Added an ```EndParallelSequence(boolean)``` method to the ```DynamicView``` class, which allows sequence numbering to continue.
45 | - Added support for decision records.
46 | - Moved PlantUML support to a separate Structurizr.PlantUML project.
47 | - Separated the API client from the core library, and created a new Structurizr.Client project.
48 | - Renamed the "type" property on Section to "title".
49 |
50 | ## 0.7.2
51 |
52 | - Added some new shapes: web browser, mobile device (portrait and landscape), and robot.
53 | - Added the ability to enable/disable the enterprise boundary on system landscape and system context views.
54 | - Added the ability to customise the terminology used when rendering views.
55 | - Added the ability to hide element metadata and/or descriptions.
56 | - Bug fixes and performance enhancements.
--------------------------------------------------------------------------------
/docs/plantuml.md:
--------------------------------------------------------------------------------
1 | # PlantUML
2 |
3 | Structurizr for .NET also includes a simple exporter that can create diagram definitions compatible with [PlantUML](http://www.plantuml.com). The following diagram types are supported:
4 |
5 | - Enterprise Context
6 | - System Context
7 | - Container
8 | - Component
9 | - Dynamic
10 | - Deployment
11 |
12 | Simply create your software architecture model and views as usual, and use the [PlantUMLWriter](https://github.com/structurizr/dotnet/blob/master/Structurizr.Core/IO/PlantUML/PlantUMLWriter.cs) class to export the views. [For example](https://github.com/structurizr/dotnet/blob/master/Structurizr.Examples/PlantUML.cs):
13 |
14 | ```c#
15 | Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
16 | Model model = workspace.Model;
17 |
18 | Person user = model.AddPerson("User", "A user of my software system.");
19 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
20 | user.Uses(softwareSystem, "Uses");
21 |
22 | ViewSet views = workspace.Views;
23 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
24 | contextView.AddAllSoftwareSystems();
25 | contextView.AddAllPeople();
26 |
27 | StringWriter stringWriter = new StringWriter();
28 | PlantUMLWriter plantUMLWriter = new PlantUMLWriter();
29 | plantUMLWriter.Write(workspace, stringWriter);
30 | Console.WriteLine(stringWriter.ToString());
31 | ```
32 |
33 | This code will generate and output a PlantUML diagram definition that looks like this:
34 |
35 | ```
36 | @startuml
37 | title Software System - System Context
38 | caption An example of a System Context diagram.
39 | component "Software System" <> as 2
40 | actor "User" <> as 1
41 | 1 ..> 2 : Uses
42 | @enduml
43 | ```
44 |
45 | If you copy/paste this into [PlantUML online](http://www.plantuml.com/plantuml/), you will get something like this:
46 |
47 | 
48 |
49 | ## Benefits of using PlantUML with Structurizr
50 |
51 | The key benefit of using PlantUML in conjunction with the Structurizr client library is that you can create diagrams from a __model__ of your software system. The model provides a set of rules that must be followed; related to elements, relationships, and how they are exposed using diagrams. This means:
52 |
53 | 1. Rather than looking after a collection of disjointed PlantUML diagram definitions, you can create many PlantUML diagrams from a single model and keep them all up to date easily, especially if integrated with your continuous build server and build pipeline.
54 | 1. The naming of elements and the definition of relationships between elements _remains consistent across diagrams_.
55 | 1. The software architecture model at the component level can be created by extracting components from a codebase, using _static analysis and reflection techniques_.
56 |
--------------------------------------------------------------------------------
/Structurizr.Examples/C4PlantUML.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using Structurizr.IO.C4PlantUML;
5 | using Structurizr.IO.C4PlantUML.ModelExtensions;
6 |
7 | namespace Structurizr.Examples
8 | {
9 | ///
10 | /// An example of how to use the C4PlantUML writer. Run this program and copy/paste
11 | /// the output into http://www.plantuml.com/plantuml/
12 | ///
13 | public class C4PlantUML
14 | {
15 | static void Main()
16 | {
17 | Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
18 | Model model = workspace.Model;
19 |
20 | model.Enterprise = new Enterprise("Some Enterprise");
21 |
22 | Person user = model.AddPerson("User", "A user of my software system.");
23 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
24 | var userUsesSystemRelation = user.Uses(softwareSystem, "Uses");
25 | // a direction could be added to relation (active in all views)
26 | // userUsesSystemRelation.SetDirection(DirectionValues.Right);
27 |
28 | ViewSet views = workspace.Views;
29 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
30 | contextView.AddAllSoftwareSystems();
31 | contextView.AddAllPeople();
32 |
33 | // C4PlantUMLWriter support view specific directions too, e.g. "User" should be left of "Software System" only in this view
34 | contextView.Relationships
35 | .First(rv => rv.Relationship.SourceId == user.Id && rv.Relationship.DestinationId == softwareSystem.Id)
36 | .SetDirection(DirectionValues.Right);
37 |
38 | using (var stringWriter = new StringWriter())
39 | {
40 | var plantUmlWriter = new C4PlantUmlWriter();
41 | plantUmlWriter.Write(workspace, stringWriter);
42 | Console.WriteLine(stringWriter.ToString());
43 | }
44 |
45 |
46 | Container webApplication = softwareSystem.AddContainer("Web Application", "Delivers content", "Java and spring MVC");
47 | Container database = softwareSystem.AddContainer("Database", "Stores information", "Relational Database Schema");
48 | // Additional mark it as database
49 | database.SetIsDatabase(true);
50 | user.Uses(webApplication, "uses", "HTTP");
51 | webApplication.Uses(database, "Reads from and writes to", "JDBC").SetDirection(DirectionValues.Right);
52 |
53 | var containerView = views.CreateContainerView(softwareSystem, "containers", "");
54 | containerView.AddAllElements();
55 |
56 | using (var stringWriter = new StringWriter())
57 | {
58 | var plantUmlWriter = new C4PlantUmlWriter();
59 | plantUmlWriter.Write(containerView, stringWriter);
60 | Console.WriteLine(stringWriter.ToString());
61 | }
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection.Examples/StructurizrAnnotations.cs:
--------------------------------------------------------------------------------
1 | using Structurizr.Analysis;
2 | using Structurizr.Api;
3 |
4 | namespace Structurizr.Examples
5 | {
6 |
7 | ///
8 | /// A small example that illustrates how to use the Structurizr annotations
9 | /// in conjunction with the StructurizrAnnotationsComponentFinderStrategy.
10 | ///
11 | /// The live workspace is available to view at https://structurizr.com/share/38341
12 | ///
13 | public class StructurizrAnnotations
14 | {
15 | private const string DatabaseTag = "Database";
16 |
17 | private const long WorkspaceId = 38341;
18 | private const string ApiKey = "key";
19 | private const string ApiSecret = "secret";
20 |
21 | static void Main()
22 | {
23 | Workspace workspace = new Workspace("Structurizr for .NET Annotations", "This is a model of my software system.");
24 | Model model = workspace.Model;
25 |
26 | Person user = model.AddPerson("User", "A user of my software system.");
27 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
28 |
29 | Container webApplication = softwareSystem.AddContainer("Web Application", "Provides users with information.", "C#");
30 | Container database = softwareSystem.AddContainer("Database", "Stores information.", "Relational database schema");
31 | database.AddTags(DatabaseTag);
32 |
33 | ComponentFinder componentFinder = new ComponentFinder(
34 | webApplication,
35 | "Structurizr.Examples.Annotations",
36 | new StructurizrAnnotationsComponentFinderStrategy()
37 | );
38 | componentFinder.FindComponents();
39 | model.AddImplicitRelationships();
40 |
41 | ViewSet views = workspace.Views;
42 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
43 | contextView.AddAllElements();
44 |
45 | ContainerView containerView = views.CreateContainerView(softwareSystem, "Containers", "The container diagram from my software system.");
46 | containerView.AddAllElements();
47 |
48 | ComponentView componentView = views.CreateComponentView(webApplication, "Components", "The component diagram for the web application.");
49 | componentView.AddAllElements();
50 |
51 | Styles styles = views.Configuration.Styles;
52 | styles.Add(new ElementStyle(Tags.Element) { Color = "#ffffff" });
53 | styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd" });
54 | styles.Add(new ElementStyle(Tags.Container) { Background = "#438dd5" });
55 | styles.Add(new ElementStyle(Tags.Component) { Background = "#85bbf0", Color = "#000000" });
56 | styles.Add(new ElementStyle(Tags.Person) { Background = "#08427b", Shape = Shape.Person });
57 | styles.Add(new ElementStyle(DatabaseTag) { Shape = Shape.Cylinder });
58 |
59 | StructurizrClient structurizrClient = new StructurizrClient(ApiKey, ApiSecret);
60 | structurizrClient.PutWorkspace(WorkspaceId, workspace);
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/Structurizr.Examples/AdrTools.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using Structurizr.AdrTools;
3 | using Structurizr.Api;
4 |
5 | namespace Structurizr.Examples
6 | {
7 |
8 | ///
9 | /// An example of how to import architecture decision records from Nat Pryce's adr-tools tool.
10 | ///
11 | /// The live workspace is available to view at https://structurizr.com/share/39459
12 | ///
13 | class AdrTools
14 | {
15 |
16 | private const long WorkspaceId = 39459;
17 | private const string ApiKey = "key";
18 | private const string ApiSecret = "secret";
19 |
20 | private const string FileSystemTag = "File System";
21 |
22 | static void Main()
23 | {
24 | Workspace workspace = new Workspace("adr-tools", "A description of the adr-tools command line utility.");
25 | Model model = workspace.Model;
26 |
27 | Person user = model.AddPerson("User", "Somebody on a software development team.");
28 | SoftwareSystem adrTools = model.AddSoftwareSystem("adr-tools", "A command-line tool for working with Architecture Decision Records (ADRs).");
29 | adrTools.Url ="https://github.com/npryce/adr-tools";
30 |
31 | Container adrShellScripts = adrTools.AddContainer("adr", "A command-line tool for working with Architecture Decision Records (ADRs).", "Shell Scripts");
32 | adrShellScripts.Url = "https://github.com/npryce/adr-tools/tree/master/src";
33 | Container fileSystem = adrTools.AddContainer("File System", "Stores ADRs, templates, etc.", "File System");
34 | fileSystem.AddTags(FileSystemTag);
35 | user.Uses(adrShellScripts, "Manages ADRs using");
36 | adrShellScripts.Uses(fileSystem, "Reads from and writes to");
37 | model.AddImplicitRelationships();
38 |
39 | ViewSet views = workspace.Views;
40 | SystemContextView contextView = views.CreateSystemContextView(adrTools, "SystemContext", "The system context diagram for adr-tools.");
41 | contextView.AddAllElements();
42 |
43 | ContainerView containerView = views.CreateContainerView(adrTools, "Containers", "The container diagram for adr-tools.");
44 | containerView.AddAllElements();
45 |
46 | DirectoryInfo adrDirectory = new DirectoryInfo("Documentation" + Path.DirectorySeparatorChar + "adr");
47 |
48 | AdrToolsImporter adrToolsImporter = new AdrToolsImporter(workspace, adrDirectory);
49 | adrToolsImporter.ImportArchitectureDecisionRecords(adrTools);
50 |
51 | Styles styles = views.Configuration.Styles;
52 | styles.Add(new ElementStyle(Tags.Element) { Shape = Shape.RoundedBox, Color = "#ffffff" });
53 | styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#18ADAD", Color = "#ffffff" });
54 | styles.Add(new ElementStyle(Tags.Person) { Shape = Shape.Person, Background = "#008282", Color = "#ffffff" });
55 | styles.Add(new ElementStyle(Tags.Container) { Background = "#6DBFBF" });
56 | styles.Add(new ElementStyle(FileSystemTag) { Shape = Shape.Folder });
57 |
58 | StructurizrClient structurizrClient = new StructurizrClient(ApiKey, ApiSecret);
59 | structurizrClient.PutWorkspace(WorkspaceId, workspace);
60 | }
61 |
62 | }
63 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/NameSuffixTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Implements a component identification rule based on matching a type name against a case-sensitive suffix.
7 | ///
8 | ///
9 | ///
10 | /// The NameSuffixTypeMatcher class provides component identification by looking for types whose names end with the
11 | /// value of the property. The name comparison is case-sensitive.
12 | ///
13 | ///
14 | public class NameSuffixTypeMatcher : ITypeMatcher
15 | {
16 | ///
17 | /// Gets the string used for suffix comparison for identifying components with this rule.
18 | ///
19 | /// A string used for case-sensitive comparisons against types used by the rule.
20 | public string Suffix { get; private set; }
21 |
22 | ///
23 | /// Gets the description to use for components matching the rule.
24 | ///
25 | /// A string containing the Description property for matching components.
26 | ///
27 | ///
28 | public string Description { get; private set; }
29 |
30 | ///
31 | /// Gets the technology name(s) to use for components matching the rule.
32 | ///
33 | /// A string containing the Technology property for matching components.
34 | ///
35 | ///
36 | public string Technology { get; private set; }
37 |
38 | ///
39 | /// Creates a new instance of using the provided suffix string.
40 | ///
41 | /// A string used for case-sensitive comparisons against the end of type names.
42 | /// The description to set on components found by this rule.
43 | /// The technology name(s) to set on components found by this rule.
44 | public NameSuffixTypeMatcher(string suffix, string description, string technology)
45 | {
46 | this.Suffix = suffix;
47 | this.Description = description;
48 | this.Technology = technology;
49 | }
50 |
51 | ///
52 | ///
53 | ///
54 | /// The rule implemented by identifies components from types whose names
55 | /// end with the case-sensitive string in . All types in the assembly whose names end with
56 | /// that suffix will be identified as a component and added to the model.
57 | ///
58 | ///
59 | public bool Matches(TypeDefinition type)
60 | {
61 | return type.Name.EndsWith(this.Suffix);
62 | }
63 |
64 | ///
65 | public string GetDescription()
66 | {
67 | return this.Description;
68 | }
69 |
70 | ///
71 | public string GetTechnology()
72 | {
73 | return this.Technology;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/ExtendsClassTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | using Structurizr.Cecil;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 | ///
8 | /// Implements a component identification rule based on inheritance from a class.
9 | ///
10 | ///
11 | ///
12 | /// The ExtendsClassTypeMatcher class provides component identification by looking for types which are subtypes of
13 | /// the class represented by the property.
14 | ///
15 | ///
16 | public class ExtendsClassTypeMatcher : ITypeMatcher
17 | {
18 | ///
19 | /// Gets the type information of the base class used for identifying components for this rule.
20 | ///
21 | /// The object representing the class used by the rule.
22 | public TypeDefinition ClassType { get; private set; }
23 |
24 | ///
25 | /// Gets the description to use for components matching the rule.
26 | ///
27 | /// A string containing the Description property for matching components.
28 | ///
29 | ///
30 | public string Description { get; private set; }
31 |
32 | ///
33 | /// Gets the technology name(s) to use for components matching the rule.
34 | ///
35 | /// A string containing the Technology property for matching components.
36 | ///
37 | ///
38 | public string Technology { get; private set; }
39 |
40 | ///
41 | /// Creates a new instance of based on the provided class.
42 | ///
43 | ///
44 | /// The object representing the base class that will be used to identify components.
45 | ///
46 | /// The description to set on components found by this rule.
47 | /// The technology name(s) to set on components found by this rule.
48 | public ExtendsClassTypeMatcher(TypeDefinition classType, string description, string technology)
49 | {
50 | this.ClassType = classType;
51 | this.Description = description;
52 | this.Technology = technology;
53 | }
54 |
55 | ///
56 | ///
57 | ///
58 | /// The rule implemented by identifies components from types that
59 | /// inherit from the class represented by . All types in the assembly under analysis
60 | /// which have that class as a base type at any depth of inheritance will be identified as a component and
61 | /// added to the model.
62 | ///
63 | ///
64 | public bool Matches(TypeDefinition type)
65 | {
66 | return type.IsSubclassOf(this.ClassType);
67 | }
68 |
69 | ///
70 | public string GetDescription()
71 | {
72 | return this.Description;
73 | }
74 |
75 | ///
76 | public string GetTechnology()
77 | {
78 | return this.Technology;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/NameSuffixTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Implements a component identification rule based on matching a type name against a case-sensitive suffix.
7 | ///
8 | ///
9 | ///
10 | /// The NameSuffixTypeMatcher class provides component identification by looking for types whose names end with the
11 | /// value of the property. The name comparison is case-sensitive.
12 | ///
13 | ///
14 | public class NameSuffixTypeMatcher : ITypeMatcher
15 | {
16 |
17 | ///
18 | /// Gets the string used for suffix comparison for identifying components with this rule.
19 | ///
20 | /// A string used for case-sensitive comparisons against types used by the rule.
21 | public string Suffix { get; private set; }
22 |
23 | ///
24 | /// Gets the description to use for components matching the rule.
25 | ///
26 | /// A string containing the Description property for matching components.
27 | ///
28 | ///
29 | public string Description { get; private set; }
30 |
31 | ///
32 | /// Gets the technology name(s) to use for components matching the rule.
33 | ///
34 | /// A string containing the Technology property for matching components.
35 | ///
36 | ///
37 | public string Technology { get; private set; }
38 |
39 | ///
40 | /// Creates a new instance of using the provided suffix string.
41 | ///
42 | /// A string used for case-sensitive comparisons against the end of type names.
43 | /// The description to set on components found by this rule.
44 | /// The technology name(s) to set on components found by this rule.
45 | public NameSuffixTypeMatcher(string suffix, string description, string technology)
46 | {
47 | this.Suffix = suffix;
48 | this.Description = description;
49 | this.Technology = technology;
50 |
51 | if (Suffix == null || Suffix.Trim().Length == 0)
52 | {
53 | throw new ArgumentException("A suffix must be supplied");
54 | }
55 | }
56 |
57 | ///
58 | ///
59 | ///
60 | /// The rule implemented by identifies components from types whose names
61 | /// end with the case-sensitive string in . All types in the assembly whose names end with
62 | /// that suffix will be identified as a component and added to the model.
63 | ///
64 | ///
65 | public bool Matches(Type type)
66 | {
67 | return type.Name.EndsWith(Suffix);
68 | }
69 |
70 | ///
71 | public string GetDescription()
72 | {
73 | return this.Description;
74 | }
75 |
76 | ///
77 | public string GetTechnology()
78 | {
79 | return this.Technology;
80 | }
81 |
82 | }
83 | }
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/ExtendsClassMatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Implements a component identification rule based on inheritance from a class.
7 | ///
8 | ///
9 | ///
10 | /// The ExtendsClassTypeMatcher class provides component identification by looking for types which are subtypes of
11 | /// the class represented by the property.
12 | ///
13 | ///
14 | public class ExtendsClassTypeMatcher : ITypeMatcher
15 | {
16 |
17 | ///
18 | /// Gets the type information of the base class used for identifying components for this rule.
19 | ///
20 | /// The object representing the class used by the rule.
21 | public Type ClassType { get; private set; }
22 |
23 | ///
24 | /// Gets the description to use for components matching the rule.
25 | ///
26 | /// A string containing the Description property for matching components.
27 | ///
28 | ///
29 | public string Description { get; private set; }
30 |
31 | ///
32 | /// Gets the technology name(s) to use for components matching the rule.
33 | ///
34 | /// A string containing the Technology property for matching components.
35 | ///
36 | ///
37 | public string Technology { get; private set; }
38 |
39 | ///
40 | /// Creates a new instance of based on the provided class.
41 | ///
42 | ///
43 | /// The object representing the base class that will be used to identify components.
44 | ///
45 | /// The description to set on components found by this rule.
46 | /// The technology name(s) to set on components found by this rule.
47 | public ExtendsClassTypeMatcher(Type classType, string description, string technology)
48 | {
49 | this.ClassType = classType;
50 | this.Description = description;
51 | this.Technology = technology;
52 |
53 | if (!ClassType.IsClass)
54 | {
55 | throw new ArgumentException(ClassType.FullName + " is not a class type");
56 | }
57 | }
58 |
59 | ///
60 | ///
61 | ///
62 | /// The rule implemented by identifies components from types that
63 | /// inherit from the class represented by . All types in the assembly under analysis
64 | /// which have that class as a base type at any depth of inheritance will be identified as a component and
65 | /// added to the model.
66 | ///
67 | ///
68 | public bool Matches(Type type)
69 | {
70 | return type.IsSubclassOf(ClassType);
71 | }
72 |
73 | ///
74 | public string GetDescription()
75 | {
76 | return this.Description;
77 | }
78 |
79 | ///
80 | public string GetTechnology()
81 | {
82 | return this.Technology;
83 | }
84 |
85 | }
86 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/InterfaceImplementationTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using Mono.Cecil;
2 |
3 | using Structurizr.Cecil;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 | ///
8 | /// Implements a component identification rule based on implementation of an interface.
9 | ///
10 | ///
11 | ///
12 | /// The InterfaceImplementationTypeMatcher class provides component identification by looking for types which
13 | /// implement the interface represented by the property.
14 | ///
15 | ///
16 | public class InterfaceImplementationTypeMatcher : ITypeMatcher
17 | {
18 | ///
19 | /// Gets the type information of the interface used for identifying components for this rule.
20 | ///
21 | /// The object representing the interface used by the rule.
22 | public TypeDefinition InterfaceType { get; private set; }
23 |
24 | ///
25 | /// Gets the description to use for components matching the rule.
26 | ///
27 | /// A string containing the Description property for matching components.
28 | ///
29 | ///
30 | public string Description { get; private set; }
31 |
32 | ///
33 | /// Gets the technology name(s) to use for components matching the rule.
34 | ///
35 | /// A string containing the Technology property for matching components.
36 | ///
37 | ///
38 | public string Technology { get; private set; }
39 |
40 | ///
41 | /// Creates a new instance of based on the provided interface.
42 | ///
43 | ///
44 | /// The object representing the interface that will be used to identify components.
45 | ///
46 | /// The description to set on components found by this rule.
47 | /// The technology name(s) to set on components found by this rule.
48 | public InterfaceImplementationTypeMatcher(TypeDefinition interfaceType,
49 | string description,
50 | string technology)
51 | {
52 | this.InterfaceType = interfaceType;
53 | this.Description = description;
54 | this.Technology = technology;
55 | }
56 |
57 | ///
58 | ///
59 | ///
60 | /// The rule implemented by identifies components from types
61 | /// that implement the interface represented by . All types in the assembly under
62 | /// analysis which implement the interface at any depth of inheritance will be identified as a component and
63 | /// added to the model.
64 | ///
65 | ///
66 | public bool Matches(TypeDefinition type)
67 | {
68 | return this.InterfaceType.IsAssignableFrom(type);
69 | }
70 |
71 | ///
72 | public string GetDescription()
73 | {
74 | return this.Description;
75 | }
76 |
77 | ///
78 | public string GetTechnology()
79 | {
80 | return this.Technology;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/InterfaceImplementationTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Structurizr.Analysis
4 | {
5 | ///
6 | /// Implements a component identification rule based on implementation of an interface.
7 | ///
8 | ///
9 | ///
10 | /// The InterfaceImplementationTypeMatcher class provides component identification by looking for types which
11 | /// implement the interface represented by the property.
12 | ///
13 | ///
14 | public class InterfaceImplementationTypeMatcher : ITypeMatcher
15 | {
16 |
17 | ///
18 | /// Gets the type information of the interface used for identifying components for this rule.
19 | ///
20 | /// The object representing the interface used by the rule.
21 | public Type InterfaceType { get; private set; }
22 |
23 | ///
24 | /// Gets the description to use for components matching the rule.
25 | ///
26 | /// A string containing the Description property for matching components.
27 | ///
28 | ///
29 | public string Description { get; private set; }
30 |
31 | ///
32 | /// Gets the technology name(s) to use for components matching the rule.
33 | ///
34 | /// A string containing the Technology property for matching components.
35 | ///
36 | ///
37 | public string Technology { get; private set; }
38 |
39 | ///
40 | /// Creates a new instance of based on the provided interface.
41 | ///
42 | ///
43 | /// The object representing the interface that will be used to identify components.
44 | ///
45 | /// The description to set on components found by this rule.
46 | /// The technology name(s) to set on components found by this rule.
47 | public InterfaceImplementationTypeMatcher(Type interfaceType, string description, string technology)
48 | {
49 | this.InterfaceType = interfaceType;
50 | this.Description = description;
51 | this.Technology = technology;
52 |
53 | if (!InterfaceType.IsInterface)
54 | {
55 | throw new ArgumentException(InterfaceType.FullName + " is not an interface type");
56 | }
57 | }
58 |
59 | ///
60 | ///
61 | ///
62 | /// The rule implemented by identifies components from types
63 | /// that implement the interface represented by . All types in the assembly under
64 | /// analysis which implement the interface at any depth of inheritance will be identified as a component and
65 | /// added to the model.
66 | ///
67 | ///
68 | public bool Matches(Type type)
69 | {
70 | return InterfaceType.IsAssignableFrom(type);
71 | }
72 |
73 | ///
74 | public string GetDescription()
75 | {
76 | return this.Description;
77 | }
78 |
79 | ///
80 | public string GetTechnology()
81 | {
82 | return this.Technology;
83 | }
84 |
85 | }
86 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil.Examples/StructurizrAnnotations.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | using Mono.Cecil;
4 |
5 | using Structurizr.Analysis;
6 | using Structurizr.Api;
7 |
8 | namespace Structurizr.Examples
9 | {
10 |
11 | ///
12 | /// A small example that illustrates how to use the Structurizr annotations
13 | /// in conjunction with the StructurizrAnnotationsComponentFinderStrategy.
14 | ///
15 | /// The live workspace is available to view at https://structurizr.com/share/38339
16 | ///
17 | public class StructurizrAnnotations
18 | {
19 | private const string DatabaseTag = "Database";
20 |
21 | private const long WorkspaceId = 38339;
22 | private const string ApiKey = "key";
23 | private const string ApiSecret = "secret";
24 |
25 | static void Main()
26 | {
27 | Workspace workspace = new Workspace("Structurizr for .NET Annotations", "This is a model of my software system.");
28 | Model model = workspace.Model;
29 |
30 | Person user = model.AddPerson("User", "A user of my software system.");
31 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
32 |
33 | Container webApplication = softwareSystem.AddContainer("Web Application", "Provides users with information.", "C#");
34 | Container database = softwareSystem.AddContainer("Database", "Stores information.", "Relational database schema");
35 | database.AddTags(DatabaseTag);
36 |
37 | string assemblyPath = typeof(StructurizrAnnotations).Assembly.Location;
38 | DefaultAssemblyResolver resolver = new DefaultAssemblyResolver();
39 | resolver.AddSearchDirectory(Path.GetDirectoryName(assemblyPath));
40 | AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(
41 | assemblyPath,
42 | new ReaderParameters { AssemblyResolver = resolver }
43 | );
44 |
45 | ComponentFinder componentFinder = new ComponentFinder(
46 | webApplication,
47 | "Structurizr.Examples.Annotations",
48 | new StructurizrAnnotationsComponentFinderStrategy(assembly)
49 | );
50 | componentFinder.FindComponents();
51 | model.AddImplicitRelationships();
52 |
53 | ViewSet views = workspace.Views;
54 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
55 | contextView.AddAllElements();
56 |
57 | ContainerView containerView = views.CreateContainerView(softwareSystem, "Containers", "The container diagram from my software system.");
58 | containerView.AddAllElements();
59 |
60 | ComponentView componentView = views.CreateComponentView(webApplication, "Components", "The component diagram for the web application.");
61 | componentView.AddAllElements();
62 |
63 | Styles styles = views.Configuration.Styles;
64 | styles.Add(new ElementStyle(Tags.Element) { Color = "#ffffff" });
65 | styles.Add(new ElementStyle(Tags.SoftwareSystem) { Background = "#1168bd" });
66 | styles.Add(new ElementStyle(Tags.Container) { Background = "#438dd5" });
67 | styles.Add(new ElementStyle(Tags.Component) { Background = "#85bbf0", Color = "#000000" });
68 | styles.Add(new ElementStyle(Tags.Person) { Background = "#08427b", Shape = Shape.Person });
69 | styles.Add(new ElementStyle(DatabaseTag) { Shape = Shape.Cylinder });
70 |
71 | StructurizrClient structurizrClient = new StructurizrClient(ApiKey, ApiSecret);
72 | structurizrClient.PutWorkspace(WorkspaceId, workspace);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/CustomAttributeTypeMatcher.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 |
3 | using Mono.Cecil;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 | ///
8 | /// Implements a component identification rule based on the use of custom attributes.
9 | ///
10 | ///
11 | ///
12 | /// The CustomAttributeTypeMatcher class provides component identification by looking for types decorated with the
13 | /// attribute represented by the property. This can be any attribute class from the
14 | /// assembly under analysis or any of its dependencies.
15 | ///
16 | ///
17 | public class CustomAttributeTypeMatcher : ITypeMatcher
18 | {
19 | ///
20 | /// Gets the type information of the attribute type used for identifying components for this rule.
21 | ///
22 | /// The object representing the attribute type used by the rule.
23 | public TypeDefinition AttributeType { get; private set; }
24 |
25 | ///
26 | /// Gets the description to use for components matching the rule.
27 | ///
28 | /// A string containing the Description property for matching components.
29 | ///
30 | ///
31 | public string Description { get; private set; }
32 |
33 | ///
34 | /// Gets the technology name(s) to use for components matching the rule.
35 | ///
36 | /// A string containing the Technology property for matching components.
37 | ///
38 | ///
39 | public string Technology { get; private set; }
40 |
41 | ///
42 | /// Creates a new instance of based on the provided attribute type.
43 | ///
44 | ///
45 | /// The object representing the attribute type that will be used to identify components.
46 | ///
47 | /// The description to set on components found by this rule.
48 | /// The technology name(s) to set on components found by this rule.
49 | public CustomAttributeTypeMatcher(TypeDefinition attributeType,
50 | string description,
51 | string technology)
52 | {
53 | this.AttributeType = attributeType;
54 | this.Description = description;
55 | this.Technology = technology;
56 | }
57 |
58 | ///
59 | ///
60 | ///
61 | /// The rule implemented by identifies components from types that are
62 | /// decorated with the attribute class represented by . All types in the assembly
63 | /// under analysis that have this attribute on the type itself (not on any of its members) will be identified
64 | /// as a component by the rule and added to the model.
65 | ///
66 | ///
67 | public bool Matches(TypeDefinition type)
68 | {
69 | return type.CustomAttributes.Any(ca => ca.AttributeType.Resolve().MetadataToken == this.AttributeType.MetadataToken);
70 | }
71 |
72 | ///
73 | public string GetDescription()
74 | {
75 | return this.Description;
76 | }
77 |
78 | ///
79 | public string GetTechnology()
80 | {
81 | return this.Technology;
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/SupportingTypes/ReferencedTypesSupportingTypesStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 |
8 | ///
9 | /// Implements a which finds all types referenced by the primary code element
10 | /// of a , and optionally those referenced by its secondary code elements.
11 | ///
12 | public class ReferencedTypesSupportingTypesStrategy : SupportingTypesStrategy
13 | {
14 |
15 | private bool _includeIndirectlyReferencedTypes;
16 |
17 | ///
18 | /// Creates a new instance of which includes the option to
19 | /// find supporting types from secondary code elements.
20 | ///
21 | public ReferencedTypesSupportingTypesStrategy() : this(true)
22 | {
23 | }
24 |
25 | ///
26 | /// Creates a new instance of which allows the option to
27 | /// find supporting types from secondary code elements to be expressly enabled or disabled.
28 | ///
29 | ///
30 | /// Whether or not to include supporting types identified from secondary code elements of the component.
31 | ///
32 | ///
33 | ///
34 | /// Setting to is equivalent to using
35 | /// the default constructor for , and will find and add to
36 | /// a component's supporting types any types referenced by the secondary code elements of the component, in
37 | /// addition to those types referenced by the primary code element of the component. Setting the param to
38 | /// instead will only find and add those types which are referenced directly by the
39 | /// primary code element of the component.
40 | ///
41 | ///
42 | /// Only those types that are in the same namespace as the code elements of a component will actually be added
43 | /// as supporting types of the component.
44 | ///
45 | ///
46 | public ReferencedTypesSupportingTypesStrategy(bool includeIndirectlyReferencedTypes)
47 | {
48 | _includeIndirectlyReferencedTypes = includeIndirectlyReferencedTypes;
49 | }
50 |
51 | ///
52 | public override HashSet FindSupportingTypes(Component component)
53 | {
54 | HashSet referencedTypes = new HashSet();
55 | referencedTypes.UnionWith(GetReferencedTypesInNamespace(component.Type));
56 |
57 | foreach (CodeElement codeElement in component.CodeElements)
58 | {
59 | referencedTypes.UnionWith(GetReferencedTypesInNamespace(codeElement.Type));
60 | }
61 |
62 | if (_includeIndirectlyReferencedTypes) {
63 | int numberOfTypes = referencedTypes.Count;
64 | bool foundMore = true;
65 | while (foundMore) {
66 | HashSet indirectlyReferencedTypes = new HashSet();
67 | foreach (string type in referencedTypes)
68 | {
69 | indirectlyReferencedTypes.UnionWith(GetReferencedTypesInNamespace(type));
70 | }
71 | referencedTypes.UnionWith(indirectlyReferencedTypes);
72 |
73 | if (referencedTypes.Count > numberOfTypes)
74 | {
75 | foundMore = true;
76 | numberOfTypes = referencedTypes.Count;
77 | }
78 | else
79 | {
80 | foundMore = false;
81 | }
82 | }
83 | }
84 |
85 | return referencedTypes;
86 | }
87 |
88 | private IEnumerable GetReferencedTypesInNamespace(string typeName)
89 | {
90 | IEnumerable referencedTypes = TypeRepository.GetReferencedTypes(typeName);
91 | return referencedTypes.Where(t => t.StartsWith(TypeRepository.Namespace));
92 | }
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/SupportingTypes/ReferencedTypesSupportingTypesStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Structurizr.Analysis.SupportingTypes
5 | {
6 | ///
7 | /// Implements a which finds all types referenced by the primary code element
8 | /// of a , and optionally those referenced by its secondary code elements.
9 | ///
10 | public class ReferencedTypesSupportingTypesStrategy : SupportingTypesStrategy
11 | {
12 | private bool _includeIndirectlyReferencedTypes;
13 |
14 | ///
15 | /// Creates a new instance of which includes the option to
16 | /// find supporting types from secondary code elements.
17 | ///
18 | public ReferencedTypesSupportingTypesStrategy() : this(true)
19 | {
20 | }
21 |
22 | ///
23 | /// Creates a new instance of which allows the option to
24 | /// find supporting types from secondary code elements to be expressly enabled or disabled.
25 | ///
26 | ///
27 | /// Whether or not to include supporting types identified from secondary code elements of the component.
28 | ///
29 | ///
30 | ///
31 | /// Setting to is equivalent to using
32 | /// the default constructor for , and will find and add to
33 | /// a component's supporting types any types referenced by the secondary code elements of the component, in
34 | /// addition to those types referenced by the primary code element of the component. Setting the param to
35 | /// instead will only find and add those types which are referenced directly by the
36 | /// primary code element of the component.
37 | ///
38 | ///
39 | /// Only those types that are in the same namespace as the code elements of a component will actually be added
40 | /// as supporting types of the component.
41 | ///
42 | ///
43 | public ReferencedTypesSupportingTypesStrategy(bool includeIndirectlyReferencedTypes)
44 | {
45 | _includeIndirectlyReferencedTypes = includeIndirectlyReferencedTypes;
46 | }
47 |
48 | ///
49 | public override HashSet FindSupportingTypes(Component component)
50 | {
51 | HashSet referencedTypes = new HashSet();
52 | referencedTypes.UnionWith(GetReferencedTypesInNamespace(component.Type));
53 |
54 | foreach (CodeElement codeElement in component.CodeElements)
55 | {
56 | referencedTypes.UnionWith(GetReferencedTypesInNamespace(codeElement.Type));
57 | }
58 |
59 | if (_includeIndirectlyReferencedTypes)
60 | {
61 | int numberOfTypes = referencedTypes.Count;
62 | bool foundMore = true;
63 | while (foundMore)
64 | {
65 | HashSet indirectlyReferencedTypes = new HashSet();
66 | foreach (string type in referencedTypes)
67 | {
68 | indirectlyReferencedTypes.UnionWith(GetReferencedTypesInNamespace(type));
69 | }
70 | referencedTypes.UnionWith(indirectlyReferencedTypes);
71 |
72 | if (referencedTypes.Count > numberOfTypes)
73 | {
74 | foundMore = true;
75 | numberOfTypes = referencedTypes.Count;
76 | }
77 | else
78 | {
79 | foundMore = false;
80 | }
81 | }
82 | }
83 |
84 | return referencedTypes;
85 | }
86 |
87 | private IEnumerable GetReferencedTypesInNamespace(string typeName)
88 | {
89 | IEnumerable referencedTypes = TypeRepository.GetReferencedTypes(typeName);
90 | return referencedTypes.Where(t => t.StartsWith(TypeRepository.Namespace));
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Structurizr.Analysis/Analysis/ComponentFinder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace Structurizr.Analysis
6 | {
7 |
8 | ///
9 | /// This class allows you to find components in a .NET codebase, when used in conjunction
10 | /// with a number of pluggable component finder strategies.
11 | ///
12 | public class ComponentFinder
13 | {
14 |
15 | ///
16 | /// The Container that components will be added to.
17 | ///
18 | public Container Container { get; }
19 |
20 | ///
21 | /// The name of the namespace to be scanned.
22 | ///
23 | public string Namespace { get; }
24 |
25 | ///
26 | /// The set of regexes that define which types should be excluded during the component finding process.
27 | ///
28 | public HashSet Exclusions { get; }
29 |
30 | private readonly List _componentFinderStrategies = new List();
31 |
32 | ///
33 | /// Create a new component finder.
34 | ///
35 | /// The Container that components will be added to
36 | /// The .NET namespace name to be scanned (e.g. "MyCompany.MyApp")
37 | /// One or more ComponentFinderStrategy objects, describing how to find components
38 | public ComponentFinder(Container container, string namespaceToScan, params ComponentFinderStrategy[] componentFinderStrategies)
39 | {
40 | if (container == null)
41 | {
42 | throw new ArgumentException("A container must be specified.");
43 | }
44 |
45 | if (namespaceToScan == null || namespaceToScan.Trim().Length == 0)
46 | {
47 | throw new ArgumentException("A package name must be specified.");
48 | }
49 |
50 | if (componentFinderStrategies.Length == 0)
51 | {
52 | throw new ArgumentException("One or more ComponentFinderStrategy objects must be specified.");
53 | }
54 |
55 | Container = container;
56 | Namespace = namespaceToScan;
57 |
58 | Exclusions = new HashSet();
59 | Exclusions.Add(new Regex(@"System\."));
60 |
61 | foreach (ComponentFinderStrategy componentFinderStrategy in componentFinderStrategies)
62 | {
63 | _componentFinderStrategies.Add(componentFinderStrategy);
64 | componentFinderStrategy.ComponentFinder = this;
65 | }
66 | }
67 |
68 | ///
69 | /// Find components, using all of the configured component finder strategies
70 | /// in the order they were added.
71 | ///
72 | /// The set of components that were found.
73 | public ICollection FindComponents()
74 | {
75 | List componentsFound = new List();
76 |
77 | foreach (ComponentFinderStrategy componentFinderStrategy in _componentFinderStrategies)
78 | {
79 | componentFinderStrategy.BeforeFindComponents();
80 | }
81 |
82 | foreach (ComponentFinderStrategy componentFinderStrategy in _componentFinderStrategies) {
83 | componentsFound.AddRange(componentFinderStrategy.FindComponents());
84 | }
85 |
86 | foreach (ComponentFinderStrategy componentFinderStrategy in _componentFinderStrategies)
87 | {
88 | componentFinderStrategy.AfterFindComponents();
89 | }
90 |
91 | return componentsFound;
92 | }
93 |
94 | ///
95 | /// Adds one or more regexes to the set of regexes that define which types should be excluded during the component finding process.
96 | ///
97 | /// One or more regular expressions, as strings.
98 | public void Exclude(params string[] regexes)
99 | {
100 | foreach (string regex in regexes)
101 | {
102 | Exclusions.Add(new Regex(regex));
103 | }
104 | }
105 |
106 | }
107 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Util/TypeDefinitionExtensions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics;
3 | using System.Linq;
4 |
5 | using Mono.Cecil;
6 |
7 | namespace Structurizr.Cecil
8 | {
9 | ///
10 | /// Provides extension methods for .
11 | ///
12 | ///
13 | /// Sourced from https://stackoverflow.com/questions/40018991/how-to-implement-isassignablefrom-with-mono-cecil
14 | ///
15 | public static class TypeDefinitionExtensions
16 | {
17 | ///
18 | /// Is childTypeDef a subclass of parentTypeDef. Does not test interface inheritance
19 | ///
20 | ///
21 | ///
22 | ///
23 | public static bool IsSubclassOf(this TypeDefinition childTypeDef, TypeDefinition parentTypeDef) =>
24 | (childTypeDef.MetadataToken != parentTypeDef.MetadataToken || childTypeDef.Module.Mvid != parentTypeDef.Module.Mvid)
25 | && childTypeDef
26 | .EnumerateBaseClasses()
27 | .Any(b => b.MetadataToken == parentTypeDef.MetadataToken && b.Module.Mvid == parentTypeDef.Module.Mvid);
28 |
29 | ///
30 | /// Does childType inherit from parentInterface
31 | ///
32 | ///
33 | ///
34 | ///
35 | public static bool DoesAnySubTypeImplementInterface(this TypeDefinition childType, TypeDefinition parentInterfaceDef)
36 | {
37 | Debug.Assert(parentInterfaceDef.IsInterface);
38 | return childType
39 | .EnumerateBaseClasses()
40 | .Any(typeDefinition => typeDefinition.DoesSpecificTypeImplementInterface(parentInterfaceDef));
41 | }
42 |
43 | ///
44 | /// Does the childType directly inherit from parentInterface. Base
45 | /// classes of childType are not tested
46 | ///
47 | ///
48 | ///
49 | ///
50 | public static bool DoesSpecificTypeImplementInterface(this TypeDefinition childTypeDef, TypeDefinition parentInterfaceDef)
51 | {
52 | Debug.Assert(parentInterfaceDef.IsInterface);
53 | return childTypeDef
54 | .Interfaces
55 | .Any(ifaceDef => DoesSpecificInterfaceImplementInterface(ifaceDef.InterfaceType.Resolve(), parentInterfaceDef));
56 | }
57 |
58 | ///
59 | /// Does interface iface0 equal or implement interface iface1
60 | ///
61 | ///
62 | ///
63 | ///
64 | public static bool DoesSpecificInterfaceImplementInterface(TypeDefinition iface0, TypeDefinition iface1)
65 | {
66 | Debug.Assert(iface1.IsInterface);
67 | Debug.Assert(iface0.IsInterface);
68 | return (iface0.MetadataToken == iface1.MetadataToken && iface0.Module.Mvid == iface1.Module.Mvid)
69 | || iface0.DoesAnySubTypeImplementInterface(iface1);
70 | }
71 |
72 | ///
73 | /// Is source type assignable to target type
74 | ///
75 | ///
76 | ///
77 | ///
78 | public static bool IsAssignableFrom(this TypeDefinition target, TypeDefinition source)
79 | => target == source
80 | || (target.MetadataToken == source.MetadataToken && target.Module.Mvid == source.Module.Mvid)
81 | || source.IsSubclassOf(target)
82 | || target.IsInterface && source.DoesAnySubTypeImplementInterface(target);
83 |
84 | ///
85 | /// Enumerate the current type, it's parent and all the way to the top type
86 | ///
87 | ///
88 | ///
89 | public static IEnumerable EnumerateBaseClasses(this TypeDefinition klassType)
90 | {
91 | for (var typeDefinition = klassType; typeDefinition != null; typeDefinition = typeDefinition.BaseType?.Resolve())
92 | {
93 | yield return typeDefinition;
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/Structurizr.Roslyn/Analysis/TypeSummaryComponentFinderStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.CodeAnalysis.MSBuild;
4 | using Microsoft.CodeAnalysis;
5 | using System.Linq;
6 | using System.Xml.Linq;
7 |
8 | namespace Structurizr.Analysis
9 | {
10 |
11 | ///
12 | /// This is a component finder that enriches existing components with the type level summary
13 | /// comment (i.e. this comment). It uses Roslyn and needs access to the source code.
14 | ///
15 | public class TypeSummaryComponentFinderStrategy : ComponentFinderStrategy
16 | {
17 |
18 | private ComponentFinder _componentFinder;
19 | ///
20 | public ComponentFinder ComponentFinder
21 | {
22 | get { return _componentFinder; }
23 | set
24 | {
25 | _componentFinder = value;
26 | }
27 | }
28 |
29 | private HashSet _components = new HashSet();
30 |
31 | ///
32 | /// Gets or sets the path to the solution (.sln) file containing the project to analyze.
33 | ///
34 | /// The file path of the Visual Studio solution file to use.
35 | public string PathToSolution { get; set; }
36 |
37 | ///
38 | /// Gets or sets the name of the project within the solution to analyze.
39 | ///
40 | /// The name of the project to analyze.
41 | public string ProjectName { get; set; }
42 |
43 | ///
44 | /// Creates a new instance of for a specified project within a
45 | /// specified Visual Studio solution.
46 | ///
47 | ///
48 | ///
49 | public TypeSummaryComponentFinderStrategy(string pathToSolution, string projectName)
50 | {
51 | this.PathToSolution = pathToSolution;
52 | this.ProjectName = projectName;
53 | }
54 |
55 | ///
56 | public void BeforeFindComponents()
57 | {
58 | }
59 |
60 | ///
61 | public IEnumerable FindComponents()
62 | {
63 | MSBuildWorkspace msWorkspace = MSBuildWorkspace.Create();
64 |
65 | Solution solution = msWorkspace.OpenSolutionAsync(PathToSolution).Result;
66 | Project project = solution.Projects.Where(p => p.Name == ProjectName).First();
67 | Compilation compilation = project.GetCompilationAsync().Result;
68 | foreach (Component component in ComponentFinder.Container.Components)
69 | {
70 | foreach (CodeElement codeElement in component.CodeElements)
71 | {
72 | try
73 | {
74 | string type = codeElement.Type.Substring(0, component.Type.IndexOf(',')); // remove the assembly name from the type
75 | INamedTypeSymbol symbol = compilation.GetTypeByMetadataName(type);
76 | foreach (Microsoft.CodeAnalysis.Location location in symbol.Locations)
77 | {
78 | if (location.IsInSource)
79 | {
80 | codeElement.Url = new Uri(location.SourceTree.FilePath).AbsoluteUri;
81 | codeElement.Size += location.SourceTree.GetText().Lines.Count;
82 | component.Size += codeElement.Size;
83 | }
84 | }
85 |
86 | if (codeElement.Role == CodeElementRole.Primary)
87 | {
88 | string xml = symbol.GetDocumentationCommentXml();
89 | if (xml != null && xml.Trim().Length > 0)
90 | {
91 | XDocument xdoc = XDocument.Parse(xml);
92 | string comment = xdoc.Descendants("summary").First().Value.Trim();
93 | component.Description = comment;
94 | }
95 | }
96 | }
97 | catch (Exception e)
98 | {
99 | Console.WriteLine("Could not get summary comment for " + component.Name + ": " + e.Message);
100 | }
101 | }
102 | }
103 |
104 | return new HashSet();
105 | }
106 |
107 | ///
108 | public void AfterFindComponents()
109 | {
110 | }
111 |
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | TestResults
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 |
84 | # Visual Studio profiler
85 | *.psess
86 | *.vsp
87 | *.vspx
88 | *.sap
89 |
90 | # TFS 2012 Local Workspace
91 | $tf/
92 |
93 | # Guidance Automation Toolkit
94 | *.gpState
95 |
96 | # ReSharper is a .NET coding add-in
97 | _ReSharper*/
98 | *.[Rr]e[Ss]harper
99 | *.DotSettings.user
100 |
101 | # JustCode is a .NET coding add-in
102 | .JustCode
103 |
104 | # TeamCity is a build add-in
105 | _TeamCity*
106 |
107 | # DotCover is a Code Coverage Tool
108 | *.dotCover
109 |
110 | # NCrunch
111 | _NCrunch_*
112 | .*crunch*.local.xml
113 | nCrunchTemp_*
114 |
115 | # MightyMoose
116 | *.mm.*
117 | AutoTest.Net/
118 |
119 | # Web workbench (sass)
120 | .sass-cache/
121 |
122 | # Installshield output folder
123 | [Ee]xpress/
124 |
125 | # DocProject is a documentation generator add-in
126 | DocProject/buildhelp/
127 | DocProject/Help/*.HxT
128 | DocProject/Help/*.HxC
129 | DocProject/Help/*.hhc
130 | DocProject/Help/*.hhk
131 | DocProject/Help/*.hhp
132 | DocProject/Help/Html2
133 | DocProject/Help/html
134 |
135 | # Click-Once directory
136 | publish/
137 |
138 | # Publish Web Output
139 | *.[Pp]ublish.xml
140 | *.azurePubxml
141 | # TODO: Comment the next line if you want to checkin your web deploy settings
142 | # but database connection strings (with potential passwords) will be unencrypted
143 | *.pubxml
144 | *.publishproj
145 |
146 | # NuGet Packages
147 | *.nupkg
148 | # The packages folder can be ignored because of Package Restore
149 | **/packages/*
150 | # except build/, which is used as an MSBuild target.
151 | !**/packages/build/
152 | # Uncomment if necessary however generally it will be regenerated when needed
153 | #!**/packages/repositories.config
154 | # NuGet v3's project.json files produces more ignoreable files
155 | *.nuget.props
156 | *.nuget.targets
157 |
158 | # Microsoft Azure Build Output
159 | csx/
160 | *.build.csdef
161 |
162 | # Microsoft Azure Emulator
163 | ecf/
164 | rcf/
165 |
166 | # Microsoft Azure ApplicationInsights config file
167 | ApplicationInsights.config
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | ~$*
182 | *~
183 | *.dbmdl
184 | *.dbproj.schemaview
185 | *.pfx
186 | *.publishsettings
187 | node_modules/
188 | orleans.codegen.cs
189 |
190 | # RIA/Silverlight projects
191 | Generated_Code/
192 |
193 | # Backup & report files from converting an old project file
194 | # to a newer Visual Studio version. Backup files are not needed,
195 | # because we have git ;-)
196 | _UpgradeReport_Files/
197 | Backup*/
198 | UpgradeLog*.XML
199 | UpgradeLog*.htm
200 |
201 | # SQL Server files
202 | *.mdf
203 | *.ldf
204 |
205 | # Business Intelligence projects
206 | *.rdl.data
207 | *.bim.layout
208 | *.bim_*.settings
209 |
210 | # Microsoft Fakes
211 | FakesAssemblies/
212 |
213 | # GhostDoc plugin setting file
214 | *.GhostDoc.xml
215 |
216 | # Node.js Tools for Visual Studio
217 | .ntvs_analysis.dat
218 |
219 | # Visual Studio 6 build log
220 | *.plg
221 |
222 | # Visual Studio 6 workspace options file
223 | *.opt
224 |
225 | # Visual Studio LightSwitch build output
226 | **/*.HTMLClient/GeneratedArtifacts
227 | **/*.DesktopClient/GeneratedArtifacts
228 | **/*.DesktopClient/ModelManifest.xml
229 | **/*.Server/GeneratedArtifacts
230 | **/*.Server/ModelManifest.xml
231 | _Pvt_Extensions
232 |
233 | # Paket dependency manager
234 | .paket/paket.exe
235 |
236 | # FAKE - F# Make
237 | .fake/
238 |
239 | # Rider project files
240 | .idea
241 |
242 | # Structurizr JSON files
243 | Structurizr.Examples/*.json
244 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools.Tests/AdrTools/AdrToolsImporterTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Linq;
4 | using Structurizr.Documentation;
5 | using Xunit;
6 |
7 | namespace Structurizr.AdrTools.Tests
8 | {
9 |
10 | public class AdrToolsImporterTests
11 | {
12 |
13 | private Workspace _workspace;
14 | private SoftwareSystem _softwareSystem;
15 | private Documentation.Documentation _documentation;
16 |
17 | public AdrToolsImporterTests()
18 | {
19 | _workspace = new Workspace("Name", "Description");
20 | _softwareSystem = _workspace.Model.AddSoftwareSystem("Software System", "Description");
21 | _documentation = _workspace.Documentation;
22 | }
23 |
24 | [Fact]
25 | public void Test_Construction_ThrowsAnException_WhenAWorkspaceIsNotSpecified()
26 | {
27 | try
28 | {
29 | new AdrToolsImporter(null, null);
30 | throw new TestFailedException();
31 | }
32 | catch (ArgumentException iae)
33 | {
34 | Assert.Equal("A workspace must be specified.", iae.Message);
35 | }
36 | }
37 |
38 | [Fact]
39 | public void Test_Construction_ThrowsAnException_WhenADirectoryIsNotSpecified()
40 | {
41 | try
42 | {
43 | new AdrToolsImporter(_workspace, null);
44 | throw new TestFailedException();
45 | }
46 | catch (ArgumentException iae)
47 | {
48 | Assert.Equal("The path to the architecture decision records must be specified.", iae.Message);
49 | }
50 | }
51 |
52 | [Fact]
53 | public void Test_Construction_ThrowsAnException_WhenADirectoryIsSpecifiedButItDoesNotExist()
54 | {
55 | try
56 | {
57 | new AdrToolsImporter(_workspace, new DirectoryInfo("some-random-path"));
58 | throw new TestFailedException();
59 | }
60 | catch (ArgumentException iae)
61 | {
62 | Assert.True(iae.Message.EndsWith("some-random-path does not exist."));
63 | }
64 | }
65 |
66 | [Fact]
67 | public void Test_ImportArchitectureDecisionRecords()
68 | {
69 | AdrToolsImporter importer = new AdrToolsImporter(_workspace, new DirectoryInfo("AdrTools\\adrs"));
70 | importer.ImportArchitectureDecisionRecords();
71 |
72 | Assert.Equal(10, _documentation.Decisions.Count);
73 |
74 | Decision decision1 = _documentation.Decisions.Where(d => d.Id == "1").First();
75 | Assert.Equal("1", decision1.Id);
76 | Assert.Equal("Record architecture decisions", decision1.Title);
77 | Assert.Equal("2016-02-12T00:00:00.0000000", decision1.Date.ToString("o"));
78 | Assert.Equal(DecisionStatus.Accepted, decision1.Status);
79 | Assert.Equal(Format.Markdown, decision1.Format);
80 | Assert.Equal("# 1. Record architecture decisions\n" +
81 | "\n" +
82 | "Date: 2016-02-12\n" +
83 | "\n" +
84 | "## Status\n" +
85 | "\n" +
86 | "Accepted\n" +
87 | "\n" +
88 | "## Context\n" +
89 | "\n" +
90 | "We need to record the architectural decisions made on this project.\n" +
91 | "\n" +
92 | "## Decision\n" +
93 | "\n" +
94 | "We will use Architecture Decision Records, as described by Michael Nygard in this article: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions\n" +
95 | "\n" +
96 | "## Consequences\n" +
97 | "\n" +
98 | "See Michael Nygard's article, linked above.\n",
99 | decision1.Content);
100 | }
101 |
102 | [Fact]
103 | public void Test_ImportArchitectureDecisionRecords_RewritesLinksBetweenADRsWhenTheyAreNotAssociatedWithAnElement()
104 | {
105 | AdrToolsImporter importer = new AdrToolsImporter(_workspace, new DirectoryInfo("AdrTools\\adrs"));
106 | importer.ImportArchitectureDecisionRecords();
107 |
108 | Decision decision5 = _documentation.Decisions.Where(d => d.Id == "5").First();
109 | Assert.True(decision5.Content.Contains("Amended by [9. Help scripts](#/:9)"));
110 | }
111 |
112 | [Fact]
113 | public void Test_TmportArchitectureDecisionRecords_RewritesLinksBetweenADRsWhenTheyAreAssociatedWithAnElement()
114 | {
115 | AdrToolsImporter importer = new AdrToolsImporter(_workspace, new DirectoryInfo("AdrTools\\adrs"));
116 | importer.ImportArchitectureDecisionRecords(_softwareSystem);
117 |
118 | Decision decision5 = _documentation.Decisions.Where(d => d.Id == "5").First();
119 | Assert.True(decision5.Content.Contains("Amended by [9. Help scripts](#%2FSoftware%20System:9)"));
120 | }
121 |
122 | [Fact]
123 | public void Test_ImportArchitectureDecisionRecords_SupportsTheIncorrectSpellingOfSuperseded()
124 | {
125 | AdrToolsImporter importer = new AdrToolsImporter(_workspace, new DirectoryInfo("AdrTools\\adrs"));
126 | importer.ImportArchitectureDecisionRecords();
127 |
128 | Decision decision4 = _documentation.Decisions.Where(d => d.Id == "4").First();
129 | Assert.Equal(DecisionStatus.Superseded, decision4.Status);
130 | Assert.True(decision4.Content.Contains("Superceded by [10. AsciiDoc format](#/:10)"));
131 | }
132 |
133 | }
134 |
135 | }
136 |
--------------------------------------------------------------------------------
/docs/c4-plantuml.md:
--------------------------------------------------------------------------------
1 | # C4-PlantUML
2 |
3 | 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:
4 |
5 | - Enterprise Context
6 | - System Context
7 | - Container
8 | - Component
9 | - Dynamic*
10 | - Deployment*
11 |
12 | *..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
13 | CustomBaseUrl=https://raw.githubusercontent.com/kirchsth/C4-PlantUML/master/ or if it is not set then the definition is merged in each diagram)
14 |
15 | 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):
16 |
17 | ```c#
18 | Workspace workspace = new Workspace("Getting Started", "This is a model of my software system.");
19 | Model model = workspace.Model;
20 |
21 | model.Enterprise = new Enterprise("Some Enterprise");
22 |
23 | Person user = model.AddPerson("User", "A user of my software system.");
24 | SoftwareSystem softwareSystem = model.AddSoftwareSystem("Software System", "My software system.");
25 | var userUsesSystemRelation = user.Uses(softwareSystem, "Uses");
26 | // a direction could be added to relation (active in all views)
27 | // userUsesSystemRelation.SetDirection(DirectionValues.Right);
28 |
29 | ViewSet views = workspace.Views;
30 | SystemContextView contextView = views.CreateSystemContextView(softwareSystem, "SystemContext", "An example of a System Context diagram.");
31 | contextView.AddAllSoftwareSystems();
32 | contextView.AddAllPeople();
33 |
34 | // C4PlantUMLWriter support view specific directions too, e.g. "User" should be left of "Software System" only in this view
35 | contextView.Relationships
36 | .First(rv => rv.Relationship.SourceId == user.Id && rv.Relationship.DestinationId == softwareSystem.Id)
37 | .SetDirection(DirectionValues.Right);
38 |
39 | using (var stringWriter = new StringWriter())
40 | {
41 | var plantUmlWriter = new C4PlantUmlWriter();
42 | plantUmlWriter.Write(workspace, stringWriter);
43 | Console.WriteLine(stringWriter.ToString());
44 | }
45 | ```
46 |
47 | This code will generate and output a PlantUML diagram definition that looks like this:
48 |
49 | ```
50 | @startuml
51 | !include
52 |
53 | ' Structurizr.SystemContextView: SystemContext
54 | title Software System - System Context
55 |
56 | LAYOUT_WITH_LEGEND()
57 |
58 | Enterprise_Boundary(SomeEnterprise, "Some Enterprise") {
59 | System(SoftwareSystem__33c0d9d, "Software System", "My software system.")
60 | Person(User__378734a, "User", "A user of my software system.")
61 | Rel_Right(User__378734a, SoftwareSystem__33c0d9d, "Uses")
62 | }
63 | @enduml
64 | ```
65 |
66 | If you copy/paste this into [PlantUML online](http://www.plantuml.com/plantuml/), you will get something like this:
67 |
68 | 
69 |
70 | __Mark containers or components as Database__
71 |
72 | Additional to the relation directions (via .SetDirection(), see above) is it possible to activate the database symbols in the diagrams via component and container specific *.IsDatabase(true) calls.
73 |
74 |
75 | ```c#
76 | Container webApplication = softwareSystem.AddContainer("Web Application", "Delivers content", "Java and spring MVC");
77 | Container database = softwareSystem.AddContainer("Database", "Stores information", "Relational Database Schema");
78 | // Additional mark it as database
79 | database.SetIsDatabase(true);
80 | user.Uses(webApplication, "uses", "HTTP");
81 | webApplication.Uses(database, "Reads from and writes to", "JDBC").SetDirection(DirectionValues.Right);
82 |
83 | var containerView = views.CreateContainerView(softwareSystem, "containers", "");
84 | containerView.AddAllElements();
85 |
86 | using (var stringWriter = new StringWriter())
87 | {
88 | var plantUmlWriter = new C4PlantUmlWriter();
89 | plantUmlWriter.Write(containerView, stringWriter);
90 | Console.WriteLine(stringWriter.ToString());
91 | }
92 | ```
93 |
94 | This code will generate and output a PlantUML diagram definition that looks like this:
95 |
96 | ```
97 | @startuml
98 | !include
99 |
100 | ' Structurizr.ContainerView: containers
101 | title Software System - Containers
102 |
103 | LAYOUT_WITH_LEGEND()
104 |
105 | Person(User__378734a, "User", "A user of my software system.")
106 | System_Boundary(SoftwareSystem__33c0d9d, "Software System") {
107 | ContainerDb(SoftwareSystem__Database__202c666, "Database", "Relational Database Schema", "Stores information")
108 | Container(SoftwareSystem__WebApplication__2004eee, "Web Application", "Java and spring MVC", "Delivers content")
109 | }
110 | Rel(User__378734a, SoftwareSystem__WebApplication__2004eee, "uses", "HTTP")
111 | Rel_Right(SoftwareSystem__WebApplication__2004eee, SoftwareSystem__Database__202c666, "Reads from and writes to", "JDBC")
112 | @enduml
113 | ```
114 |
115 | You will get something like this:
116 |
117 | 
118 |
119 | ## Benefits of using C4-PlantUML with Structurizr
120 |
121 | The key benefit of using PlantUML in conjunction with the Structurizr client library is that you can create diagrams from a __model__ of your software system. The model provides a set of rules that must be followed; related to elements, relationships, and how they are exposed using diagrams. This means:
122 |
123 | 1. Rather than looking after a collection of disjointed PlantUML diagram definitions, you can create many PlantUML diagrams from a single model and keep them all up to date easily, especially if integrated with your continuous build server and build pipeline.
124 | 1. The naming of elements and the definition of relationships between elements _remains consistent across diagrams_.
125 | 1. The software architecture model at the component level can be created by extracting components from a codebase, using _static analysis and reflection techniques_.
126 |
--------------------------------------------------------------------------------
/Structurizr.AdrTools/AdrTools/AdrToolsImporter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Net;
7 | using System.Text;
8 | using System.Text.RegularExpressions;
9 | using Structurizr.Documentation;
10 |
11 | namespace Structurizr.AdrTools
12 | {
13 |
14 | public class AdrToolsImporter
15 | {
16 |
17 | private readonly Regex TitleRegex = new Regex("^# \\d*\\. (.*)$", RegexOptions.Multiline);
18 | private readonly Regex DateRegex = new Regex("^Date: (\\d\\d\\d\\d-\\d\\d-\\d\\d)$", RegexOptions.Multiline);
19 | private readonly Regex StatusRegex = new Regex("## Status\\n\\n(\\w*)", RegexOptions.Multiline);
20 |
21 | private const string SupercededAlternativeSpelling = "Superceded";
22 |
23 | private Workspace _workspace;
24 | private DirectoryInfo _path;
25 |
26 | public AdrToolsImporter(Workspace workspace, DirectoryInfo path)
27 | {
28 | if (workspace == null)
29 | {
30 | throw new ArgumentException("A workspace must be specified.");
31 | }
32 |
33 | if (path == null)
34 | {
35 | throw new ArgumentException("The path to the architecture decision records must be specified.");
36 | }
37 |
38 | if (!Directory.Exists(path.FullName))
39 | {
40 | throw new ArgumentException(path.FullName + " does not exist.");
41 | }
42 |
43 | _workspace = workspace;
44 | _path = path;
45 | }
46 |
47 | public ISet ImportArchitectureDecisionRecords()
48 | {
49 | return ImportArchitectureDecisionRecords(null);
50 | }
51 |
52 | public ISet ImportArchitectureDecisionRecords(SoftwareSystem softwareSystem)
53 | {
54 | HashSet decisions = new HashSet();
55 |
56 | IEnumerable markdownFiles = _path.GetFiles().Where(f => f.Name.EndsWith(".md"));
57 |
58 | // first create an index of filename -> ID
59 | Dictionary index = new Dictionary();
60 | foreach (FileInfo file in markdownFiles)
61 | {
62 | index.Add(file.Name, ExtractIntegerIdFromFileName(file));
63 | }
64 |
65 | foreach (FileInfo file in markdownFiles)
66 | {
67 | string id = ExtractIntegerIdFromFileName(file);
68 | DateTime date = new DateTime();
69 | string title = "";
70 | DecisionStatus status = DecisionStatus.Proposed;
71 | string content = File.ReadAllText(file.FullName, Encoding.UTF8);
72 | content = content.Replace("\r", "");
73 | Format format = Format.Markdown;
74 |
75 | title = ExtractTitle(content);
76 | date = ExtractDate(content);
77 | status = ExtractStatus(content);
78 |
79 | foreach (string filename in index.Keys) {
80 | content = content.Replace(filename, CalculateUrl(softwareSystem, index[filename]));
81 | }
82 |
83 | Decision decision = _workspace.Documentation.AddDecision(softwareSystem, id, date, title, status, format, content);
84 | decisions.Add(decision);
85 | }
86 |
87 | return decisions;
88 | }
89 |
90 | private string CalculateUrl(SoftwareSystem softwareSystem, string id)
91 | {
92 | if (softwareSystem == null) {
93 | return "#/:" + UrlEncode(id);
94 | }
95 | else
96 | {
97 | return "#" + UrlEncode(softwareSystem.CanonicalName) + ":" + UrlEncode(id);
98 | }
99 | }
100 |
101 | private string UrlEncode(string value)
102 | {
103 | return WebUtility.UrlEncode(value).Replace("+", "%20");
104 | }
105 |
106 | private string ExtractIntegerIdFromFileName(FileInfo file)
107 | {
108 | return "" + Convert.ToInt64(file.Name.Substring(0, 4));
109 | }
110 |
111 | private string ExtractTitle(string content)
112 | {
113 | MatchCollection matchCollection = TitleRegex.Matches(content);
114 | if (matchCollection.Count > 0)
115 | {
116 | return matchCollection[0].Groups[1].Value;
117 | }
118 | else
119 | {
120 | return "Untitled";
121 | }
122 | }
123 |
124 | private DateTime ExtractDate(string content)
125 | {
126 | MatchCollection matchCollection = DateRegex.Matches(content);
127 | if (matchCollection.Count > 0)
128 | {
129 | string dateAsString = matchCollection[0].Groups[1].Value;
130 | DateTime date;
131 | DateTime.TryParseExact(dateAsString, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out date);
132 | return date;
133 | }
134 | else
135 | {
136 | return new DateTime();
137 | }
138 | }
139 |
140 | private DecisionStatus ExtractStatus(string content)
141 | {
142 | MatchCollection matchCollection = StatusRegex.Matches(content);
143 | if (matchCollection.Count > 0)
144 | {
145 | string status = matchCollection[0].Groups[1].Value;
146 |
147 | if (status == SupercededAlternativeSpelling)
148 | {
149 | return DecisionStatus.Superseded;
150 | }
151 | else
152 | {
153 | DecisionStatus decisionStatus;
154 | Enum.TryParse(status, out decisionStatus);
155 |
156 | return decisionStatus;
157 | }
158 | }
159 | else
160 | {
161 | return DecisionStatus.Proposed;
162 | }
163 | }
164 |
165 | }
166 | }
--------------------------------------------------------------------------------
/docs/structurizr-annotations.md:
--------------------------------------------------------------------------------
1 | # Structurizr annotations
2 |
3 | Structurizr for .NET includes some custom code annotation attributes that you can add to your code. These serve to either make it explicit how components should be extracted from your codebase, or they help supplement the software architecture model.
4 |
5 | The attributes can be found in the [Structurizr.Annotations](https://nuget.org/packages/Structurizr.Annotations) NuGet package, which is a very small standalone JAR file containing only the Structurizr annotations. All attributes have a runtime retention policy, so they will be present in the compiled bytecode.
6 |
7 | ## [Component]
8 |
9 | A type-level annotation that can be used to signify that the annotated type (an interface or class) can be considered to be a "component". The properties are as follows:
10 |
11 | - Description: The description of the component (optional).
12 | - Technology: The technology of component (optional).
13 |
14 | ## [CodeElement]
15 |
16 | A type-level annotation that can be used to signify that the annotated type can be considered to be a supporting type for a component. The properties are as follows:
17 |
18 | - Description: The description of the code element (optional).
19 |
20 | ## [UsedByPerson]
21 |
22 | A type-level annotation that can be used to signify that the named person uses the component on which this annotation is placed, creating a relationship form the person to the component. The properties are as follows:
23 |
24 | - Name: The name of the person (required).
25 | - Description: The description of the relationship (optional).
26 | - Technology: The technology of relationship (optional).
27 |
28 | ## [UsedBySoftwareSystem]
29 |
30 | A type-level annotation that can be used to signify that the named software system uses the component on which this annotation is placed, creating a relationship from the software system to the component. The properties are as follows:
31 |
32 | - Name: The name of the software system (required).
33 | - Description: The description of the relationship (optional).
34 | - Technology: The technology of relationship (optional).
35 |
36 | ## [UsedByContainer]
37 |
38 | A type-level annotation that can be used to signify that the named container uses the component on which this annotation is placed, creating a relationship from the container to the component. The properties are as follows:
39 |
40 | - Name: The name of the container (required).
41 | - Description: The description of the relationship (optional).
42 | - Technology: The technology of relationship (optional).
43 |
44 | If the container resides in the same software system as the component, the simple name can be used to identify the container (e.g. "Database"). Otherwise, the full canonical name of the form "Software System/Container" must be used (e.g. "Some Other Software System/Database").
45 |
46 | ## [UsesSoftwareSystem]
47 |
48 | A type-level annotation that can be used to signify that the component on which this annotation is placed has a relationship to the named software system, creating a relationship from the component to the software system. The properties are as follows:
49 |
50 | - Name: The name of the software system (required).
51 | - Description: The description of the relationship (optional).
52 | - Technology: The technology of relationship (optional).
53 |
54 | ## [UsesContainer]
55 |
56 | A type-level annotation that can be used to signify that the component on which this annotation is placed has a relationship to the named container, creating a relationship from the component to the container. The properties are as follows:
57 |
58 | - Name: The name of the container (required).
59 | - Description: The description of the relationship (optional).
60 | - Technology: The technology of relationship (optional).
61 |
62 | If the container resides in the same software system as the component, the simple name can be used to identify the container (e.g. "Database"). Otherwise, the full canonical name of the form "Software System/Container" must be used (e.g. "Some Other Software System/Database").
63 |
64 | ## [UsesComponent]
65 |
66 | A field-level annotation that can be used to supplement the existing relationship (i.e. add a description and/or technology) between two components.
67 |
68 | When using the various component finder strategies, Structurizr for .NET will identify components along with the relationships between those components. Since this is typically done using reflection against the compiled bytecode, you'll notice that the description and technology properties of the resulting relationships is always empty. The ```[UsesComponent]``` annotation provides a simple way to ensure that such information is added into the model.
69 |
70 | The properties are as follows:
71 |
72 | - Description: The description of the relationship (required).
73 | - Technology: The technology of relationship (optional).
74 |
75 | ## Example
76 |
77 | Here are some examples of the annotations, which have been used to create the following diagram.
78 |
79 | 
80 |
81 | ```csharp
82 | [Component(Description = "Serves HTML pages to users.", Technology = "ASP.NET MVC")]
83 | [UsedByPerson("User", Description = "Uses", Technology = "HTTPS")]
84 | class HtmlController
85 | {
86 |
87 | [UsesComponent("Gets data using")]
88 | private IRepository repository = new EfRepository();
89 |
90 | }
91 | ```
92 |
93 | ```csharp
94 | [Component(Description = "Provides access to data stored in the database.", Technology = "C#")]
95 | public interface IRepository
96 | {
97 |
98 | string GetData(long id);
99 |
100 | }
101 | ```
102 |
103 | ```csharp
104 | [UsesContainer("Database", Description = "Reads from", Technology = "Entity Framework")]
105 | class EfRepository : IRepository
106 | {
107 |
108 | public string GetData(long id)
109 | {
110 | return "...";
111 | }
112 |
113 | }
114 | ```
115 |
116 | See [StructurizrAnnotations.cs](https://github.com/structurizr/dotnet/blob/master/Structurizr.Reflection.Examples/StructurizrAnnotations.cs) for the full source code illustrating how to use the various annotations in conjunction with the reflection-based component finder. The resulting diagrams can be found at [https://structurizr.com/share/38341](https://structurizr.com/share/38341). A Mono.Cecil based version of the example is also available; see its own [StructurizrAnnotations.cs](https://github.com/structurizr/dotnet/blob/master/Structurizr.Cecil.Examples/StructurizrAnnotations.cs) for source code and [https://structurizr.com/share/38339](https://structurizr.com/share/38339) for the resulting diagrams.
--------------------------------------------------------------------------------
/Structurizr.Reflection/Analysis/TypeMatcherComponentFinderStrategy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Structurizr.Analysis
5 | {
6 |
7 | ///
8 | /// Implements a component finder strategy which uses a collection of objects to identify
9 | /// components and their dependencies.
10 | ///
11 | ///
12 | ///
13 | ///
14 | ///
15 | public class TypeMatcherComponentFinderStrategy : ComponentFinderStrategy
16 | {
17 |
18 | private ComponentFinder _componentFinder;
19 |
20 | ///
21 | public ComponentFinder ComponentFinder
22 | {
23 | get { return _componentFinder; }
24 | set
25 | {
26 | _componentFinder = value;
27 | }
28 | }
29 |
30 | private HashSet _componentsFound = new HashSet();
31 |
32 | private ITypeRepository _typeRepository;
33 | private List _typeMatchers = new List();
34 | private List _supportingTypesStrategies = new List();
35 |
36 | ///
37 | /// Creates a new instance of for identifying
38 | /// components using the provided type matchers.
39 | ///
40 | ///
41 | /// An array of objects implementing that will be used to identify components in the
42 | /// provided assembly.
43 | ///
44 | public TypeMatcherComponentFinderStrategy(params ITypeMatcher[] typeMatchers)
45 | {
46 | this._typeMatchers.AddRange(typeMatchers);
47 | }
48 |
49 | ///
50 | public void BeforeFindComponents()
51 | {
52 | _typeRepository = new ReflectionTypeRepository(_componentFinder.Namespace, _componentFinder.Exclusions);
53 | foreach (SupportingTypesStrategy strategy in _supportingTypesStrategies)
54 | {
55 | strategy.TypeRepository = _typeRepository;
56 | }
57 | }
58 |
59 | ///
60 | public IEnumerable FindComponents()
61 | {
62 | foreach (Type type in _typeRepository.GetAllTypes())
63 | {
64 | foreach (ITypeMatcher typeMatcher in this._typeMatchers)
65 | {
66 | if (typeMatcher.Matches(type))
67 | {
68 | Component component = ComponentFinder.Container.AddComponent(
69 | type.Name,
70 | type,
71 | typeMatcher.GetDescription(),
72 | typeMatcher.GetTechnology());
73 | _componentsFound.Add(component);
74 | }
75 | }
76 | }
77 |
78 | return _componentsFound;
79 | }
80 |
81 | ///
82 | public void AfterFindComponents()
83 | {
84 | // before finding dependencies, let's find the types that are used to implement each component
85 | foreach (Component component in _componentsFound)
86 | {
87 | foreach (CodeElement codeElement in component.CodeElements)
88 | {
89 | codeElement.Visibility = _typeRepository.FindVisibility(codeElement.Type);
90 | codeElement.Category = _typeRepository.FindCategory(codeElement.Type);
91 | }
92 |
93 | foreach (SupportingTypesStrategy strategy in _supportingTypesStrategies)
94 | {
95 | foreach (string type in strategy.FindSupportingTypes(component))
96 | {
97 | if (ComponentFinder.Container.GetComponentOfType(type) == null)
98 | {
99 | CodeElement codeElement = component.AddSupportingType(type);
100 | codeElement.Visibility = _typeRepository.FindVisibility(type);
101 | codeElement.Category = _typeRepository.FindCategory(type);
102 | }
103 | }
104 | }
105 | }
106 |
107 | foreach (Component component in ComponentFinder.Container.Components)
108 | {
109 | if (component.Type != null)
110 | {
111 | AddEfferentDependencies(component, component.Type, new HashSet());
112 |
113 | // and repeat for the supporting types
114 | foreach (CodeElement codeElement in component.CodeElements)
115 | {
116 | AddEfferentDependencies(component, codeElement.Type, new HashSet());
117 | }
118 | }
119 | }
120 | }
121 |
122 | private void AddEfferentDependencies(Component component, string type, HashSet typesVisited)
123 | {
124 | typesVisited.Add(type);
125 |
126 | foreach (string referencedTypeName in _typeRepository.GetReferencedTypes(type))
127 | {
128 | Component destinationComponent = ComponentFinder.Container.GetComponentOfType(referencedTypeName);
129 | if (destinationComponent != null)
130 | {
131 | if (component != destinationComponent)
132 | {
133 | component.Uses(destinationComponent, "");
134 | }
135 | }
136 | else if (!typesVisited.Contains(referencedTypeName))
137 | {
138 | AddEfferentDependencies(component, referencedTypeName, typesVisited);
139 | }
140 | }
141 | }
142 |
143 | ///
144 | /// Adds a strategy for identifying supporting types of components identified by this strategy.
145 | ///
146 | ///
147 | /// A instance to use for identifying supporting types.
148 | ///
149 | public void AddSupportingTypesStrategy(SupportingTypesStrategy strategy)
150 | {
151 | if (strategy != null)
152 | {
153 | _supportingTypesStrategies.Add(strategy);
154 | strategy.TypeRepository = _typeRepository;
155 | }
156 | }
157 |
158 | }
159 | }
--------------------------------------------------------------------------------
/Structurizr.Cecil/Util/CustomAttributeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 | using Mono.Cecil;
6 |
7 | namespace Structurizr.Cecil.Util
8 | {
9 | ///
10 | /// Provides extension methods for working with custom attributes.
11 | ///
12 | static class CustomAttributeExtensions
13 | {
14 | ///
15 | /// Retrieves custom attributes of a specified type that are applied to a specified type.
16 | ///
17 | /// The type to inspect.
18 | /// The type of attribute to search for.
19 | ///
20 | /// A collection of custom attributes which match , or an empty enumeration if
21 | /// no such attribute is found.
22 | ///
23 | public static IEnumerable ResolvableAttributes(this TypeDefinition klassType)
24 | where TAttribute : Attribute
25 | {
26 | var attributeType = typeof(TAttribute).GetTypeInfo();
27 |
28 | foreach (var customAttribute in klassType.CustomAttributes)
29 | {
30 | if (!customAttribute.Is()) continue;
31 |
32 | var arguments = customAttribute.ConstructorArguments.Select(a => a.Value).ToArray();
33 |
34 | var createdAttribute = (TAttribute)Activator.CreateInstance(typeof(TAttribute), arguments);
35 | foreach (var attributeProperty in customAttribute.Properties)
36 | {
37 | var propertyInfo = attributeType.GetDeclaredProperty(attributeProperty.Name);
38 | propertyInfo.SetValue(createdAttribute, attributeProperty.Argument.Value);
39 | }
40 |
41 | yield return createdAttribute;
42 | }
43 | }
44 |
45 | ///
46 | /// Retrieves custom attributes of a specified type that are applied to a specified field.
47 | ///
48 | /// The field to inspect.
49 | /// The type of attribute to search for.
50 | ///
51 | /// A collection of custom attributes which match , or an empty enumeration if
52 | /// no such attribute is found.
53 | ///
54 | public static IEnumerable ResolvableAttributes(this FieldDefinition field)
55 | where TAttribute : Attribute
56 | {
57 | var attributeType = typeof(TAttribute).GetTypeInfo();
58 |
59 | foreach (var customAttribute in field.CustomAttributes)
60 | {
61 | if (!customAttribute.Is()) continue;
62 |
63 | var arguments = customAttribute.ConstructorArguments.Select(a => a.Value).ToArray();
64 |
65 | var createdAttribute = (TAttribute)Activator.CreateInstance(typeof(TAttribute), arguments);
66 | foreach (var attributeProperty in customAttribute.Properties)
67 | {
68 | var propertyInfo = attributeType.GetDeclaredProperty(attributeProperty.Name);
69 | propertyInfo.SetValue(createdAttribute, attributeProperty.Argument.Value);
70 | }
71 |
72 | yield return createdAttribute;
73 | }
74 | }
75 |
76 | ///
77 | /// Retrieves custom attributes of a specified type that are applied to a specified property.
78 | ///
79 | /// The property to inspect.
80 | /// The type of attribute to search for.
81 | ///
82 | /// A collection of custom attributes which match , or an empty enumeration if
83 | /// no such attribute is found.
84 | ///
85 | public static IEnumerable ResolvableAttributes(this PropertyDefinition property)
86 | where TAttribute : Attribute
87 | {
88 | var attributeType = typeof(TAttribute).GetTypeInfo();
89 |
90 | foreach (var customAttribute in property.CustomAttributes)
91 | {
92 | if (!customAttribute.Is()) continue;
93 |
94 | var arguments = customAttribute.ConstructorArguments.Select(a => a.Value).ToArray();
95 |
96 | var createdAttribute = (TAttribute)Activator.CreateInstance(typeof(TAttribute), arguments);
97 | foreach (var attributeProperty in customAttribute.Properties)
98 | {
99 | var propertyInfo = attributeType.GetDeclaredProperty(attributeProperty.Name);
100 | propertyInfo.SetValue(createdAttribute, attributeProperty.Argument.Value);
101 | }
102 |
103 | yield return createdAttribute;
104 | }
105 | }
106 |
107 | ///
108 | /// Retrieves custom attributes of a specified type that are applied to a specified parameter.
109 | ///
110 | /// The property to inspect.
111 | /// The type of attribute to search for.
112 | ///
113 | /// A collection of custom attributes which match , or an empty enumeration if
114 | /// no such attribute is found.
115 | ///
116 | public static IEnumerable ResolvableAttributes(this ParameterDefinition parameter)
117 | where TAttribute : Attribute
118 | {
119 | var attributeType = typeof(TAttribute).GetTypeInfo();
120 |
121 | foreach (var customAttribute in parameter.CustomAttributes)
122 | {
123 | if (!customAttribute.Is()) continue;
124 |
125 | var arguments = customAttribute.ConstructorArguments.Select(a => a.Value).ToArray();
126 |
127 | var createdAttribute = (TAttribute)Activator.CreateInstance(typeof(TAttribute), arguments);
128 | foreach (var attributeProperty in customAttribute.Properties)
129 | {
130 | var propertyInfo = attributeType.GetDeclaredProperty(attributeProperty.Name);
131 | propertyInfo.SetValue(createdAttribute, attributeProperty.Argument.Value);
132 | }
133 |
134 | yield return createdAttribute;
135 | }
136 | }
137 |
138 | private static bool Is(this CustomAttribute attribute)
139 | {
140 | return attribute.AttributeType.FullName == typeof(TAttribute).FullName;
141 | }
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/Structurizr.Cecil/Analysis/TypeMatcherComponentFinderStrategy.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | using Mono.Cecil;
4 |
5 | using Structurizr.Cecil;
6 |
7 | namespace Structurizr.Analysis
8 | {
9 | ///
10 | /// Implements a component finder strategy which uses a collection of objects to identify
11 | /// components and their dependencies.
12 | ///
13 | ///
14 | ///
15 | ///
16 | ///
17 | public class TypeMatcherComponentFinderStrategy : ComponentFinderStrategy
18 | {
19 | ///
20 | public ComponentFinder ComponentFinder { get; set; }
21 |
22 | private HashSet _componentsFound = new HashSet();
23 |
24 | private AssemblyDefinition _primaryAssembly;
25 | private ITypeRepository _typeRepository;
26 | private List _typeMatchers = new List();
27 | private List _supportingTypesStrategies = new List();
28 |
29 | ///
30 | /// Creates a new instance of for identifying
31 | /// components from the provided assembly using the provided type matchers.
32 | ///
33 | ///
34 | /// An instance representing the assembly to analyze.
35 | ///
36 | ///
37 | /// An array of objects implementing that will be used to identify components in the
38 | /// provided assembly.
39 | ///
40 | public TypeMatcherComponentFinderStrategy(AssemblyDefinition assembly,
41 | params ITypeMatcher[] typeMatchers)
42 | {
43 | this._primaryAssembly = assembly;
44 | this._typeMatchers.AddRange(typeMatchers);
45 | }
46 |
47 | ///
48 | public void BeforeFindComponents()
49 | {
50 | _typeRepository = new CecilTypeRepository(
51 | _primaryAssembly,
52 | ComponentFinder.Namespace,
53 | ComponentFinder.Exclusions);
54 | foreach (SupportingTypesStrategy strategy in _supportingTypesStrategies)
55 | {
56 | strategy.TypeRepository = _typeRepository;
57 | }
58 | }
59 |
60 | ///
61 | public IEnumerable FindComponents()
62 | {
63 | foreach (TypeDefinition type in _typeRepository.GetAllTypes())
64 | {
65 | foreach (ITypeMatcher typeMatcher in this._typeMatchers)
66 | {
67 | if (typeMatcher.Matches(type))
68 | {
69 | Component component = ComponentFinder.Container.AddComponent(
70 | type.Name,
71 | type.GetAssemblyQualifiedName(),
72 | typeMatcher.GetDescription(),
73 | typeMatcher.GetTechnology());
74 | _componentsFound.Add(component);
75 | }
76 | }
77 | }
78 |
79 | return _componentsFound;
80 | }
81 |
82 | ///
83 | public void AfterFindComponents()
84 | {
85 | // before finding dependencies, let's find the types that are used to implement each component
86 | foreach (Component component in _componentsFound)
87 | {
88 | foreach (CodeElement codeElement in component.CodeElements)
89 | {
90 | codeElement.Visibility = _typeRepository.FindVisibility(codeElement.Type);
91 | codeElement.Category = _typeRepository.FindCategory(codeElement.Type);
92 | }
93 |
94 | foreach (SupportingTypesStrategy strategy in _supportingTypesStrategies)
95 | {
96 | foreach (string type in strategy.FindSupportingTypes(component))
97 | {
98 | if (ComponentFinder.Container.GetComponentOfType(type) == null)
99 | {
100 | CodeElement codeElement = component.AddSupportingType(type);
101 | codeElement.Visibility = _typeRepository.FindVisibility(type);
102 | codeElement.Category = _typeRepository.FindCategory(type);
103 | }
104 | }
105 | }
106 | }
107 |
108 | foreach (Component component in ComponentFinder.Container.Components)
109 | {
110 | if (component.Type != null)
111 | {
112 | AddEfferentDependencies(component, component.Type, new HashSet());
113 |
114 | // and repeat for the supporting types
115 | foreach (CodeElement codeElement in component.CodeElements)
116 | {
117 | AddEfferentDependencies(component, codeElement.Type, new HashSet());
118 | }
119 | }
120 | }
121 | }
122 |
123 | private void AddEfferentDependencies(Component component, string type, HashSet typesVisited)
124 | {
125 | typesVisited.Add(type);
126 |
127 | foreach (string referencedTypeName in _typeRepository.GetReferencedTypes(type))
128 | {
129 | Component destinationComponent = ComponentFinder.Container.GetComponentOfType(referencedTypeName);
130 | if (destinationComponent != null)
131 | {
132 | if (component != destinationComponent)
133 | {
134 | component.Uses(destinationComponent, "");
135 | }
136 | }
137 | else if (!typesVisited.Contains(referencedTypeName))
138 | {
139 | AddEfferentDependencies(component, referencedTypeName, typesVisited);
140 | }
141 | }
142 | }
143 |
144 | ///
145 | /// Adds a strategy for identifying supporting types of components identified by this strategy.
146 | ///
147 | ///
148 | /// A instance to use for identifying supporting types.
149 | ///
150 | public void AddSupportingTypesStrategy(SupportingTypesStrategy strategy)
151 | {
152 | if (strategy != null)
153 | {
154 | _supportingTypesStrategies.Add(strategy);
155 | strategy.TypeRepository = _typeRepository;
156 | }
157 | }
158 | }
159 | }
160 |
--------------------------------------------------------------------------------