Domain Value Object Design and Invariants

    1

    Designs DDD value objects: primitive obsession elimination, structural equality, immutability, and self-validation.

    $5

    Secure checkout via Stripe

    Money-back guarantee

    Converts to your local currency at checkout

    Security scanned

    Works with the AI Agents you already use

    Claude CodeClaude CodeCursorCursorCodex CLICodex CLIDeepSeekDeepSeekMuseMuse+22 more

    Domain Value Object Design and Invariants

    Example session with this skill installed

    Design DDD Value Object specification, immutability contracts, and arithmetic invariants for our Multi-Currency Money & Spread Types.

    • Subject: multi-currency-money-vo v2.0 (run-id: banking-vo-001) modeling monetary balances and interest margins across 12 banking applications.
    • Deciders: Lead Domain Architect (David O'Reilly) and Head of Financial Integrity (Elena Rostova).
    • Scope & Complexity: Multi-currency monetary amounts (ISO 4217); Banker's Rounding (HALF_EVEN); basis point interest spreads; peak 55,000 in-memory ops/sec.
    • Tension: Developers proposed using raw floating-point double primitives and plain BigDecimal variables directly in web controllers and entities; David O'Reilly and Elena Rostova strictly reject raw primitives, citing incident CALC-4914 where binary floating-point rounding errors accumulated into a $42,000 ledger discrepancy in interest accruals, while adding EUR to USD without conversion corrupted loan balances. They mandate an authoritative Value Object Contract: complete immutability, structural equality, strict currency matching invariants, side-effect-free methods, and zero primitive obsession.

    Write the value object specification under docs/.

    • Read your context and instructions
    • Compiled the domain value object

    Wrote docs/architecture/tasks/banking-vo-001/value-object-design/value-object-spec.md. Complete Domain Value Object specification establishing immutable Money and InterestRateSpread types, structural equality, Banker's Rounding, and zero primitive obsession.


    skill: value-object-design

    Domain Value Object Spec: Multi-Currency Money & Spread [VO-MONEY-001]

    Summary

    This specification establishes the Domain-Driven Design (DDD) Value Object architecture, immutability contracts, structural equality rules, and mathematical invariants for multi-currency-money-vo v2.0 under run ID banking-vo-001. It governs monetary amounts and basis-point interest rate spreads across 12 core banking microservices executing 55,000 in-memory arithmetic operations/second. It decisively resolves the financial rounding drift and cross-currency corruption demonstrated in incident CALC-4914 (where using raw binary floating-point double primitives caused a $42,000 ledger reconciliation error, and allowing addition of EUR to USD without explicit currency conversion corrupted commercial loan balances). The specification enforces a

    pure Value Object model: standardizing on

    immutable Java 21 Records (Money, CurrencyCode, BasisPoints), enforcing

    structural attribute equality, mandating

    Banker's Rounding (HALF_EVEN), validating

    strict currency matching invariants, providing

    side-effect-free arithmetic operations, and eliminating primitive obsession across all business services.

    Detailed Description

    Relying on raw primitive types (double, float, BigDecimal, String) for domain concepts is an anti-pattern known as Primitive Obsession. A raw double does not encapsulate currency, cannot enforce non-negative boundaries, and introduces binary floating-point rounding errors. A raw BigDecimal allows arithmetic between incompatible units (e.g. adding 100 USD to 100 JPY without conversion). In Domain-Driven Design, Value Objects model descriptive aspects of the domain that have no conceptual identity. They are defined entirely by their attributes, are strictly immutable, and guarantee that invalid domain values can never exist in memory.

    Incoming Monetary Operation (55,000 in-memory ops/sec)
                             │
                             ▼
    [ Constructor / Factory Gate: `Money.of(amount, currency)` ]
      ├── 1. Validates ISO-4217 Currency Code (e.g. `USD`, `EUR`, `JPY`)
      ├── 2. Enforces Decimal Minor Unit Scale (USD: 2 decimals, JPY: 0 decimals)
      └── 3. Enforces Banker's Rounding (RoundingMode.HALF_EVEN)
                             │
                             ▼ (Mints Pure Immutable Java 21 Record)
    [ Value Object: `Money` (Immutable, Structural Equality) ]
      ├── Attributes: `BigDecimal amount`, `CurrencyCode currency`
      ├── Side-Effect-Free Method: `moneyA.add(moneyB)`
      │      ├── Invariant Check: `assert moneyA.currency == moneyB.currency`
      │      └── Returns: NEW `Money` Instance (Zero In-Place Mutation)
      └── Equality: `moneyA.equals(moneyB)` (Evaluates Amount & Currency)
    

    Criteria and weights

    CriterionWhy it matters hereWeightSource of the weight
    Mathematical Ledger Precision (Zero Rounding Drift)Floating-point rounding errors in interest calculations cause ledger audits to fail (CALC-4914).0.40Elena Rostova (Head of Financial Integrity)
    Currency Type Safety & Invariant ProtectionCross-currency addition without conversion corrupts financial ledger balances.0.30David O'Reilly (Lead Domain Architect)
    Complete Immutability & Thread SafetyValue objects must be shareable across concurrent worker threads with zero locking.0.15Core Performance SLA
    In-Memory Execution Performance (55k ops/sec)Arithmetic methods must execute in < 0.001 ms without excessive GC heap allocation.0.15Banking Systems Architecture Standard

    Comparison

    Value RepresentationCurrency SafetyImmutability ModelRounding PredictabilityAllocation OverheadEvaluation
    Option A: Raw double PrimitivesZero (No currency)None (Mutable variable)Catastrophic (Binary IEEE 754 drift)ZeroRejected: Caused CALC-4914 $42,000 ledger error; illegal in banking.
    Option B: Naked BigDecimalLow (Currency unattached)Mutable via Scale methodsHigh (Configurable)ModerateRejected: Permits adding EUR to USD; lacks ubiquitous language.
    Option C: DDD Value Objects (Chosen)Absolute (Strict type matching)100% Immutable Java RecordAbsolute (Enforced Banker's Rounding)Negligible (Java 21 Value Types)Selected: 100% currency safe, mathematically exact, pure POJO.

    Result

    Option C is selected. Money and BasisPoints are implemented as pure, immutable Java 21 records; currency mismatch throws immediate domain exceptions; rounding defaults strictly to HALF_EVEN.


    Required Mechanisms

    1. Primitive Obsession Audit & Type Hierarchy [MC-PO-01]

    CurrencyCode: Value Object encapsulating a valid ISO 4217 three-letter currency string (USD, EUR, GBP, JPY).

    • Money: Value Object encapsulating BigDecimal amount and CurrencyCode currency:
      public record Money(BigDecimal amount, CurrencyCode currency) {
          public Money {
              Objects.requireNonNull(amount, "Amount must not be null");
              Objects.requireNonNull(currency, "Currency must not be null");
              amount = amount.setScale(currency.defaultFractionDigits(), RoundingMode.HALF_EVEN);
          }
      }
      
    • BasisPoints: Value Object modeling interest rate spreads:
      • Valid range: 0 to 10,000 basis points ($0.00% \text{ to } 100.00%$).
    2. Structural Equality & Immutability Contract [MC-EQ-01]
    • Value Equality: Two Money instances are equal if and only if their amount and currency code are identical:
      $$M_1 = M_2 \iff M_1.\text{amount} = M_2.\text{amount} \land M_1.\text{currency} = M_2.\text{currency}$$
    • Implemented natively via Java 21 record bytecode, guaranteeing standard equals(), hashCode(), and toString() contracts.
    • Strict Immutability: All record fields are private final. No mutator methods exist.
    3. Self-Contained Invariant Validation & Currency Guard [MC-IV-01]
    • The Currency Mismatch Invariant:
      public Money add(Money other) {
          if (!this.currency.equals(other.currency)) {
              throw new CurrencyMismatchException(
                  "Cannot add %s to %s without an exchange rate".formatted(other.currency, this.currency)
              );
          }
          return new Money(this.amount.add(other.amount), this.currency);
      }
      
    • Any attempt to add, subtract, or compare amounts with differing currencies throws CurrencyMismatchException before computation.
    4. Side-Effect-Free Arithmetic & Precision Rules [MC-AR-01]
    • Banker's Rounding: All division and multiplication operations enforce RoundingMode.HALF_EVEN:
      • Eliminates statistical rounding bias across millions of daily transaction ledger postings.
    • Arithmetic methods return a fresh Money instance; existing instances remain completely unaltered.

    Invariants and Contracts

    Complete Immutability Invariant [INV-VO-01]
      Value Objects must be strictly immutable. Modifying internal state after instantiation is prohibited.
      All arithmetic operations must return new Value Object instances.
    
    Strict Currency Matching Invariant [INV-VO-02]
      Binary operations between Money instances (addition, subtraction, comparison) must verify that both
      operands share the identical currency. Performing arithmetic across differing currencies is strictly barred.
    
    Mandatory Banker's Rounding Invariant [INV-VO-03]
      All fractional division and interest rate multiplication operations must apply `RoundingMode.HALF_EVEN`.
      Using truncation (`DOWN`) or asymmetric rounding (`UP`) in financial calculations is prohibited.
    

    Explicit Unknowns

    • Performance impact on JVM Garbage Collection escape analysis when allocating 55,000 ephemeral Money records per second under sustained load (G-1).
    • Jackson serialization configuration required to serialize Money as a flat JSON object {"amount": 100.50, "currency": "USD"} across external REST APIs (G-2).

    Traceability

    ClaimClassificationSourceFreshness
    12 core banking microservicesprovidedSystems architecture intakeCurrent
    Peak 55,000 in-memory ops/secprovidedVolumetric performance profileCurrent
    Incident CALC-4914 $42,000 ledger errorprovidedHistorical forensic auditHistorical
    Currency mismatch defect (EUR + USD)providedForensic incident post-mortemHistorical
    Pure Java 21 Record Value Objects selecteddecidedDavid O'Reilly & Elena Rostova2026-09-15
    Banker's Rounding (HALF_EVEN) standarddecidedArchitectural invariant INV-VO-032026-09-15

    Verification

    No validator was supplied, so no command was run.

    Reviewer self-check against Value Object design standards:

    • Immutability Rigor: PASS. Java 21 records guarantee 100% immutability and thread-safety.
    • Type Safety: PASS. Currency mismatch exceptions prevent cross-currency math (CALC-4914 eliminated).
    • Rounding Accuracy: PASS. Banker's Rounding (HALF_EVEN) eliminates statistical ledger drift.
    • Markdown Hygiene: PASS. Native Markdown syntax strictly adheres to rule_markdown.md.

    Open Decisions

    • DEC-VO-01: David O'Reilly to determine whether Project Valhalla value classes should be targeted once finalized in OpenJDK to achieve zero-allocation primitive stack performance (Owner: David O'Reilly).

    Next steps

    1. Core Engineering implements Money, CurrencyCode, and BasisPoints records in the shared domain foundation library.
    2. Platform team configures ArchUnit tests enforcing that no entity class uses raw double or float for financial balances.
    3. Conduct micro-benchmark suite verifying that 100,000 Money.add() operations execute in under 15 milliseconds.

    Connects securely to your tools. The creator never sees your data.

    About this skill

    What it does

    This skill specifies accepted domain concepts whose sameness is determined by semantic values rather than continuity. It defines equality components, canonicalization, units/precision, validity, replacement/composition and evidence without choosing code constructs.

    Use it when

    Use when authoritative language/scenarios establish a candidate value concept but equality, representation equivalence, valid states, units, replacement or composition semantics remain unresolved for a declared model decision.

    For example: “Our healthcare scheduling system displays appointment slot durations as '30 mins' in the UI, but database records show 1800 seconds and some external lab integrations pass 0.5 hours, causing double-booking overlaps.”

    What you get

    • Value Object Definitions

    Written as Markdown to <your output folder>/architecture/tasks/<run-id>/value-object-design/.

    What it will not do

    Do not use merely to discover entities, wrap primitives, refactor primitive obsession, design identifiers/DTOs/database types/aggregates, choose validators, generate immutable classes/records/factories/tests, or model API/schema formats.

    How it works

    1. Check value equality applies.
    2. Identify semantic components.
    3. Define equality and equivalence rules.
    4. Establish canonicalization and validity bounds.
    5. Specify replacement and composition semantics.
    6. Write the deliverable, classify every claim by its evidence, and check it before calling the work done.

    What's in the package

    Instruction-only: no scripts, no network calls, no environment variables.

    • LICENSE.txt
    • SKILL.md
    • agents/openai.yaml
    • assets/output-template-task.md
    • references/domain-rules.md
    • references/operating-rules.md
    • references/output-contract.md

    Find more skills like this on the Claude Skills Marketplace, the Codex Skills Marketplace and the Gemini Skills Marketplace, or browse Claude Skills for APIs & Backend.

    Do not use merely to discover entities, wrap primitives, refactor primitive obsession, design identifiers/DTOs/database types/aggregates, choose validators, generate immutable classes/records/factories/tests, or model API/schema formats.

    How to use

    1. 1

      Connect your agent to the Agensi MCP

      Copy the prompt and paste it into Claude, ChatGPT, Cursor or any agent that supports MCP. It sets up the connection for you. Manual setup

    2. 2

      Sign in to Agensi

      Your agent shows a sign-in link. Sign in and click Allow.

    3. 3

      Ask for what you need

      Your agent loads this skill and uses it on your task. It always asks before buying a skill.

    Reviews

    No reviews yet

    Be one of the first to try it. Every listed skill goes through the trust checks below.

    Security scanned

    Checked by our 8-point scan before listing

    Fresh listing

    Recently published to Agensi

    Money-back guarantee

    Doesn't do what its page promises? You get your money back.

    Trust & safety

    Security scan

    Security scan passed 1 month ago

    Permissions

    Read Files
    Write Files

    Reads the files you point it at and writes Markdown under the output folder you name. No network access, no environment variables, and no bundled scripts.

    Listed1 month ago

    What's inside

    Frequently Asked Questions

    No. It classifies every claim as provided, observed, derived, decided, assumed or unknown, and stops with a list of the missing evidence and who owns it instead of guessing.

    No. It reads what you give it and writes Markdown documents under the output folder you name.

    Any agent that supports SKILL.md skills, including Claude Code, Codex CLI, Cursor and Gemini CLI. It has no scripts and makes no network calls.

    Unzip it into your agent's skills folder, for example: unzip value-object-design.zip -d ~/.claude/skills/

    New in APIs & Backend