Skip to content
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)

More tools from this site

  • claudestream Drive Claude Code from Python: run it as a subprocess and read its output as typed events, with async and sync sessions, sandbox policies, and tools you define in Python
  • claudewheel A TUI Claude Code Launcher that lets you have more than one profile, manage sessions lifecycle, pick the exact CC version, model to use (even older unlisted ones), pick which GitHub account to use, etc.
  • dirstat Fast, single-binary directory statistics CLI: every file under a tree grouped by format, with counts, sizes, and lines of code, as a colored terminal table or as JSON
  • fastware A batteries-included ASGI framework: msgspec JSON, a managed Granian server, dependency injection, SSE, WebSockets, auth, and a test client
  • go-toml-edit Zero-dep TOML editing library for Go with comment preservation
  • howmuchleft The fastest Claude Code statusline: context window, 5-hour, and weekly limit usage as three customizable gradient bars, rendering in about 6 ms
  • orxtra
  • pgdesign
  • predraw Declarative rendering pipeline: describe a scene in JSON and get SVG, PNG and WebP out, with light and dark style tokens, reusable components and text converted to path outlines
  • reposummary Turn a git repository's history into a Markdown journal: pick a time window or revision range and get a readable digest of what changed, optionally narrated by an LLM
  • safegit git wrapper CLI that gives each commit its own temporary index and retries ref updates on conflict, so concurrent agents share one repository
  • saferm Command-line replacement for rm that archives every deletion with a mandatory reason and the context it ran in, so deleted files can be listed, inspected and restored
  • selfdoc Static Site Generator that builds a project's documentation site directly from its source code, so the docs can never drift from the code they describe, with SEO/AEO, first-class blog, search, and cross-project linking built in
  • strictcli
  • stricttest An always-on test-isolation floor: a pytest plugin and a Go env-hygiene module that make a test suite structurally unable to reach real credentials, the real HOME, the network, or the development repository.
  • wesktop A Python framework that turns an ASGI web app into a desktop application, serving it from a local Granian server and displaying it in a native OS window via pywebview
Search