rlsbl v0.92.0 /Layers
On this page

Monorepo architectural layer enforcement — configuring layer order, assignments, overrides, and validating dependency direction.

#Layers

#Overview

Architectural layer rules enforce dependency direction in monorepo workspaces. Layers define a vertical ordering of concerns — foundational libraries at the bottom, application code at the top. The rule is simple: higher layers can depend on lower layers, but lower layers cannot depend on higher layers.

This prevents architectural erosion where foundation code accumulates dependencies on application-specific logic, creating circular dependency chains that make independent releases impossible.

#Configuration

Layers are configured in the [layers] section of .rlsbl-monorepo/workspace.toml. The configuration defines the layer ordering, assigns projects to layers via glob patterns, and optionally declares override rules for cross-layer exceptions. All three subsections (order, assignments, overrides) are validated at check time.

#Structure

TM toml
[layers]
order = ["foundation", "specs", "contracts", "implementations", "flows", "app"]

[layers.assignments]
foundation = ["schema", "models", "infra"]
specs = ["*_spec"]
implementations = ["payments_*", "shipping_*"]
app = ["app"]

[layers.overrides]
unrestricted = ["conformance", "testing"]
forbidden_targets = ["legacy_*"]

[[layers.overrides.allow]]
source = "schema"
target = "app"

#Keys

Keys
KeyTypeRequiredDescription
orderarray of stringsYesLayer names from bottom (index 0) to top. Foundational layers first, application layers last.
[layers.assignments]tableYesMaps each layer name to a list of glob patterns matching project names.
[layers.overrides]tableNoException rules (see below).

#Order semantics

The order list defines the allowed dependency direction using a zero-indexed hierarchy where lower indices represent foundational layers and higher indices represent application layers. Dependencies flow downward (high to low) but never upward:

  • Index 0 is the bottom (most foundational)
  • Last index is the top (most application-specific)
  • A project at index N can depend on projects at index < N (downward)
  • A project at index N cannot depend on projects at index > N (upward = violation)
  • Projects at the same layer index can depend on each other freely

#Assignments

Each layer name in order must have a corresponding key in [layers.assignments]. The value is an array of glob patterns that match project names (as declared in workspace.toml name fields).

TM toml
[layers.assignments]
foundation = ["schema", "models", "infra"]
specs = ["*_spec"]
contracts = ["*_contract", "api_*"]
implementations = ["payments_*", "shipping_*", "auth"]
flows = ["checkout_flow", "onboarding_flow"]
app = ["app"]

#Glob pattern syntax

Patterns use Python's fnmatch module for matching project names against layer assignments. Since project names are flat strings without path separators, the patterns operate on simple name matching rather than filesystem path resolution:

Glob pattern syntax
PatternMeaning
*Matches any sequence of characters (including empty)
?Matches exactly one character
[seq]Matches any character in seq
[!seq]Matches any character not in seq

** is not supported as a recursive wildcard. fnmatch treats ** as two consecutive * wildcards, which behaves identically to a single * since project names are flat strings (no path separators).

Patterns are matched against project names only (the name field in workspace.toml), not file paths or directory names.

In layer assignments, first matching pattern wins. If a project name matches patterns in multiple layers, validate_layer_assignments() reports an error identifying the overlapping layers. Every project must be assigned to exactly one layer — both unassigned and multiply-assigned projects are validation failures.

#Overrides

The [layers.overrides] table provides three exception mechanisms for cases where strict layer ordering is insufficient. Overrides are evaluated during the layers-violations check and modify which dependency edges are considered violations versus allowed exceptions:

Overrides
OverrideTypeEffect
unrestrictedarray of glob patternsPackages matching these patterns are exempt from all layer checks as source. They can depend on anything regardless of layer position.
forbidden_targetsarray of glob patternsNothing may depend on packages matching these patterns. Any dependency edge targeting a forbidden package is a violation regardless of layer direction.
[[layers.overrides.allow]]array of tablesExplicit exceptions to layer rules. Each entry has source and target (project names, not globs).

#When to use each override

When to use each override
SituationOverride to use
Test infrastructure that imports from any layerunrestricted
Conformance suites that verify all layersunrestricted
Deprecated packages being phased outforbidden_targets
Legacy code that should not gain new dependentsforbidden_targets
A specific cross-layer dependency that is architecturally justified[[layers.overrides.allow]]

#Allow override example

TM toml
[[layers.overrides.allow]]
source = "schema"
target = "app"

This permits the schema project (a lower layer) to depend on app (a higher layer). Use sparingly — each allow override is a documented exception that weakens the architectural guarantee.

#Validation

validate_layer_assignments() runs as part of layer checking and enforces structural correctness of the layer configuration itself, catching misconfigurations before dependency direction is even evaluated. This validation step prevents confusing runtime errors by ensuring the layer definitions are internally consistent and cover all workspace projects. It checks 4 invariants:

  1. Every project in the workspace is assigned to exactly one layer (no gaps)
  2. No project matches patterns in multiple layers (no overlaps)
  3. Every layer name in order has a corresponding key in assignments
  4. Every key in assignments is listed in order

Violations produce clear error messages identifying which project is unassigned or multiply-assigned.

#Running the check

The layers-violations check is an untagged check that runs only with --all or --name. It walks the workspace dependency graph and reports every edge that violates the configured layer ordering, including the source and target project names and their respective layer positions:

$_ bash
# Run just the layers check
rlsbl check --name layers-violations

# Run all workspace checks (includes layers)
rlsbl check --tag workspace

The check calls check_layer_violations(), which:

  1. Loads layer config from workspace.toml
  2. Resolves each project's layer assignment via glob matching
  3. Walks the workspace dependency graph (declared depends-on in workspace.toml)
  4. For each dependency edge, checks whether it violates layer ordering
  5. Reports each violating edge with source project, target project, source layer, target layer, and direction

A non-empty list of violations causes the check to fail (exit code 1). The output lists each violation so all problems are visible in a single run.

#Decision table

Decision table
Source layer indexTarget layer indexOverrideResult
HigherLowerNoneAllowed (downward dependency)
LowerHigherNoneViolation (upward dependency)
SameSameNoneAllowed (same-layer)
AnyAnySource in unrestrictedAllowed
AnyForbidden targetNoneViolation
LowerHigher[[allow]] with matching source+targetAllowed (explicit exception)