Skip to content

Appendix: Design Constraints

The constitution that governs Invariant's design.

Overview

These constraints are non-negotiable. They exist to keep the kernel focused, testable, and portable.

The rules

1. Correctness before convenience

Never produce silently wrong results. If there's doubt, block and explain.

2. Explicit over implicit

All constraints are declared in metadata, not inferred from patterns or conventions.

3. Construction-time validation

Entities validate their invariants at construction. There are no "invalid but tolerated" states.

4. In-memory determinism

The kernel must run entirely in-memory with fake ports. Every use case must be testable with:

  • Fake repositories
  • Fake clocks
  • Deterministic inputs

5. Ports isolate all I/O

All communication with external systems goes through defined port interfaces. The domain layer has zero dependencies.

6. Validation is data, not control flow

Validation produces structured results (issues, disclosures, remediations). It never throws exceptions for semantic violations.

Why these rules exist

Rule Protects against
Correctness first Silent data quality failures
Explicit constraints Hidden assumptions and magic
Construction validation Invalid state propagating through the system
In-memory determinism Untestable code, infrastructure coupling
Port isolation Direct database calls, hidden dependencies
Validation as data Exception-driven control flow, lost error context

Anti-patterns

Violating in-memory execution:

# BAD: Direct database access in domain
class Study:
    def get_datasets(self):
        return db.query("SELECT * FROM datasets")  # NO!

Violating port isolation:

# BAD: Use case with hidden dependency
class ValidateQueryUseCase:
    def execute(self):
        import pandas as pd  # NO!

Violating validation as data:

# BAD: Throwing for semantic issues
if not is_valid:
    raise InvalidQueryError()  # NO! Return ValidationResult instead