Skip to content

Commit

Permalink
Simplified C# AaC by making it strongly-typed throughout (#116)
Browse files Browse the repository at this point in the history
  • Loading branch information
SlavaVedernikov authored Oct 3, 2024
2 parents 5bd43b4 + f01bbe9 commit d1592e4
Show file tree
Hide file tree
Showing 77 changed files with 281 additions and 171 deletions.
4 changes: 2 additions & 2 deletions C4InterFlow.Automation/C4InterFlow.Automation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<Deterministic>true</Deterministic>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>1.7.3</Version>
<Version>1.7.4</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow.Automation</PackageId>
<PackageVersion>1.7.3</PackageVersion>
<PackageVersion>1.7.4</PackageVersion>
<Authors>Slava Vedernikov</Authors>
<Description>Revolutionise your Application Architecture Documentation with C4InterFlow. Designed for Architects and Engineers, this tool leverages the widely-recognised C4 Model (Architecture Visualisation framework), enhanced with unique features like Interface and Flow, to describe your Application Architecture as Code. Experience an intuitive, efficient way to document complex systems, ensuring clarity and consistency across your teams and products.</Description>
<Copyright>Copyright 2024 Slava Vedernikov</Copyright>
Expand Down
23 changes: 12 additions & 11 deletions C4InterFlow.Automation/Writers/CSharpCodeWriter.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using C4InterFlow.Structures;
using System.Text;
using YamlDotNet.Core;

namespace C4InterFlow.Automation.Writers
{
Expand All @@ -23,7 +24,7 @@ public partial class Containers
public partial class {containerName} : IContainerInstance
{{
public static Container Instance => new Container(
Utils.GetStructureAlias<{AnyCodeWriter.GetName(containerName)}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({containerName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
ContainerType = ContainerType.{(!string.IsNullOrEmpty(type) ? type : "None")},
Description = {(!string.IsNullOrEmpty(description) ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Expand Down Expand Up @@ -102,7 +103,7 @@ public string GetSoftwareSystemCode(string architectureNamespace, string name, s
public partial class {softwareSystemName} : ISoftwareSystemInstance
{{
public static SoftwareSystem Instance => new SoftwareSystem(
Utils.GetStructureAlias<{AnyCodeWriter.GetName(softwareSystemName)}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({softwareSystemName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
Description = {(!string.IsNullOrEmpty(description) ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Boundary = Boundary.{(!string.IsNullOrEmpty(boundary) ? boundary : "Internal")}
Expand Down Expand Up @@ -130,7 +131,7 @@ public string GetActorCode(string architectureNamespace, string type, string nam
public class {actorName} : I{type}Instance
{{
public static {type} Instance => new {type}(
Utils.GetStructureAlias<{AnyCodeWriter.GetName(actorName)}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({actorName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
Description = {(description != null ? description : "\"\"")},
}};
Expand Down Expand Up @@ -159,7 +160,7 @@ public partial class Components
public partial class {componentName} : IComponentInstance
{{
public static Component Instance => new Component(
Utils.GetStructureAlias<{componentName}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({componentName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
ComponentType = ComponentType.{componentType},
Description = {(!string.IsNullOrEmpty(description) ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Expand Down Expand Up @@ -236,13 +237,13 @@ public partial class Interfaces
public partial class {componentInterfaceName} : IInterfaceInstance
{{
public static Interface Instance => new Interface(
Utils.GetStructureAlias<{componentInterfaceName}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({componentInterfaceName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
Description = {(description != null ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Path = {(path != null ? AnyCodeWriter.EnsureDoubleQuotes(path) : "\"\"")},
IsPrivate = {(isPrivate != null ? isPrivate.ToString().ToLower() : "false")},
Protocol = {(protocol != null ? AnyCodeWriter.EnsureDoubleQuotes(protocol) : "\"\"")},
Flow = new Flow(Utils.GetStructureAlias<{componentInterfaceName}>()),
Flow = new Flow(Interface.GetAlias<{componentInterfaceName}>()),
Input = {(input != null ? input : "\"\"")},
InputTemplate = {(inputTemplate != null ? inputTemplate : "\"\"")},
Output = {(output != null ? output : "\"\"")},
Expand Down Expand Up @@ -278,10 +279,10 @@ public partial class Interfaces
public partial class {containerInterfaceName} : IInterfaceInstance
{{
public static Interface Instance => new Interface(
Utils.GetStructureAlias<{containerInterfaceName}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({containerInterfaceName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
Description = {(description != null ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Flow = new Flow(Utils.GetStructureAlias<{containerInterfaceName}>()),
Flow = new Flow(Interface.GetAlias<{containerInterfaceName}>()),
Protocol = {(protocol != null ? AnyCodeWriter.EnsureDoubleQuotes(protocol) : "\"\"")},
Input = {(input != null ? input : "\"\"")},
InputTemplate = {(inputTemplate != null ? inputTemplate : "\"\"")},
Expand Down Expand Up @@ -312,10 +313,10 @@ public partial class Interfaces
public partial class {softwareSystemInterfaceName} : IInterfaceInstance
{{
public static Interface Instance => new Interface(
Utils.GetStructureAlias<{softwareSystemInterfaceName}>(), {AnyCodeWriter.EnsureDoubleQuotes(label)})
typeof({softwareSystemInterfaceName}), {AnyCodeWriter.EnsureDoubleQuotes(label)})
{{
Description = {(description != null ? AnyCodeWriter.EnsureDoubleQuotes(description) : "\"\"")},
Flow = new Flow(Utils.GetStructureAlias<{softwareSystemInterfaceName}>()),
Flow = new Flow(Interface.GetAlias<{softwareSystemInterfaceName}>()),
Protocol = {(protocol != null ? AnyCodeWriter.EnsureDoubleQuotes(protocol) : "\"\"")},
Input = {(input != null ? input : "\"\"")},
InputTemplate = {(inputTemplate != null ? inputTemplate : "\"\"")},
Expand Down Expand Up @@ -493,7 +494,7 @@ private string GetFormattedParams(string @params)

public string GetUseFlowCode(string alias)
{
return $"\t.Use(\"{alias}\")";
return $"\t.Use<{alias}>()";
}

public string GetBusinessProcessCode(string architectureNamespace, string name, string label, string businessActivitiesCode, string? description = null)
Expand Down
2 changes: 1 addition & 1 deletion C4InterFlow.Cli/C4InterFlow.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>1.7.3</Version>
<Version>1.7.4</Version>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
Expand Down
4 changes: 2 additions & 2 deletions C4InterFlow/C4InterFlow.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
<Deterministic>true</Deterministic>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Version>1.7.3</Version>
<Version>1.7.4</Version>
</PropertyGroup>

<PropertyGroup>
<PackageId>C4InterFlow</PackageId>
<PackageVersion>1.7.3</PackageVersion>
<PackageVersion>1.7.4</PackageVersion>
<Authors>Slava Vedernikov</Authors>
<Description>Revolutionise your Application Architecture Documentation with C4InterFlow. Designed for Architects and Engineers, this tool leverages the widely-recognised C4 Model (Architecture Visualisation framework), enhanced with unique features like Interface and Flow, to describe your Application Architecture as Code. Experience an intuitive, efficient way to document complex systems, ensuring clarity and consistency across your teams and products.</Description>
<Copyright>Copyright 2024 Slava Vedernikov</Copyright>
Expand Down
21 changes: 20 additions & 1 deletion C4InterFlow/Structures/Component.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace C4InterFlow.Structures;
using C4InterFlow.Structures.Interfaces;

namespace C4InterFlow.Structures;

/// <summary>
/// The word "component" is a hugely overloaded term in the software development industry, but in this context a
Expand All @@ -17,6 +19,10 @@ public record Component : Structure
public string Container { get; init; }
public Note[]? Notes { get; init; }

public Component(Type type) : this(GetAlias(type), Utils.GetLabelFromAlias(GetAlias(type)))
{ }
public Component(Type type, string label) : this(GetAlias(type), label)
{ }
public Component(string alias) : this(alias, Utils.GetLabelFromAlias(alias))
{ }
public Component(string alias, string label) : this(Utils.GetContainerAlias(alias), alias, label)
Expand All @@ -27,4 +33,17 @@ public Component(string container, string alias, string label) : base(alias, lab
ComponentType = ComponentType.None;
}

public static string GetAlias<T>() where T : IComponentInstance
{
return Utils.GetStructureAlias<T>();
}

public static string GetAlias(Type type)
{
if (!typeof(IComponentInstance).IsAssignableFrom(type))
throw new ArgumentException($"Expected '{typeof(IComponentInstance).FullName}' type, but '{type.FullName}' type provided.", nameof(type));

return Utils.GetStructureAlias(type);
}

}
21 changes: 20 additions & 1 deletion C4InterFlow/Structures/Container.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace C4InterFlow.Structures;
using C4InterFlow.Structures.Interfaces;

namespace C4InterFlow.Structures;

/// <summary>
/// Not Docker! In the C4 model, a container represents an application or a data store. A container is something
Expand All @@ -17,6 +19,10 @@ public record Container : Structure
public string? Technology { get; init; }
public string SoftwareSystem { get; init; }

public Container(Type type) : this(GetAlias(type), Utils.GetLabelFromAlias(GetAlias(type)))
{ }
public Container(Type type, string label) : this(GetAlias(type), label)
{ }
public Container(string alias) : this(alias, Utils.GetLabelFromAlias(alias))
{ }
public Container(string alias, string label) : this(Utils.GetSoftwareSystemAlias(alias), alias, label)
Expand All @@ -26,4 +32,17 @@ public Container(string system, string alias, string label) : base(alias, label)
SoftwareSystem = system;
ContainerType = ContainerType.None;
}

public static string GetAlias<T>() where T : IContainerInstance
{
return Utils.GetStructureAlias<T>();
}

public static string GetAlias(Type type)
{
if (!typeof(IContainerInstance).IsAssignableFrom(type))
throw new ArgumentException($"Expected '{typeof(IContainerInstance).FullName}' type, but '{type.FullName}' type provided.", nameof(type));

return Utils.GetStructureAlias(type);
}
}
25 changes: 24 additions & 1 deletion C4InterFlow/Structures/Entity.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.ComponentModel;
using C4InterFlow.Structures.Interfaces;
using System.ComponentModel;

namespace C4InterFlow.Structures
{
Expand All @@ -22,12 +23,34 @@ public enum EntityType

public record Entity : Structure
{
public Entity(Type type, EntityType entityType) : this(GetAlias(type), Utils.GetLabelFromAlias(GetAlias(type)), entityType)
{ }
public Entity(Type type, string label, EntityType entityType) : this(GetAlias(type), label, entityType)
{ }
public Entity(string alias, EntityType entityType) : this(alias, Utils.GetLabelFromAlias(alias), entityType)
{ }
public Entity(string alias, string label, EntityType entityType) : this(Utils.GetContainerAlias(alias), alias, label, entityType)
{ }
public Entity(string container, string alias, string label, EntityType entityType)
: base(alias, label)
{
EntityType = entityType;
Container = container;
}

public static string GetAlias<T>() where T : IEntityInstance
{
return Utils.GetStructureAlias<T>();
}

public static string GetAlias(Type type)
{
if (!typeof(IEntityInstance).IsAssignableFrom(type))
throw new ArgumentException($"Expected '{typeof(IEntityInstance).FullName}' type, but '{type.FullName}' type provided.", nameof(type));

return Utils.GetStructureAlias(type);
}

public EntityType EntityType { get; init; }
public string Container { get; init; }
public string[] ComposedOfMany { get; init; }
Expand Down
5 changes: 5 additions & 0 deletions C4InterFlow/Structures/Flow.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@

using C4InterFlow.Structures.Interfaces;
using System.Text.RegularExpressions;

namespace C4InterFlow.Structures
Expand Down Expand Up @@ -513,6 +514,10 @@ public Flow Return(string value)
return this;
}

public Flow Use<T>() where T : IInterfaceInstance
{
return Use(Interface.GetAlias<T>());
}
public Flow Use(string interfaceAlias)
{
var flowType = FlowType.Use;
Expand Down
21 changes: 20 additions & 1 deletion C4InterFlow/Structures/Interface.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
namespace C4InterFlow.Structures
using C4InterFlow.Structures.Interfaces;

namespace C4InterFlow.Structures
{
public record Interface : Structure
{
public Interface(Type type) : this(GetAlias(type), Utils.GetLabelFromAlias(GetAlias(type)))
{ }
public Interface(Type type, string label) : this(GetAlias(type), label)
{ }
public Interface(string alias) : this(alias, Utils.GetLabelFromAlias(alias))
{ }
public Interface(string alias, string label) : this(Utils.GetInterfaceOwnerAlias(alias), alias, label)
Expand All @@ -11,6 +17,19 @@ public Interface(string owner, string alias, string label) : base(alias, label)
Owner = owner;
}

public static string GetAlias<T>() where T : IInterfaceInstance
{
return Utils.GetStructureAlias<T>();
}

public static string GetAlias(Type type)
{
if (!typeof(IInterfaceInstance).IsAssignableFrom(type))
throw new ArgumentException($"Expected '{typeof(IInterfaceInstance).FullName}' type, but '{type.FullName}' type provided.", nameof(type));

return Utils.GetStructureAlias(type);
}

public string Protocol { get; init; }
public bool IsPrivate { get; init; }
public string Path { get; init; }
Expand Down
21 changes: 20 additions & 1 deletion C4InterFlow/Structures/SoftwareSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace C4InterFlow.Structures;
using C4InterFlow.Structures.Interfaces;

namespace C4InterFlow.Structures;

/// <summary>
/// A software system is the highest level of abstraction and describes something that delivers value to its users,
Expand All @@ -9,6 +11,10 @@
/// </summary>
public sealed record SoftwareSystem : Structure
{
public SoftwareSystem(Type type) : this(GetAlias(type), Utils.GetLabelFromAlias(GetAlias(type)))
{ }
public SoftwareSystem(Type type, string label) : this(GetAlias(type), label)
{ }
public SoftwareSystem(string alias) : this(alias, Utils.GetLabelFromAlias(alias))
{ }
public SoftwareSystem(string alias, string label) : base(alias, label)
Expand All @@ -24,4 +30,17 @@ public SoftwareSystem(string alias, string label, string description, Boundary b
Description = description;
Boundary = boundary;
}

public static string GetAlias<T>() where T : ISoftwareSystemInstance
{
return Utils.GetStructureAlias<T>();
}

public static string GetAlias(Type type)
{
if (!typeof(ISoftwareSystemInstance).IsAssignableFrom(type))
throw new ArgumentException($"Expected '{typeof(ISoftwareSystemInstance).FullName}' type, but '{type.FullName}' type provided.", nameof(type));

return Utils.GetStructureAlias(type);
}
}
7 changes: 6 additions & 1 deletion C4InterFlow/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@ public static string GetContainerAlias(string alias)

public static string GetStructureAlias<T>()
{
return typeof(T).FullName?.Replace('+', '.') ?? string.Empty;
return GetStructureAlias(typeof(T));
}

public static string GetStructureAlias(Type type)
{
return type.FullName?.Replace('+', '.') ?? string.Empty;
}

public static string GetSoftwareSystemAlias(string alias)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace DotNetEShop.SoftwareSystems
{
public partial class BasketApi : ISoftwareSystemInstance
{
public static SoftwareSystem Instance => new SoftwareSystem(Utils.GetStructureAlias<BasketApi>(), "Basket Api")
public static SoftwareSystem Instance => new SoftwareSystem(typeof(BasketApi), "Basket Api")
{
Description = "",
Boundary = Boundary.Internal
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public partial class Containers
{
public partial class Data : IContainerInstance
{
public static Container Instance => new Container(Utils.GetStructureAlias<Data>(), "Data")
public static Container Instance => new Container(typeof(Data), "Data")
{
ContainerType = ContainerType.None,
Description = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public partial class Components
{
public partial class RedisBasketRepository : IComponentInstance
{
public static Component Instance => new Component(Utils.GetStructureAlias<RedisBasketRepository>(), "Redis Basket Repository")
public static Component Instance => new Component(typeof(RedisBasketRepository), "Redis Basket Repository")
{
ComponentType = ComponentType.None,
Description = "",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ public partial class Interfaces
{
public partial class DeleteBasketAsync : IInterfaceInstance
{
public static Interface Instance => new Interface(Utils.GetStructureAlias<DeleteBasketAsync>(), "Delete Basket Async")
public static Interface Instance => new Interface(typeof(DeleteBasketAsync), "Delete Basket Async")
{
Description = "",
Path = "",
IsPrivate = false,
Protocol = "",
Flow = new Flow(Utils.GetStructureAlias<DeleteBasketAsync>())
.Use("DotNetEShop.SoftwareSystems.BasketApi.Containers.Data.Components.RedisBasketRepository.Interfaces.GetBasketKey")
.Use("DotNetEShop.SoftwareSystems.BasketApi.Containers.Data.Components.RedisDatabase.Interfaces.KeyDeleteAsync"),
Flow = new Flow(Interface.GetAlias<DeleteBasketAsync>())
.Use<DotNetEShop.SoftwareSystems.BasketApi.Containers.Data.Components.RedisBasketRepository.Interfaces.GetBasketKey>()
.Use<DotNetEShop.SoftwareSystems.BasketApi.Containers.Data.Components.RedisDatabase.Interfaces.KeyDeleteAsync>(),
Input = "",
InputTemplate = "",
Output = "",
Expand Down
Loading

0 comments on commit d1592e4

Please sign in to comment.