Skip to main content

DimensionAttributeValueSet

Overview

The DimensionAttributeValueSet aggregate represents an immutable collection of financial dimension values that can be shared across multiple master data entities (Customer, Vendor, Item, etc.) as default dimensions. This aggregate is the cornerstone of the default dimensions feature, enabling consistent dimensional classification across all financial transactions while promoting reusability and data integrity.

Module: Financial Dimensions
Aggregate Root: DimensionAttributeValueSet
Child Entities: DimensionAttributeValueSetItem
Domain Services: DimensionAttributeValueSetDomainService


Business Purpose & Context

Problem Statement

In an ERP system, master data entities like Customers, Vendors, and Items often need to carry standard financial dimensions that should be automatically applied to all transactions involving those entities. Without a systematic approach, this leads to:

  • Data Duplication: Same dimension combinations stored multiple times
  • Inconsistency: Manual entry leads to variations in dimensional classification
  • Performance Issues: Repeated validation and storage of identical dimension sets
  • Maintenance Burden: Updates to dimensions require touching multiple entities

Solution Approach

The DimensionAttributeValueSet aggregate solves these problems through:

  • Hash-Based Uniqueness: Identical dimension combinations are automatically deduplicated
  • Immutable Design: Once created, sets cannot be modified, ensuring referential integrity
  • Reusability: Multiple entities can reference the same dimension set
  • Atomic Creation: All dimensions in a set are resolved and validated as a single transaction

Business Scenarios

  1. Customer Default Dimensions: A customer in the "SALES" department with cost center "CC_100" gets these dimensions automatically applied to all invoices
  2. Vendor Classification: A vendor classified with project "PROJ_ALPHA" and location "NYC" inherits these dimensions on all purchase transactions
  3. Item Categorization: Inventory items with default dimensions for tracking purposes (department, product line, etc.)

Aggregate Structure

DimensionAttributeValueSet (Aggregate Root)

Purpose: Manages an immutable collection of dimension attribute/value pairs with hash-based uniqueness.

public class DimensionAttributeValueSet : AggregateRootBase
{
public string HashCode { get; private set; }
public IReadOnlyCollection<DimensionAttributeValueSetItem> Items { get; }
public int GetDimensionCount() => _items.Count;
}

Key Characteristics:

  • Identity: Guid-based primary key
  • Uniqueness: SHA256 hash of sorted dimension pairs ensures no duplicates
  • Immutability: Cannot be modified after creation - create new set for changes
  • Atomicity: All items validated and created together or not at all

DimensionAttributeValueSetItem (Child Entity)

Purpose: Represents a single dimension attribute/value pair within the set.

public class DimensionAttributeValueSetItem
{
public Guid DimensionAttributeId { get; private set; }
public Guid DimensionAttributeValueId { get; private set; }
public string DisplayValue { get; private set; }
}

Key Characteristics:

  • Foreign Keys: References both DimensionAttribute and DimensionAttributeValue
  • Display Value: Cached for performance (avoids joins in read scenarios)
  • Immutable: Cannot be modified after creation

Business Rules & Invariants

Core Business Rules

Rule 1: Hash-Based Uniqueness Golden Rule

Statement: Dimension sets with identical content must share the same hash code and be reused.

Implementation:

// Hash generation based on sorted (AttributeId, ValueId) pairs
var canonicalString = CreateCanonicalString(sortedPairs);
var hashString = GenerateHash(canonicalString); // SHA256

Enforcement: Domain service validates before creation; repository enforces unique constraint on HashCode

Rule 2: No Duplicate Attributes

Statement: A single dimension attribute can appear only once within a dimension set.

Rationale: Each dimension attribute represents a unique classification axis - duplication would be meaningless.

Enforcement:

private void ValidateAggregateInvariants()
{
var duplicates = _items.GroupBy(item => item.DimensionAttributeId)
.Where(group => group.Count() > 1);

if (duplicates.Any())
throw new InvalidOperationException("Duplicate dimension attributes found");
}

Rule 3: Maximum Dimension Limit

Statement: A dimension set cannot exceed 20 dimensions to prevent performance degradation.

Rationale: Extremely large dimension sets can impact query performance and UI usability.

Enforcement: Validated in ValidateAggregateInvariants() method

Rule 4: All Referenced Values Must Be Valid

Statement: All dimension attribute values must exist, be active, and not be suspended.

Enforcement: Cross-aggregate validation performed by domain service before set creation

Rule 5: Immutability After Creation

Statement: Once created, dimension sets cannot be modified.

Rationale:

  • Enables safe reuse across multiple entities
  • Maintains referential integrity for historical data
  • Simplifies concurrency control

Implementation: No public methods for modification; create new set for changes

Validation Rules

Structural Validation

  • All GUIDs must be non-empty
  • Display values must be non-null and non-empty
  • At least one dimension pair required

Cross-Aggregate Validation

  • Dimension attributes must exist and be active
  • Dimension values must exist and not be suspended
  • Value must belong to the specified attribute

Business Logic Validation

  • No duplicate dimension attributes
  • Dimension count within maximum limit
  • Hash code uniqueness verification

State Management

Aggregate Lifecycle

State Transitions

  1. Requested: Client provides dimension segment inputs
  2. Validating: Structural and business rule validation
  3. Resolving: String values resolved to stable IDs
  4. Hash Calculation: Deterministic hash generated
  5. Existence Check: Repository queried for existing set
  6. Reused: Existing set found and returned
  7. Creating: New set created and persisted
  8. Created: New set successfully created

Error States

  • Validation Failed: Invalid input data or business rule violations
  • Resolution Failed: Dimension values not found or suspended
  • Creation Failed: Database constraints or technical errors

Domain Events

DimensionAttributeValueSetCreatedDomainEvent

Trigger: When a new dimension set is successfully created Payload:

public record DimensionAttributeValueSetCreatedDomainEvent(
Guid DimensionAttributeValueSetId,
string HashCode,
int DimensionCount,
DateTime CreatedAt);

Use Cases:

  • Analytics: Track dimension set creation patterns
  • Caching: Invalidate related caches
  • Audit: Record dimension set creation events
  • Integration: Notify other bounded contexts

Future Events (Planned)

  • DimensionAttributeValueSetReusedDomainEvent: Track reuse patterns
  • DimensionAttributeValueSetAssignedDomainEvent: Track assignments to entities
  • DimensionAttributeValueSetUnassignedDomainEvent: Track when references are removed

Key Methods & Operations

Factory Methods

CreateItem (Static Factory)

public static DimensionAttributeValueSetItem CreateItem(
Guid dimensionAttributeId,
Guid dimensionAttributeValueId,
string displayValue)

Purpose: Creates validated dimension set items with proper validation

Query Methods

ContainsDimension

public bool ContainsDimension(Guid dimensionAttributeId)

Purpose: Checks if a specific dimension attribute is present in the set

GetValueForDimension

public Guid? GetValueForDimension(Guid dimensionAttributeId)

Purpose: Retrieves the value ID for a specific dimension attribute

GetDisplayValueForDimension

public string? GetDisplayValueForDimension(Guid dimensionAttributeId)

Purpose: Retrieves the display value for a specific dimension attribute

Utility Methods

IsEquivalentTo

public bool IsEquivalentTo(DimensionAttributeValueSet other)

Purpose: Compares two dimension sets based on hash codes for deduplication logic

ToString (Override)

public override string ToString()

Purpose: Human-readable representation for logging and debugging


Integration Points

General Ledger Module

  • Journal Transactions: Inherits default dimensions from master data entities
  • Dimension Combinations: Dimension sets feed into dimension combination creation
  • Account Structure Resolution: Default dimensions must comply with account structure requirements

Accounts Receivable Module

  • Customer Master Data: Customers reference dimension sets via DefaultDimensionId
  • Invoice Processing: Customer invoices inherit default dimensions automatically
  • Payment Processing: Customer payments inherit dimensional classification

Accounts Payable Module

  • Vendor Master Data: Vendors reference dimension sets for default dimensions
  • Purchase Invoice Processing: Vendor invoices inherit vendor default dimensions
  • Payment Processing: Vendor payments inherit dimensional classification

Inventory Module

  • Item Master Data: Inventory items can have default dimensions for movement tracking
  • Movement Processing: Stock movements inherit item default dimensions
  • Costing: Default dimensions used in inventory costing and valuation

Performance Considerations

Hash Generation Performance

  • Algorithm: SHA256 provides good balance of speed and collision resistance
  • Caching: Hash codes are pre-calculated and stored, not computed on-demand
  • Sorting: Input pairs sorted once during creation for deterministic results

Query Performance

  • Primary Index: Unique index on HashCode for fast existence checks
  • Foreign Key Indexes: Indexes on DimensionAttributeId and DimensionAttributeValueId
  • Composite Queries: Repository methods optimized for batch operations

Memory Usage

  • Display Value Caching: Cached display values avoid expensive joins
  • Collection Size Limit: 20-dimension maximum prevents memory bloat
  • Immutable Collections: ReadOnly collections reduce memory overhead

Repository Patterns

  • Batch Loading: GetByIdsWithItemsAsync for loading multiple sets efficiently
  • Existence Checks: ExistsByHashCodeAsync without loading full entity
  • Lazy Loading: Items collection supports lazy loading for large sets

Testing Strategy

Unit Tests (Aggregate)

// Business Rule Testing
[Test] public void Constructor_WithDuplicateAttributes_ThrowsException()
[Test] public void Constructor_WithTooManyDimensions_ThrowsException()
[Test] public void ContainsDimension_WithExistingAttribute_ReturnsTrue()
[Test] public void IsEquivalentTo_WithSameHashCode_ReturnsTrue()

Unit Tests (Domain Service)

// Hash Generation Testing
[Test] public void GenerateHashCode_WithSameDimensions_ReturnsSameHash()
[Test] public void GenerateHashCode_WithDifferentOrder_ReturnsSameHash()
[Test] public void ValidateDimensionSet_WithDuplicates_ReturnsFailure()

Integration Tests

// Cross-Aggregate Testing
[Test] public async Task GetOrCreate_WithValidDimensions_CreatesOrReusesSet()
[Test] public async Task GetOrCreate_WithInactiveDimension_ThrowsException()
[Test] public async Task GetOrCreate_WithSuspendedValue_ThrowsException()

Performance Tests

// Load Testing
[Test] public void HashGeneration_With1000Sets_CompletesUnder100ms()
[Test] public void SetCreation_WithMaxDimensions_CompletesUnder10ms()

Dependencies

Internal Dependencies

  • DimensionAttribute Aggregate: Validates dimension attributes exist and are active
  • DimensionAttributeValue Aggregate: Validates dimension values exist and are not suspended
  • SeedWork: Inherits from AggregateRootBase and uses domain event infrastructure

External Dependencies

  • None: Pure domain aggregate with no infrastructure dependencies

Referenced By

  • Customer Aggregate (Accounts Receivable): References via DefaultDimensionId
  • Vendor Aggregate (Accounts Payable): References via DefaultDimensionId
  • Item Aggregate (Inventory): References via DefaultDimensionId
  • DimensionCombination Aggregate (General Ledger): Inherits dimensions during transaction processing

Security Considerations

Access Control

  • Read Access: Financial dimension read permissions required
  • Create Access: Master data management permissions required
  • No Update/Delete: Immutable design eliminates update/delete security concerns

Data Integrity

  • Hash Collision Prevention: SHA256 algorithm provides cryptographic collision resistance
  • Referential Integrity: Foreign key constraints ensure referenced dimensions exist
  • Immutability: Once created, cannot be tampered with

Audit Trail

  • Creation Events: Domain events provide audit trail of set creation
  • Usage Tracking: Aggregate root tracking provides creation/update timestamps
  • Cross-Module Tracking: Referenced by master data entities provides usage audit

Troubleshooting Guide

Common Issues

Issue: "Duplicate dimension attributes found"

Cause: Client sent multiple segments with the same DimensionAttributeId
Solution: Validate input data; ensure each dimension attribute appears only once

Issue: "Dimension attribute not found"

Cause: Referenced dimension attribute doesn't exist or is not active
Solution: Verify dimension attribute exists and is active; check spelling of IDs

Issue: "Dimension value is suspended"

Cause: Referenced dimension value has been suspended
Solution: Use different value or reactivate the suspended value

Issue: "Hash code collision"

Cause: Extremely rare SHA256 collision
Solution: Log the incident; implement collision resolution strategy

Diagnostic Queries

-- Find dimension sets with most dimensions
SELECT Id, HashCode, (SELECT COUNT(*) FROM DimensionAttributeValueSetItems WHERE DimensionAttributeValueSetId = Id) as DimensionCount
FROM DimensionAttributeValueSets ORDER BY DimensionCount DESC;

-- Find most frequently reused dimension sets
SELECT d.Id, d.HashCode, COUNT(c.DefaultDimensionId) as UsageCount
FROM DimensionAttributeValueSets d
LEFT JOIN Customers c ON c.DefaultDimensionId = d.Id
GROUP BY d.Id, d.HashCode ORDER BY UsageCount DESC;

Last Updated: Phase 1, Week 2
Version: 1.0
Status: Complete Implementation