Zig 0.16 · v0.0.2 preview

Named layer policies

Layers assign project files to named architecture areas and express allowed dependencies between those areas. Ordered definitions make overlapping selectors deterministic.

A compiled example

const std = @import("std");
const archunit = @import("archunit");

test "service may depend only on domain" {
    var architecture = try archunit.projectLayers(std.testing.allocator, .{});
    defer architecture.deinit(std.testing.allocator);
    var domain_stage = try architecture.layer("domain");
    defer domain_stage.deinit();
    var with_domain = try domain_stage.definedBy(.{ .glob = "src/domain/**" });
    defer with_domain.deinit(std.testing.allocator);
    var service_stage = try with_domain.layer("service");
    defer service_stage.deinit();
    var with_service = try service_stage.definedBy(.{ .glob = "src/service/**" });
    defer with_service.deinit(std.testing.allocator);
    var policy_stage = try with_service.whereLayer("service");
    defer policy_stage.deinit();
    var rule = try policy_stage.mayOnlyDependOnLayers(&.{"domain"});
    defer rule.deinit(std.testing.allocator);
    try archunit.expectPasses(&rule, .init(.init(std.testing.allocator, std.testing.io)));
}

Compiled source: test/readme/layers.zig

Define layers in ownership order

Create a layered architecture, then add definitions with a stable name and path selectors. The first matching definition owns a file when selectors overlap. Exclusions apply before ownership assignment.

Layer names must be unique and policy references must name existing layers. Definitions and policies are validated before extraction, so configuration errors remain distinct from architecture violations.

Choose the dependency policy

PolicyMeaning
mayOnlyDependOnOutgoing dependencies may target only the named layers (and the same layer).
mayNotDependOnOutgoing dependencies must avoid the named layers.
mayOnlyBeDependedOnByIncoming dependencies may originate only in the named layers.
mayNotBeDependedOnByIncoming dependencies must avoid the named layers.

Strict ownership closes gaps

Enable strict assignment when every connected project file must belong to a named layer. Unassigned sources and targets become structured violations instead of silently living outside the architecture map. For isolated files, combine strict layers with a file callback or repository dogfood rule that names every top-level production folder.

Violation evidence

A layer dependency violation records the policy kind, source and target layer, source and target file, and the underlying dependency evidence. The formatter can therefore explain both the intended boundary and the concrete import that crossed it.

A practical default Let domain layers depend on a shared low-level layer, keep domain layers independent of one another, allow the testing adapter to read common results, and let only the public facade assemble all domains.
Next: Slices