Skip to content

Nest slices in packages via GenerationOptions, deprecate MatchingWithPackages - #513

Draft
alexanderlinne wants to merge 1 commit into
stop-deriving-namespace-from-slicefrom
nest-slices-in-packages-option
Draft

alexanderlinne wants to merge 1 commit into
stop-deriving-namespace-from-slicefrom
nest-slices-in-packages-option

Conversation

@alexanderlinne

Copy link
Copy Markdown
Collaborator

Why MatchingWithPackages existed, and why it no longer has to

On main, a slice carries a single string, its name, plus an optional NameSpace. The PlantUML exporter works out everything from those strings:

  • which slice is a package of another (StartsWith),
  • which arrows to merge (Contains),
  • how deep LimitDependencies goes (counting dots).

For that it needs fully qualified names such as App.Orders.Web. Rules need the opposite: short names for failure messages and freeze stores. Both requirements apply to the same string, so the choice has to be made when the slices are built, which is why there are two methods:

  • Matching produces short names and draws flat.
  • MatchingWithPackages produces prefix + name and draws nested.

The prefix is the literal pattern text before the capture group, so both methods group types the same way; only the names differ. In other words, a diagram option ends up in failure messages and frozen violations.

This PR keeps the diagram data on the slice instead of in its name. Each slice of a pattern carries, internally and outside its identity:

  • the namespace the pattern matched before the capture group (the same for all of its types, or none),
  • the path it captured.

The exporter builds the qualified names it lays the diagram out with from that data when it renders. So the flat vs. nested choice moves from slice creation to rendering, and one set of slices can be checked and drawn both ways.

This PR doesn't touch the matcher; it runs on main's. Moving the decision here first lets the next two PRs change naming and matching without a temporary answer for diagrams.

What changes

  • GenerationOptions.NestSlicesInPackages (default false) nests slices in package blocks. Focus mode (WithDependenciesFromFocusOn and the fluent WithDependenciesFromSlices(slices, focusOnPackage)) takes an optional GenerationOptions for it; the package to focus on is fully qualified exactly when nesting is on.

  • MatchingWithPackages is [Obsolete] and creates exactly Matching's slices, so rule descriptions, slices, names, violations and freeze keys are the same as with Matching. Its slices still nest by default, so existing diagram code keeps drawing the same diagrams.

  • Same diagrams as before. Except with a leading .. (see below), MatchingWithPackages(p) and Matching(p) with NestSlicesInPackages both produce byte-identical PlantUML to MatchingWithPackages(p) on main. The nested snapshots were renamed (BuildUmlBySlicesMatchingWithPackages_* → BuildUmlBySlicesNested_*) and switched to the option, and none of their contents changed. MatchingWithPackagesTests checks that both methods give the same slices, the same rule results and the same diagrams.

    pr2-1-same-nested-diagram
  • The root package comes from the match, not from the pattern text. With a leading .., MatchingWithPackages nested slices under the pattern text with one of its dots stripped. ..DuplicatePrefix.Sub.(**) thus got a package .DuplicatePrefix.Sub, which doesn't exist; it is now nested under the namespace its types are in:

    pr2-2-duplicate-prefix

    A slice whose types matched different namespaces before the capture group, which a leading .. allows, has no namespace to nest under. It is drawn on its own under its name, as are slices that don't come from a pattern (SlicedBy, custom SliceAssignments).

  • No two slices share a node. Two slices that would be drawn under the same name are rejected with an ArgumentException naming both, instead of being drawn as one node. Here that can happen when a slice that isn't nested has a description equal to the qualified name of a nested one.

  • Guide. Sections 5.2–5.6 use Matching with NestSlicesInPackages, and a new section 5.7 explains the migration. 5.6 also gets a missing semicolon.

Breaking changes

  • MatchingWithPackages is obsolete. It creates Matching's slices, so its failure messages and frozen violations no longer use names qualified with the namespace before the capture group. Frozen violations of MatchingWithPackages rules have to be frozen again once.
  • Slice.NameSpace, Slice.ContainsNamespace() and SliceIdentifier.NameSpace are removed, and SliceIdentifier.Of loses its nameSpace parameter.

Migration: replace MatchingWithPackages(pattern) with Matching(pattern) and draw the slices with new GenerationOptions { NestSlicesInPackages = true }.

How the images were made

Both images render PlantUML that the exporter produced on the SlicesTestAssembly fixture. The "before" side was produced at PR 1 (main plus the Namespace change, which doesn't touch slice diagrams), the "after" side at this PR. The first one shows the .verified.txt snapshots on either side. The "before" side of the second was produced with a throwaway test, because main has no snapshot for it. They were rendered with PlantUML 1.2025.4 and Graphviz, with the remote !include of C4-PlantUML replaced by PlantUML's bundled copy (!include <C4/C4_Container>).

@alexanderlinne
alexanderlinne added this pull request to stack #514 October 24, 2026 15:57
@codecov-commenter

codecov-commenter commented Oct 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.68345% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.56%. Comparing base (2d23189) to head (e8f8233).

Files with missing lines Patch % Lines
...tNET/Domain/PlantUml/Export/PlantUmlFileBuilder.cs 97.56% 1 Missing and 1 partial ⚠️
...ntUml/PlantUmlFluentComponentDiagramInitializer.cs 0.00% 2 Missing ⚠️
ArchUnitNET/Fluent/Slices/SliceAssignment.cs 92.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@                          Coverage Diff                           @@
##           stop-deriving-namespace-from-slice     #513      +/-   ##
======================================================================
+ Coverage                               86.49%   86.56%   +0.07%     
======================================================================
  Files                                     261      261              
  Lines                                   12675    12766      +91     
  Branches                                 1238     1245       +7     
======================================================================
+ Hits                                    10963    11051      +88     
- Misses                                   1366     1368       +2     
- Partials                                  346      347       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…ngWithPackages

MatchingWithPackages existed because a slice carried nothing but its
name: the exporter worked out the nesting from that string, so it had to
be fully qualified, while rules needed short names. The choice between
the two had to be made when the slices were created, and the qualified
names ended up in failure messages and frozen-rule stores as well.

Both methods group types the same way: the namespace before the capture
group is literal pattern text, so prefixing every name with it changes
no grouping. Keep that diagram data on the slice instead of in its name.
Each slice of a pattern carries, internally and outside its identity,
the namespace the pattern matched before the capture group and the path
it captured; SliceAssignment.Apply gives each slice that namespace if
all of its types matched the same one.

GenerationOptions.NestSlicesInPackages (default false) makes the
exporter nest slices along that namespace and path, which it builds into
the qualified names it lays the diagram out with. Slices without diagram
data, or whose types matched different namespaces before the group, are
drawn on their own under their names. Two slices that would be drawn
under the same name are rejected with an ArgumentException instead of
being merged into one node. Focus mode takes the options too.

MatchingWithPackages is obsolete. It creates the same slices as
Matching, so its rules report the same names and violations. It only
still nests its slices in diagrams by default, so existing diagram code
keeps drawing the same diagrams: every former MatchingWithPackages
snapshot is unchanged with Matching and NestSlicesInPackages.

The root package now comes from the match rather than from the pattern
text. With a leading "..", MatchingWithPackages used the pattern text
with one of its dots stripped, so "..DuplicatePrefix.Sub.(**)" was
nested under a package ".DuplicatePrefix.Sub." instead of the namespace
the types are in.

The guide shows Matching with NestSlicesInPackages in its diagram
examples and gains a migration section.

Breaking changes:
- MatchingWithPackages is obsolete, and creates Matching's slices, so
  its failure messages and frozen violations no longer use names
  qualified with the namespace before the capture group.
- Slice.NameSpace, Slice.ContainsNamespace() and
  SliceIdentifier.NameSpace are removed, and SliceIdentifier.Of loses
  its nameSpace parameter.

Signed-off-by: Alexander Linne <alexander.linne@tngtech.com>
@alexanderlinne
alexanderlinne force-pushed the nest-slices-in-packages-option branch from d38ef64 to e8f8233 Compare October 24, 2026 20:08
@alexanderlinne
alexanderlinne marked this pull request as draft October 25, 2026 19:39

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants