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
- Customer Default Dimensions: A customer in the "SALES" department with cost center "CC_100" gets these dimensions automatically applied to all invoices
- Vendor Classification: A vendor classified with project "PROJ_ALPHA" and location "NYC" inherits these dimensions on all purchase transactions
- 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
- Requested: Client provides dimension segment inputs
- Validating: Structural and business rule validation
- Resolving: String values resolved to stable IDs
- Hash Calculation: Deterministic hash generated
- Existence Check: Repository queried for existing set
- Reused: Existing set found and returned
- Creating: New set created and persisted
- 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 patternsDimensionAttributeValueSetAssignedDomainEvent: Track assignments to entitiesDimensionAttributeValueSetUnassignedDomainEvent: 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:
GetByIdsWithItemsAsyncfor loading multiple sets efficiently - Existence Checks:
ExistsByHashCodeAsyncwithout 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
AggregateRootBaseand 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