diff --git a/ArchUnitNET/Domain/PlantUml/Export/PlantUmlDiagram.cs b/ArchUnitNET/Domain/PlantUml/Export/PlantUmlDiagram.cs
index 7d0e01167..a2af58e78 100644
--- a/ArchUnitNET/Domain/PlantUml/Export/PlantUmlDiagram.cs
+++ b/ArchUnitNET/Domain/PlantUml/Export/PlantUmlDiagram.cs
@@ -57,13 +57,14 @@ public string GetPlantUmlString(RenderOptions renderOptions)
///
///
/// A slice is identified in the diagram by its name, but not every name is a valid id.
- /// PlantUML rejects "[Inner] as App.Outer+Inner" and "Container(App.Orders.*.Http, ...)"
- /// as syntax errors, and slice names can contain such characters: SlicedBy names slices
- /// after type names such as "Outer+Inner" or "List`1", and a hand-built SliceIdentifier
- /// can be called anything. No character that a namespace cannot contain works in their
- /// place either, so the id is made from the name with those characters replaced by "_",
- /// and suffixed until it clashes with no other name in the diagram. The name itself stays
- /// the label, and slices whose names are valid ids keep them.
+ /// PlantUML rejects "[Inner] as App.Outer+Inner" and "Container(Slice Orders, ...)" as
+ /// syntax errors, and slice names can contain such characters: every slice description
+ /// starts with "Slice " and joins several parts with " - ", SlicedBy names slices after
+ /// type names such as "Outer+Inner" or "List`1", and a hand-built SliceIdentifier can be
+ /// called anything. No character that a namespace cannot contain works in their place either, so
+ /// the id is made from the name with those characters replaced by "_", and suffixed until
+ /// it clashes with no other name in the diagram. The name itself stays the label, and
+ /// slices whose names are valid ids keep them.
///
private static void AssignAliases(IReadOnlyCollection elements)
{
diff --git a/ArchUnitNET/Domain/PlantUml/Export/PlantUmlFileBuilder.cs b/ArchUnitNET/Domain/PlantUml/Export/PlantUmlFileBuilder.cs
index fc758aa6f..ef647c043 100644
--- a/ArchUnitNET/Domain/PlantUml/Export/PlantUmlFileBuilder.cs
+++ b/ArchUnitNET/Domain/PlantUml/Export/PlantUmlFileBuilder.cs
@@ -728,12 +728,14 @@ private void ReplaceCirclesWithAppropriateDependencyType()
/// nested slice is therefore drawn under its qualified name, the namespace its types
/// matched before the first capture group followed by its
/// , e.g. "App.Orders.Http", with that namespace
- /// as its root package. Every other slice is drawn under its description, without a
- /// package.
+ /// as its root package. Every other slice is drawn under its description, e.g.
+ /// "Slice Orders - Http", without a package.
///
///
- /// Qualified names of one pattern are distinct, but a description can coincide with a
- /// qualified name. Drawing two slices as one node would be wrong, so that is rejected.
+ /// A description always contains a space and a qualified name never does, so the two
+ /// can't be mistaken for each other, and qualified names of one pattern are distinct.
+ /// Descriptions can still coincide, for a custom slice whose part contains " - ".
+ /// Drawing two slices as one node would be wrong, so that is rejected.
///
///
///
@@ -757,11 +759,11 @@ private void AssignDiagramNames(bool nestSlicesInPackages)
if (slicesByName.TryGetValue(diagramName.Name, out var other))
{
throw new ArgumentException(
- "The slices \""
- + other.Identifier.Identifier
- + "\" and \""
- + slice.Identifier.Identifier
- + "\" would both be drawn as \""
+ "The slices with parts "
+ + FormatParts(other)
+ + " and "
+ + FormatParts(slice)
+ + " would both be drawn as \""
+ diagramName.Name
+ "\"."
);
@@ -772,6 +774,13 @@ private void AssignDiagramNames(bool nestSlicesInPackages)
}
}
+ private static string FormatParts(Slice slice)
+ {
+ return "["
+ + string.Join(", ", slice.Identifier.Parts.Select(part => "\"" + part + "\""))
+ + "]";
+ }
+
/// The name the diagram draws a slice under; see .
private string NameOf(Slice slice)
{
diff --git a/ArchUnitNET/Domain/SliceIdentifier.cs b/ArchUnitNET/Domain/SliceIdentifier.cs
index 8dc528989..fc1090391 100644
--- a/ArchUnitNET/Domain/SliceIdentifier.cs
+++ b/ArchUnitNET/Domain/SliceIdentifier.cs
@@ -1,28 +1,48 @@
-using JetBrains.Annotations;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using JetBrains.Annotations;
namespace ArchUnitNET.Domain
{
- public class SliceIdentifier : StringIdentifier, IHasDescription
+ ///
+ /// Identifies a slice by its parts: for a slice pattern, the values its capture groups
+ /// captured, in group order. Two identifiers are the same slice exactly when their parts are
+ /// equal and both are ignored or both are not.
+ ///
+ public class SliceIdentifier : IHasDescription
{
public static readonly SliceIdentifierComparer Comparer = new SliceIdentifierComparer();
public readonly bool Ignored;
private SliceIdentifier(
- string identifier,
+ IReadOnlyList parts,
bool ignored,
int? countOfAsteriskInPattern = null,
string matchedPrefix = null,
string diagramPath = null
)
- : base(identifier)
{
+ Parts = parts;
Ignored = ignored;
CountOfAsteriskInPattern = countOfAsteriskInPattern;
MatchedPrefix = matchedPrefix;
DiagramPath = diagramPath;
}
- public string Description => Identifier;
+ ///
+ /// What identifies the slice: for a slice pattern, the values its capture groups
+ /// captured, in group order. "App.(**)" over "App.Orders.Web" gives
+ /// [Orders.Web].
+ ///
+ public IReadOnlyList Parts { get; }
+
+ ///
+ /// The slice's name, built from its parts: "Slice " followed by the parts joined with
+ /// " - ", e.g. "Slice Orders - Http". It depends on the parts only, so equal slices always
+ /// have the same name.
+ ///
+ public string Description => "Slice " + string.Join(" - ", Parts);
///
/// The part of the type's namespace the pattern matched before the first capture group,
@@ -44,9 +64,31 @@ private SliceIdentifier(
public readonly int? CountOfAsteriskInPattern;
- public static SliceIdentifier Of(string identifier, int? countOfAsteriskInPattern = null)
+ ///
+ /// Creates an identifier for a slice with a single part, the same slice as
+ /// Of(new[] { part }).
+ ///
+ ///
+ /// The slice's only part: a custom SliceAssignment that returns identifiers with the
+ /// same part for two types puts them into the same slice.
+ ///
+ ///
+ /// The number of "(*)" groups in the pattern, if the slice comes from one.
+ ///
+ public static SliceIdentifier Of(string part, int? countOfAsteriskInPattern = null)
+ {
+ return new SliceIdentifier(CheckParts(new[] { part }), false, countOfAsteriskInPattern);
+ }
+
+ ///
+ /// Creates an identifier for the slice with the given parts. Identifiers with equal parts,
+ /// in the same order, belong to the same slice.
+ ///
+ /// The parts, at least one; none of them may be null.
+ /// There are no parts, or one of them is null.
+ public static SliceIdentifier Of(IEnumerable parts)
{
- return new SliceIdentifier(identifier, false, countOfAsteriskInPattern);
+ return new SliceIdentifier(CheckParts(parts), false);
}
///
@@ -54,14 +96,14 @@ public static SliceIdentifier Of(string identifier, int? countOfAsteriskInPatter
/// pattern matched, which diagrams use to nest the slice.
///
internal static SliceIdentifier OfMatch(
- string identifier,
+ IEnumerable parts,
int? countOfAsteriskInPattern,
string matchedPrefix,
string diagramPath
)
{
return new SliceIdentifier(
- identifier,
+ CheckParts(parts),
false,
countOfAsteriskInPattern,
matchedPrefix,
@@ -71,14 +113,34 @@ string diagramPath
public static SliceIdentifier Ignore()
{
- return new SliceIdentifier("Ignored", true);
+ return new SliceIdentifier(new[] { "Ignored" }, true);
+ }
+
+ private static IReadOnlyList CheckParts(IEnumerable parts)
+ {
+ if (parts == null)
+ {
+ throw new ArgumentNullException(nameof(parts));
+ }
+
+ var list = parts.ToList();
+ if (list.Count == 0)
+ {
+ throw new ArgumentException("A slice needs at least one part.", nameof(parts));
+ }
+
+ if (list.Any(part => part == null))
+ {
+ throw new ArgumentException("A slice part can't be null.", nameof(parts));
+ }
+
+ return list.AsReadOnly();
}
///
- /// Is true when the two SliceIdentifiers belong to the same slice
+ /// Whether the two identifiers belong to the same slice: both ignored, or both not
+ /// ignored with the same parts.
///
- ///
- ///
public bool CompareTo(SliceIdentifier other)
{
if (other == null)
@@ -87,12 +149,12 @@ public bool CompareTo(SliceIdentifier other)
}
return Ignored && other.Ignored
- || !Ignored && !other.Ignored && Identifier == other.Identifier;
+ || !Ignored && !other.Ignored && Parts.SequenceEqual(other.Parts);
}
private bool Equals(SliceIdentifier other)
{
- return Identifier == other.Identifier && Ignored == other.Ignored;
+ return Ignored == other.Ignored && Parts.SequenceEqual(other.Parts);
}
public override bool Equals(object obj)
@@ -114,10 +176,19 @@ public override int GetHashCode()
{
unchecked
{
- var hashCode = 397 ^ Identifier.GetHashCode();
- hashCode = (hashCode * 397) ^ Ignored.GetHashCode();
+ var hashCode = Ignored.GetHashCode();
+ foreach (var part in Parts)
+ {
+ hashCode = (hashCode * 397) ^ part.GetHashCode();
+ }
+
return hashCode;
}
}
+
+ public override string ToString()
+ {
+ return Description;
+ }
}
}
diff --git a/ArchUnitNET/Fluent/Slices/SliceRuleInitializer.cs b/ArchUnitNET/Fluent/Slices/SliceRuleInitializer.cs
index 1482a6602..8db081cf7 100644
--- a/ArchUnitNET/Fluent/Slices/SliceRuleInitializer.cs
+++ b/ArchUnitNET/Fluent/Slices/SliceRuleInitializer.cs
@@ -170,7 +170,7 @@ private static SliceIdentifier AssignFunc(
// What the pattern captured is both what identifies the slice and the path a
// diagram nests it along.
return SliceIdentifier.OfMatch(
- sliceString,
+ new[] { sliceString },
countOfSingleAsterisk,
matchedPrefix,
sliceString
diff --git a/ArchUnitNET/Fluent/Slices/SlicesShould.cs b/ArchUnitNET/Fluent/Slices/SlicesShould.cs
index 5e1fc1cfa..c67c60874 100644
--- a/ArchUnitNET/Fluent/Slices/SlicesShould.cs
+++ b/ArchUnitNET/Fluent/Slices/SlicesShould.cs
@@ -91,7 +91,7 @@ Architecture architecture
}
var cycleIdentifier = new EnumerableIdentifier(
- cycle.Contents.Select(slice => slice.Identifier)
+ cycle.Contents.Select(slice => new StringIdentifier(slice.Description))
);
yield return new EvaluationResult(
cycle,
@@ -106,7 +106,7 @@ Architecture architecture
else
{
var slicesIdentifier = new EnumerableIdentifier(
- slicesList.Select(slice => slice.Identifier)
+ slicesList.Select(slice => new StringIdentifier(slice.Description))
);
yield return new EvaluationResult(
slicesList,
@@ -166,7 +166,7 @@ Architecture architecture
yield return new EvaluationResult(
slice,
- slice.Identifier,
+ new StringIdentifier(slice.Description),
passed,
description,
archRule,
diff --git a/ArchUnitNETTests/ArchUnitNET/Storage/CustomPathFrozenRules.json b/ArchUnitNETTests/ArchUnitNET/Storage/CustomPathFrozenRules.json
index 9216791d3..ceb002561 100644
--- a/ArchUnitNETTests/ArchUnitNET/Storage/CustomPathFrozenRules.json
+++ b/ArchUnitNETTests/ArchUnitNET/Storage/CustomPathFrozenRules.json
@@ -15,12 +15,12 @@
{
"ArchRuleDescription": "Slices matching \"SlicesTestAssembly.MultipleSubnamespaces.(**)\" should not depend on each other",
"Violations": [
- "Slice3",
- "Slice3.Group1",
- "Slice2",
- "Slice2.Service",
- "Slice1",
- "Slice1.Service"
+ "Slice Slice3",
+ "Slice Slice3.Group1",
+ "Slice Slice2",
+ "Slice Slice2.Service",
+ "Slice Slice1",
+ "Slice Slice1.Service"
]
}
]
\ No newline at end of file
diff --git a/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.json b/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.json
index 5e4531c65..93b5d4c34 100644
--- a/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.json
+++ b/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.json
@@ -21,12 +21,12 @@
{
"ArchRuleDescription": "Slices matching \"SlicesTestAssembly.MultipleSubnamespaces.(**)\" should not depend on each other",
"Violations": [
- "Slice3",
- "Slice3.Group1",
- "Slice2",
- "Slice2.Service",
- "Slice1",
- "Slice1.Service"
+ "Slice Slice3",
+ "Slice Slice3.Group1",
+ "Slice Slice2",
+ "Slice Slice2.Service",
+ "Slice Slice1",
+ "Slice Slice1.Service"
]
},
{
diff --git a/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml b/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml
index c0905ff78..785f1107e 100644
--- a/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml
+++ b/ArchUnitNETTests/ArchUnitNET/Storage/FrozenRules.xml
@@ -12,11 +12,11 @@
ArchUnitNETTests.Fluent.FreezeTests+Violation
- Slice3
- Slice3.Group1
- Slice2
- Slice2.Service
- Slice1
- Slice1.Service
+ Slice Slice3
+ Slice Slice3.Group1
+ Slice Slice2
+ Slice Slice2.Service
+ Slice Slice1
+ Slice Slice1.Service
\ No newline at end of file
diff --git a/ArchUnitNETTests/Domain/PlantUml/PlantUmlSliceDiagramTests.cs b/ArchUnitNETTests/Domain/PlantUml/PlantUmlSliceDiagramTests.cs
index d3f13c11b..a02feb79b 100644
--- a/ArchUnitNETTests/Domain/PlantUml/PlantUmlSliceDiagramTests.cs
+++ b/ArchUnitNETTests/Domain/PlantUml/PlantUmlSliceDiagramTests.cs
@@ -154,9 +154,8 @@ public Task BuildUmlBySlicesFocusOn_Matching_ColorsFocusedSlices()
{
// Slices that are not nested have no namespace, so the focused ones take
// PlantUmlSlice.BuildString's namespace-less branch with a colour set -- the only
- // path on which the exporter renders "[Slice1] #99ffd1" rather than a package.
- // They are drawn under their description, which carries no Root prefix, hence the
- // bare focus string.
+ // path on which the exporter renders "[Slice Slice1] #99ffd1" rather than a package.
+ // They are drawn under their description, which "Slice1" is contained in.
var slices = SortedSlices(SliceRuleDefinition.Slices().Matching(Root + "(**)"));
var uml = new PlantUmlFileBuilder()
.WithDependenciesFromFocusOn(slices, "Slice1")
@@ -371,9 +370,9 @@ public void BuildUmlBySlices_SameSlicesDrawnFlatAndNested()
var nested = new PlantUmlFileBuilder().WithDependenciesFrom(slices, Nested).AsString();
Assert.DoesNotContain("package", flat);
- Assert.Contains("[Slice1.Service]", flat);
+ Assert.Contains("[Slice Slice1] as Slice_Slice1", flat);
Assert.Contains("package \"SlicesTestAssembly.MultipleSubnamespaces\"", nested);
- Assert.DoesNotContain("[Slice1.Service]", nested);
+ Assert.DoesNotContain("Slice Slice1", nested);
}
// --- Two slices may not share a node ----------------------------------------------
@@ -381,20 +380,18 @@ public void BuildUmlBySlices_SameSlicesDrawnFlatAndNested()
[Fact]
public void BuildUmlBySlices_SameDiagramName_Throws()
{
- // A slice that is not nested is drawn under its description, which here is the
- // qualified name a nested slice is drawn under.
- var slices = SortedSlices(SliceRuleDefinition.Slices().Matching(Root + "(**)"))
- .Concat(
- new[] { new Slice(SliceIdentifier.Of(Root + "Slice1"), Array.Empty()) }
- )
- .ToList();
+ // Different slices, both described "Slice A - B".
+ var slices = new[]
+ {
+ new Slice(SliceIdentifier.Of(new[] { "A - B" }), Array.Empty()),
+ new Slice(SliceIdentifier.Of(new[] { "A", "B" }), Array.Empty()),
+ };
var ex = Assert.Throws(() =>
- new PlantUmlFileBuilder().WithDependenciesFrom(slices, Nested)
+ new PlantUmlFileBuilder().WithDependenciesFrom(slices)
);
Assert.Equal(
- "The slices \"Slice1\" and \"SlicesTestAssembly.MultipleSubnamespaces.Slice1\" "
- + "would both be drawn as \"SlicesTestAssembly.MultipleSubnamespaces.Slice1\".",
+ "The slices with parts [\"A - B\"] and [\"A\", \"B\"] would both be drawn as \"Slice A - B\".",
ex.Message
);
}
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlicesFocusOn_Matching_ColorsFocusedSlices.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlicesFocusOn_Matching_ColorsFocusedSlices.verified.txt
index b96ed6505..efeb37eb4 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlicesFocusOn_Matching_ColorsFocusedSlices.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlicesFocusOn_Matching_ColorsFocusedSlices.verified.txt
@@ -4,12 +4,12 @@
HIDE_STEREOTYPE()
-[Slice1] #99ffd1
-[Slice1.Service] #99ffd1
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice3] --|> [Slice1]
+[Slice Slice1] as Slice_Slice1 #99ffd1
+[Slice Slice1.Service] as Slice_Slice1.Service #99ffd1
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice3] --|> [Slice_Slice1]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_C4Style.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_C4Style.verified.txt
index 851e24568..9b02ce5b4 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_C4Style.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_C4Style.verified.txt
@@ -4,19 +4,19 @@
HIDE_STEREOTYPE()
-Container(Slice1, Slice1)
-Container(Slice1.Service, Slice1.Service)
-Container(Slice2, Slice2)
-Container(Slice2.Service, Slice2.Service)
-Container(Slice3, Slice3)
-Container(Slice3.Group1, Slice3.Group1)
-Container(Slice3.Group1.Inner, Slice3.Group1.Inner)
-Container(Slice3.Group2, Slice3.Group2)
-Container(Slice3.Group2.Inner, Slice3.Group2.Inner)
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
-[Slice3.Group1] --|> [Slice3]
+Container(Slice_Slice1, Slice Slice1)
+Container(Slice_Slice1.Service, Slice Slice1.Service)
+Container(Slice_Slice2, Slice Slice2)
+Container(Slice_Slice2.Service, Slice Slice2.Service)
+Container(Slice_Slice3, Slice Slice3)
+Container(Slice_Slice3.Group1, Slice Slice3.Group1)
+Container(Slice_Slice3.Group1.Inner, Slice Slice3.Group1.Inner)
+Container(Slice_Slice3.Group2, Slice Slice3.Group2)
+Container(Slice_Slice3.Group2.Inner, Slice Slice3.Group2.Inner)
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
+[Slice_Slice3.Group1] --|> [Slice_Slice3]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DirectCircle_RendersCircleDependency.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DirectCircle_RendersCircleDependency.verified.txt
index ffc596323..846b74ca6 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DirectCircle_RendersCircleDependency.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DirectCircle_RendersCircleDependency.verified.txt
@@ -4,7 +4,7 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice2]
-[Slice1] <-[#red]> [Slice2]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice2] as Slice_Slice2
+[Slice_Slice1] <-[#red]> [Slice_Slice2]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DoubleAsterisk.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DoubleAsterisk.verified.txt
index b33b9dcab..92de1e097 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DoubleAsterisk.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_DoubleAsterisk.verified.txt
@@ -4,19 +4,19 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice3.Group1.Inner]
-[Slice3.Group2]
-[Slice3.Group2.Inner]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
-[Slice3.Group1] --|> [Slice3]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice Slice3.Group1.Inner] as Slice_Slice3.Group1.Inner
+[Slice Slice3.Group2] as Slice_Slice3.Group2
+[Slice Slice3.Group2.Inner] as Slice_Slice3.Group2.Inner
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
+[Slice_Slice3.Group1] --|> [Slice_Slice3]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_ExcludeNodesWithoutDependencies.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_ExcludeNodesWithoutDependencies.verified.txt
index c5667533f..5baf30abd 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_ExcludeNodesWithoutDependencies.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_ExcludeNodesWithoutDependencies.verified.txt
@@ -4,16 +4,16 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
-[Slice3.Group1] --|> [Slice3]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
+[Slice_Slice3.Group1] --|> [Slice_Slice3]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_LimitDependencies_Compact.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_LimitDependencies_Compact.verified.txt
index 991ec5378..4d06a235e 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_LimitDependencies_Compact.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_LimitDependencies_Compact.verified.txt
@@ -4,17 +4,17 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice3.Group1.Inner]
-[Slice3.Group2]
-[Slice3.Group2.Inner]
-[Slice1] --> [Slice2]
-[Slice1.Service] --> [Slice2.Service]
-[Slice2] --> [Slice3]
-[Slice3] --> [Slice1]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice Slice3.Group1.Inner] as Slice_Slice3.Group1.Inner
+[Slice Slice3.Group2] as Slice_Slice3.Group2
+[Slice Slice3.Group2.Inner] as Slice_Slice3.Group2.Inner
+[Slice_Slice1] --> [Slice_Slice2]
+[Slice_Slice1.Service] --> [Slice_Slice2.Service]
+[Slice_Slice2] --> [Slice_Slice3]
+[Slice_Slice3] --> [Slice_Slice1]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_MultipleCaptureGroups.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_MultipleCaptureGroups.verified.txt
index 041ebd608..8e5c21f27 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_MultipleCaptureGroups.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_MultipleCaptureGroups.verified.txt
@@ -4,17 +4,17 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice3.Group2]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
-[Slice3.Group1] --|> [Slice3]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice Slice3.Group2] as Slice_Slice3.Group2
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
+[Slice_Slice3.Group1] --|> [Slice_Slice3]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_NonContiguousCaptureGroups.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_NonContiguousCaptureGroups.verified.txt
index 041ebd608..8e5c21f27 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_NonContiguousCaptureGroups.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_NonContiguousCaptureGroups.verified.txt
@@ -4,17 +4,17 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice3.Group2]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
-[Slice3.Group1] --|> [Slice3]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice Slice3.Group2] as Slice_Slice3.Group2
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
+[Slice_Slice3.Group1] --|> [Slice_Slice3]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_SingleAsterisk.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_SingleAsterisk.verified.txt
index c37caedd7..a4f69a9d3 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_SingleAsterisk.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_SingleAsterisk.verified.txt
@@ -4,10 +4,10 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice2]
-[Slice3]
-[Slice1] --|> [Slice2]
-[Slice2] --|> [Slice3]
-[Slice3] --|> [Slice1]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice2] as Slice_Slice2
+[Slice Slice3] as Slice_Slice3
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
@enduml
diff --git a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_WithDependencyFilter.verified.txt b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_WithDependencyFilter.verified.txt
index 969b504e8..61a2c05ad 100644
--- a/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_WithDependencyFilter.verified.txt
+++ b/ArchUnitNETTests/Domain/PlantUml/Snapshots/PlantUmlSliceDiagramTests.BuildUmlBySlices_WithDependencyFilter.verified.txt
@@ -4,18 +4,18 @@
HIDE_STEREOTYPE()
-[Slice1]
-[Slice1.Service]
-[Slice2]
-[Slice2.Service]
-[Slice3]
-[Slice3.Group1]
-[Slice3.Group1.Inner]
-[Slice3.Group2]
-[Slice3.Group2.Inner]
-[Slice1] --|> [Slice2]
-[Slice1.Service] --|> [Slice2.Service]
-[Slice2] --|> [Slice3]
-[Slice2.Service] --|> [Slice3]
-[Slice3] --|> [Slice1]
+[Slice Slice1] as Slice_Slice1
+[Slice Slice1.Service] as Slice_Slice1.Service
+[Slice Slice2] as Slice_Slice2
+[Slice Slice2.Service] as Slice_Slice2.Service
+[Slice Slice3] as Slice_Slice3
+[Slice Slice3.Group1] as Slice_Slice3.Group1
+[Slice Slice3.Group1.Inner] as Slice_Slice3.Group1.Inner
+[Slice Slice3.Group2] as Slice_Slice3.Group2
+[Slice Slice3.Group2.Inner] as Slice_Slice3.Group2.Inner
+[Slice_Slice1] --|> [Slice_Slice2]
+[Slice_Slice1.Service] --|> [Slice_Slice2.Service]
+[Slice_Slice2] --|> [Slice_Slice3]
+[Slice_Slice2.Service] --|> [Slice_Slice3]
+[Slice_Slice3] --|> [Slice_Slice1]
@enduml
diff --git a/ArchUnitNETTests/Fluent/Slices/SliceDomainTests.cs b/ArchUnitNETTests/Fluent/Slices/SliceDomainTests.cs
index 0c2b92e02..f61fc8927 100644
--- a/ArchUnitNETTests/Fluent/Slices/SliceDomainTests.cs
+++ b/ArchUnitNETTests/Fluent/Slices/SliceDomainTests.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using ArchUnitNET.Domain;
using ArchUnitNET.Domain.Extensions;
@@ -65,7 +65,7 @@ public void Slice_ToString_ReturnsDescription()
{
var slice = new Slice(SliceIdentifier.Of("SomeSlice"), Array.Empty());
- Assert.Equal("SomeSlice", slice.ToString());
+ Assert.Equal("Slice SomeSlice", slice.ToString());
Assert.Equal(slice.Description, slice.ToString());
}
@@ -104,6 +104,14 @@ public void SliceIdentifier_CompareTo_Null_ReturnsFalse()
Assert.False(SliceIdentifier.Of("A").CompareTo(null));
}
+ [Fact]
+ public void SliceIdentifier_CompareTo_OneIgnored_ReturnsFalse()
+ {
+ // Same parts, so only the Ignored flag tells the two apart.
+ Assert.False(SliceIdentifier.Of("Ignored").CompareTo(SliceIdentifier.Ignore()));
+ Assert.False(SliceIdentifier.Ignore().CompareTo(SliceIdentifier.Of("Ignored")));
+ }
+
[Fact]
public void SliceIdentifier_Equals_Branches()
{
@@ -116,15 +124,132 @@ public void SliceIdentifier_Equals_Branches()
Assert.False(identifier.Equals(SliceIdentifier.Of("B")));
}
+ [Fact]
+ public void SliceIdentifier_Equals_ComparesParts()
+ {
+ var identifier = SliceIdentifier.Of(new[] { "A", "B" });
+
+ Assert.Equal(SliceIdentifier.Of(new[] { "A", "B" }), identifier);
+ Assert.Equal(
+ SliceIdentifier.Of(new[] { "A", "B" }).GetHashCode(),
+ identifier.GetHashCode()
+ );
+ Assert.NotEqual(SliceIdentifier.Of(new[] { "B", "A" }), identifier);
+ Assert.NotEqual(SliceIdentifier.Of("A.B"), identifier);
+ Assert.NotEqual(SliceIdentifier.Of(new[] { "A", "B", "C" }), identifier);
+ }
+
[Fact]
public void SliceIdentifier_Equals_IgnoresDiagramData()
{
// Where a diagram draws the slice is no part of its identity.
- var nested = SliceIdentifier.OfMatch("A.B", null, "X.", "A.B");
+ var nested = SliceIdentifier.OfMatch(new[] { "A", "B" }, null, "X.", "A.B");
+
+ Assert.Equal(SliceIdentifier.Of(new[] { "A", "B" }), nested);
+ Assert.Equal(
+ SliceIdentifier.Of(new[] { "A", "B" }).GetHashCode(),
+ nested.GetHashCode()
+ );
+ Assert.Equal("Slice A - B", nested.Description);
+ }
+
+ [Fact]
+ public void SliceIdentifier_OfString_IsASingleSlicePart()
+ {
+ var ofString = SliceIdentifier.Of("Orders");
+ var ofParts = SliceIdentifier.Of(new[] { "Orders" });
+
+ Assert.Equal(new[] { "Orders" }, ofString.Parts);
+ Assert.Equal(ofParts, ofString);
+ Assert.Equal("Slice Orders", ofString.Description);
+ Assert.Equal("Slice Orders", ofParts.Description);
+ }
+
+ [Fact]
+ public void SliceIdentifier_Description_PrefixesTheJoinedParts()
+ {
+ Assert.Equal("Slice Orders.Web", SliceIdentifier.Of("Orders.Web").Description);
+ Assert.Equal(
+ "Slice Orders - Http",
+ SliceIdentifier.Of(new[] { "Orders", "Http" }).Description
+ );
+ }
+
+ [Fact]
+ public void SliceIdentifier_Of_RejectsMissingParts()
+ {
+ Assert.Throws(() =>
+ SliceIdentifier.Of((System.Collections.Generic.IEnumerable)null)
+ );
+ Assert.Throws(() => SliceIdentifier.Of(new string[0]));
+ Assert.Throws(() => SliceIdentifier.Of(new[] { "A", null }));
+ }
+
+ [Fact]
+ public void SliceIdentifier_PartsContainingTheSeparator_AreADifferentSlice()
+ {
+ // Both are described "Slice A - B", but they are different slices.
+ var onePart = SliceIdentifier.Of(new[] { "A - B" });
+ var twoParts = SliceIdentifier.Of(new[] { "A", "B" });
+
+ Assert.Equal(onePart.Description, twoParts.Description);
+ Assert.NotEqual(onePart, twoParts);
+ Assert.False(SliceIdentifier.Comparer.Equals(onePart, twoParts));
+ }
+
+ [Fact]
+ public void SliceIdentifier_ToString_IsTheDescription()
+ {
+ Assert.Equal("Slice Orders", SliceIdentifier.Of("Orders").ToString());
+ }
+
+ // --- Violation keys, which violation stores keep ---------------------------------
+
+ [Fact]
+ public void ViolationKey_IsTheDescription()
+ {
+ var keys = SliceRuleDefinition
+ .Slices()
+ .Matching(Root + "(*)")
+ .Should()
+ .NotDependOnEachOther()
+ .Evaluate(Architecture)
+ .Select(result => result.EvaluatedObjectIdentifier.Identifier);
+
+ Assert.Contains("Slice Slice1", keys);
+ }
+
+ [Fact]
+ public void ViolationKey_OfSeveralParts_IsTheDescription()
+ {
+ // Patterns capture a single part, so a custom assignment builds several.
+ var creator = new SliceRuleCreator();
+ creator.SetSliceAssignment(
+ new SliceAssignment(
+ type => SliceIdentifier.Of(new[] { "Custom", type.Name }),
+ "by name"
+ )
+ );
+
+ var keys = new SlicesShould(creator)
+ .NotDependOnEachOther()
+ .Evaluate(Architecture)
+ .Select(result => result.EvaluatedObjectIdentifier.Identifier);
+
+ Assert.Contains("Slice Custom - Slice1Class", keys);
+ }
+
+ // --- SlicedBy ------------------------------------------------------------------
+
+ [Fact]
+ public void SlicedBy_NamesSlicesAfterTheTypeLikeEveryOtherSlice()
+ {
+ var type = Architecture.GetClassOfType(typeof(MixedTypesClass));
+
+ var slice = Assert.Single(new IType[] { type }.SlicedBy(""));
- Assert.Equal(SliceIdentifier.Of("A.B"), nested);
- Assert.Equal(SliceIdentifier.Of("A.B").GetHashCode(), nested.GetHashCode());
- Assert.Equal("A.B", nested.Description);
+ Assert.Equal(new[] { type.FullName }, slice.Identifier.Parts);
+ Assert.Equal("Slice " + type.FullName, slice.Description);
}
[Fact]
diff --git a/ArchUnitNETTests/Fluent/Slices/SlicesTests.cs b/ArchUnitNETTests/Fluent/Slices/SlicesTests.cs
index 232cb4a2b..67290d3ba 100644
--- a/ArchUnitNETTests/Fluent/Slices/SlicesTests.cs
+++ b/ArchUnitNETTests/Fluent/Slices/SlicesTests.cs
@@ -176,15 +176,15 @@ public void Matching_SingleAsterisk_CapturesEveryDepth()
Assert.Equal(
new[]
{
- "Slice1",
- "Slice1.Service",
- "Slice2",
- "Slice2.Service",
- "Slice3",
- "Slice3.Group1",
- "Slice3.Group1.Inner",
- "Slice3.Group2",
- "Slice3.Group2.Inner",
+ "Slice Slice1",
+ "Slice Slice1.Service",
+ "Slice Slice2",
+ "Slice Slice2.Service",
+ "Slice Slice3",
+ "Slice Slice3.Group1",
+ "Slice Slice3.Group1.Inner",
+ "Slice Slice3.Group2",
+ "Slice Slice3.Group2.Inner",
},
Descriptions(Root + "(*)")
);
@@ -199,7 +199,10 @@ public void Matching_SingleAsteriskDotDot_CapturesEveryDepth()
[Fact]
public void Matching_DoubleAsteriskDotDot_FoldsSubnamespacesIntoParent()
{
- Assert.Equal(new[] { "Slice1", "Slice2", "Slice3" }, Descriptions(Root + "(**).."));
+ Assert.Equal(
+ new[] { "Slice Slice1", "Slice Slice2", "Slice Slice3" },
+ Descriptions(Root + "(**)..")
+ );
}
///
@@ -227,8 +230,8 @@ public void Matching_DoubleAsteriskDotDot_DropsTypesDirectlyInCapturedNamespace(
.ToArray()
);
- Assert.Equal(new[] { Root + "Slice1.Service.Service1Class" }, types["Slice1"]);
- Assert.Equal(new[] { Root + "Slice2.Service.Service2Class" }, types["Slice2"]);
+ Assert.Equal(new[] { Root + "Slice1.Service.Service1Class" }, types["Slice Slice1"]);
+ Assert.Equal(new[] { Root + "Slice2.Service.Service2Class" }, types["Slice Slice2"]);
Assert.Equal(
new[]
{
@@ -237,7 +240,7 @@ public void Matching_DoubleAsteriskDotDot_DropsTypesDirectlyInCapturedNamespace(
Root + "Slice3.Group2.Group2Class",
Root + "Slice3.Group2.Inner.Inner2Class",
},
- types["Slice3"]
+ types["Slice Slice3"]
);
}
@@ -276,7 +279,7 @@ public void DuplicatePrefixSegment_CapturesAfterTheFirstOccurrenceOfThePrefix()
.ToList();
Assert.Single(slices);
- Assert.Equal("DuplicatePrefix.Sub", slices[0].Description);
+ Assert.Equal("Slice DuplicatePrefix.Sub", slices[0].Description);
}
// --- ".." semantics -------------------------------------------------------------
@@ -287,11 +290,11 @@ public void DotDot_BetweenCaptureGroups_CapturesEveryDepth()
Assert.Equal(
new[]
{
- "Alpha.Service",
- "AlphaService",
- "Outer.Inner",
- "Outer.Mid.Inner",
- "Single",
+ "Slice Alpha.Service",
+ "Slice AlphaService",
+ "Slice Outer.Inner",
+ "Slice Outer.Mid.Inner",
+ "Slice Single",
},
Descriptions(DotDot + "(*)..(*)")
);
@@ -303,11 +306,11 @@ public void DotDot_BeforeLiteral_CapturesEveryDepth()
Assert.Equal(
new[]
{
- "Alpha.Service",
- "AlphaService",
- "Outer.Inner",
- "Outer.Mid.Inner",
- "Single",
+ "Slice Alpha.Service",
+ "Slice AlphaService",
+ "Slice Outer.Inner",
+ "Slice Outer.Mid.Inner",
+ "Slice Single",
},
Descriptions(DotDot + "(*)..Service")
);
@@ -328,8 +331,8 @@ public void DotDot_BeforeLiteral_GivesAlphaServiceAndAlphaDotServiceDistinctSlic
// holding exactly the one type it's made of. This is not evidence the matcher tells
// the two shapes apart -- it would look identical if it ignored segment boundaries
// entirely.
- Assert.Single(slices.Single(slice => slice.Description == "AlphaService").Types);
- Assert.Single(slices.Single(slice => slice.Description == "Alpha.Service").Types);
+ Assert.Single(slices.Single(slice => slice.Description == "Slice AlphaService").Types);
+ Assert.Single(slices.Single(slice => slice.Description == "Slice Alpha.Service").Types);
}
// --- failure messages -----------------------------------------------------------
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_FluentDefinition_CombinesEvaluationAndDescription.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
index 15d5cc49f..7c6f6bb58 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
@@ -21,11 +21,11 @@ All Evaluations passed
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles and Types that have full name "SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: True
@@ -33,11 +33,11 @@ Description: SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass pa
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_WithArchRule_CombinesEvaluationAndDescription.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_WithArchRule_CombinesEvaluationAndDescription.verified.txt
index c22e38343..f13e66cbd 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_WithArchRule_CombinesEvaluationAndDescription.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.And_WithArchRule_CombinesEvaluationAndDescription.verified.txt
@@ -26,11 +26,11 @@ Message:
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles and Types that have name "NotTheNameOfAnyObject" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: False
@@ -38,11 +38,11 @@ Description: There are no objects matching the criteria
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_FluentDefinition_CombinesEvaluationAndDescription.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
index 14faa3188..acbf6584a 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_FluentDefinition_CombinesEvaluationAndDescription.verified.txt
@@ -3,21 +3,21 @@
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
@@ -28,11 +28,11 @@ Slice3 -> Slice1
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles or Types that have full name "SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: True
@@ -40,11 +40,11 @@ Description: SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass pa
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
@@ -55,11 +55,11 @@ Slice3 -> Slice1
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles or Types that have name "NotTheNameOfAnyObject" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: False
@@ -67,11 +67,11 @@ Description: There are no objects matching the criteria
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_WithArchRule_CombinesEvaluationAndDescription.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_WithArchRule_CombinesEvaluationAndDescription.verified.txt
index 42340d70c..c2a19f195 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_WithArchRule_CombinesEvaluationAndDescription.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SliceRuleCombinationTests.Or_WithArchRule_CombinesEvaluationAndDescription.verified.txt
@@ -3,11 +3,11 @@
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles or Types that have full name "SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: True
@@ -15,11 +15,11 @@ Description: SlicesTestAssembly.DotDotSemantics.Outer.Mid.Inner.MidInnerClass pa
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
@@ -30,11 +30,11 @@ Slice3 -> Slice1
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles or Types that have name "NotTheNameOfAnyObject" should exist
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: False
@@ -42,11 +42,11 @@ Description: There are no objects matching the criteria
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_MultipleSubnamespaces_ReportsEveryCycle.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_MultipleSubnamespaces_ReportsEveryCycle.verified.txt
index b15623362..88b409548 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_MultipleSubnamespaces_ReportsEveryCycle.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_MultipleSubnamespaces_ReportsEveryCycle.verified.txt
@@ -1,21 +1,21 @@
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles
Result: False
Description: Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should be free of cycles" failed:
Cycle found:
-Slice2 -> Slice3
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
-Slice3 -> Slice1
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_WithCycle_ReturnsDescriptiveCycleMessage.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_WithCycle_ReturnsDescriptiveCycleMessage.verified.txt
index 03a2b704c..0bb65e78b 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_WithCycle_ReturnsDescriptiveCycleMessage.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.BeFreeOfCycles_WithCycle_ReturnsDescriptiveCycleMessage.verified.txt
@@ -1,17 +1,17 @@
Query: Slices matching "SlicesTestAssembly.DirectCircle.(*)" should be free of cycles
Result: False
Description: Cycle found:
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.DirectCircle.Slice1.Slice1Class -> SlicesTestAssembly.DirectCircle.Slice2.Slice2Class
-Slice2 -> Slice1
+Slice Slice2 -> Slice Slice1
SlicesTestAssembly.DirectCircle.Slice2.Slice2Class -> SlicesTestAssembly.DirectCircle.Slice1.Slice1Class
Message:
"Slices matching "SlicesTestAssembly.DirectCircle.(*)" should be free of cycles" failed:
Cycle found:
-Slice1 -> Slice2
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.DirectCircle.Slice1.Slice1Class -> SlicesTestAssembly.DirectCircle.Slice2.Slice2Class
-Slice2 -> Slice1
+Slice Slice2 -> Slice Slice1
SlicesTestAssembly.DirectCircle.Slice2.Slice2Class -> SlicesTestAssembly.DirectCircle.Slice1.Slice1Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_MultipleSubnamespaces_ReportsEveryDependency.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_MultipleSubnamespaces_ReportsEveryDependency.verified.txt
index 0a801d01b..d17bbc960 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_MultipleSubnamespaces_ReportsEveryDependency.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_MultipleSubnamespaces_ReportsEveryDependency.verified.txt
@@ -1,64 +1,64 @@
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should not depend on each other
Result: False
-Description: Slice1 does depend on other slices:
-Slice1 -> Slice2
+Description: Slice Slice1 does depend on other slices:
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
Result: False
-Description: Slice1.Service does depend on other slices:
-Slice1.Service -> Slice2.Service
+Description: Slice Slice1.Service does depend on other slices:
+Slice Slice1.Service -> Slice Slice2.Service
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Service.Service1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class
Result: False
-Description: Slice2 does depend on other slices:
-Slice2 -> Slice3
+Description: Slice Slice2 does depend on other slices:
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
Result: False
-Description: Slice2.Service does depend on other slices:
-Slice2.Service -> Slice3
+Description: Slice Slice2.Service does depend on other slices:
+Slice Slice2.Service -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
Result: False
-Description: Slice3 does depend on other slices:
-Slice3 -> Slice1
+Description: Slice Slice3 does depend on other slices:
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
Result: False
-Description: Slice3.Group1 does depend on other slices:
-Slice3.Group1 -> Slice3
+Description: Slice Slice3.Group1 does depend on other slices:
+Slice Slice3.Group1 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Group1.Group1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
Result: True
-Description: Slice3.Group1.Inner does not depend on another slice.
+Description: Slice Slice3.Group1.Inner does not depend on another slice.
Result: True
-Description: Slice3.Group2 does not depend on another slice.
+Description: Slice Slice3.Group2 does not depend on another slice.
Result: True
-Description: Slice3.Group2.Inner does not depend on another slice.
+Description: Slice Slice3.Group2.Inner does not depend on another slice.
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**)" should not depend on each other" failed:
- Slice1 does depend on other slices:
-Slice1 -> Slice2
+ Slice Slice1 does depend on other slices:
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class
- Slice1.Service does depend on other slices:
-Slice1.Service -> Slice2.Service
+ Slice Slice1.Service does depend on other slices:
+Slice Slice1.Service -> Slice Slice2.Service
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Service.Service1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class
- Slice2 does depend on other slices:
-Slice2 -> Slice3
+ Slice Slice2 does depend on other slices:
+Slice Slice2 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Slice2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
- Slice2.Service does depend on other slices:
-Slice2.Service -> Slice3
+ Slice Slice2.Service does depend on other slices:
+Slice Slice2.Service -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
- Slice3 does depend on other slices:
-Slice3 -> Slice1
+ Slice Slice3 does depend on other slices:
+Slice Slice3 -> Slice Slice1
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice1.Slice1Class
- Slice3.Group1 does depend on other slices:
-Slice3.Group1 -> Slice3
+ Slice Slice3.Group1 does depend on other slices:
+Slice Slice3.Group1 -> Slice Slice3
SlicesTestAssembly.MultipleSubnamespaces.Slice3.Group1.Group1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice3.Slice3Class
diff --git a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_ReturnsDescriptiveDependencyMessage.verified.txt b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_ReturnsDescriptiveDependencyMessage.verified.txt
index 448e70e4b..56687bc1e 100644
--- a/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_ReturnsDescriptiveDependencyMessage.verified.txt
+++ b/ArchUnitNETTests/Fluent/Slices/Snapshots/SlicesTests.NotDependOnEachOther_ReturnsDescriptiveDependencyMessage.verified.txt
@@ -1,17 +1,17 @@
Query: Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**).." should not depend on each other
Result: False
-Description: Slice1 does depend on other slices:
-Slice1 -> Slice2
+Description: Slice Slice1 does depend on other slices:
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Service.Service1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class
Result: True
-Description: Slice2 does not depend on another slice.
+Description: Slice Slice2 does not depend on another slice.
Result: True
-Description: Slice3 does not depend on another slice.
+Description: Slice Slice3 does not depend on another slice.
Message:
"Slices matching "SlicesTestAssembly.MultipleSubnamespaces.(**).." should not depend on each other" failed:
- Slice1 does depend on other slices:
-Slice1 -> Slice2
+ Slice Slice1 does depend on other slices:
+Slice Slice1 -> Slice Slice2
SlicesTestAssembly.MultipleSubnamespaces.Slice1.Service.Service1Class -> SlicesTestAssembly.MultipleSubnamespaces.Slice2.Service.Service2Class
diff --git a/documentation/docs/guide.md b/documentation/docs/guide.md
index 2c522a8c1..ccf6d5a52 100644
--- a/documentation/docs/guide.md
+++ b/documentation/docs/guide.md
@@ -174,13 +174,46 @@ IArchRule rule = Classes().That().DoNotHaveAnyAttributes(typeof(Display)).Should
.NotDependOnAny(Classes().That().AreAssignableTo(typeof(ICanvas)));
```
-### 3.6. Cycle Rule
+### 3.6. Slice Rules
+Slices group types by namespace. A *slice* is the set of types whose namespaces produced the same
+captured values from a pattern -- with `"App.(*)"`, the types in `App.Orders` form one slice, those in
+`App.Shipping` another.
+
+To use the `Slices()` entry point directly, add:
+```cs
+using static ArchUnitNET.Fluent.Slices.SliceRuleDefinition;
+```
+
+#### How slices are named
+A slice is identified by **what its capture groups captured**, in group order. Two types end up in
+the same slice exactly when their groups captured the same values.
+
+A slice is named after its captures: `Slice ` followed by the captured values joined with ` - `. The
+name never reads as a namespace, and it is what failure messages and diagrams show.
+
+| Pattern | Namespace | Slice name |
+|---|---|---|
+| `App.(*)` | `App.Orders` | `Slice Orders` |
+| `App.(**)` | `App.Orders.Web` | `Slice Orders.Web` |
+
+Frozen violations of a slice rule (see `FreezingArchRule`) are stored by the slice name, e.g.
+`Slice Orders`.
+
+#### Cycle-free rule

```cs
IArchRule rule = Slices().Matching("Module.(*)").Should()
.BeFreeOfCycles();
```
+#### No-dependency rule
+```cs
+IArchRule rule = Slices().Matching("Module.(*)").Should()
+ .NotDependOnEachOther();
+```
+`NotDependOnEachOther` forbids *any* dependency between slices, where `BeFreeOfCycles` only forbids
+circular ones.
+
## 4. How to check
To get a meaningful error message we recommend using