Skip to content

Design Decisions ​

The brepjs repository tracks architectural decision records (ADRs) under docs/decisions/. Each ADR is a short markdown document explaining one decision: the context, the choice, the alternatives, and the consequences. This chapter is the index.

Why ADRs ​

Architecture decisions accumulate over a project's lifetime. Without records, future contributors (and future me) have to reverse-engineer the decision from the code, often missing the context that justified it. ADRs make the rationale durable: they survive when the original author forgets, leaves, or simply moves on.

The brepjs ADRs are intentionally short, typically a page or two each. Longer than a comment, shorter than a chapter.

The ADRs ​

IDTitleSummary
0001Layered architectureWhy imports flow downward only, what each layer contains
0002Kernel abstractionWhy brepjs has a KernelInterface rather than coupling to OpenCascade directly
0003Branded typesWhy Edge, Wire, Face are nominal not structural types
0004Phantom dimension typesWhy shapes carry a D extends '2D' | '3D' parameter
0005Topological validity typesWhy ClosedWire, OrientedFace, ManifoldShell, ValidSolid exist as separate types
0006Domain boundariesHow Layer 2 modules relate to each other (peer imports allowed)
0007Kernel interface segregationWhy KernelInterface is decomposed into smaller fragments
0008Layer 1 core auditAudit of what belongs in core/ vs higher layers
0009Tolerance as type parameterWhether tolerance should be encoded in the type system
0010Layer 2 domain auditAudit of what belongs in each Layer 2 module
0011Geometric validity brandsThe validity brand system (extending 0005 with concrete patterns)
0012Layer 3 auditAudit of what belongs in Layer 3 modules
0013Voxel domainScope and boundaries of the voxel/SDF domain
0014Identity beside contentWhy families identity lives beside the content-addressed IR, never inside it

The list grows as decisions accumulate. The full directory is the source of truth.

ADR template ​

When proposing a new architectural decision, copy docs/decisions/template.md and fill it in:

markdown
# ADR NNNN: Title

## Status

Proposed | Accepted | Superseded by NNNN | Deprecated

## Context

What's the situation that requires a decision? What are the constraints?

## Decision

What did we decide to do?

## Alternatives considered

What other options were on the table? Why weren't they chosen?

## Consequences

What does this enable? What does it constrain? What follow-up work does it imply?

Status starts at Proposed, transitions to Accepted when merged. Deprecation or supersession adds new ADRs that point back at the original.

When to write an ADR ​

Roughly: when a decision affects more than one module, when there's a real alternative someone might choose differently, when the rationale isn't obvious from the code. Examples:

  • Adding a new layer or moving a module between layers
  • Introducing a new kind of brand or type-level enforcement
  • Choosing between two approaches with different long-term consequences (e.g. "should tolerance be a type parameter or a runtime field?")
  • Defining a public API contract (e.g. "what's the canonical surface for query operations?")

Don't write an ADR for routine code changes: bug fixes, performance improvements, dependency bumps. The git history is enough for those.

Reading order ​

If you want to understand the brepjs architecture from first principles:

  1. 0001: Layered architecture: the foundation
  2. 0002: Kernel abstraction: why the kernel is replaceable
  3. 0003: Branded types: the type-system foundation
  4. 0004: Phantom dimension types: extends 0003 for 2D/3D safety
  5. 0005: Topological validity types + 0011: Geometric validity brands: the validity layer
  6. 0006: Domain boundaries + 0008, 0010: audits: what's in each module

The rest are deeper drills into specific subsystems.

Next steps ​

Released under the Apache 2.0 License.