Item Aggregate
Purpose
The Item aggregate serves as the central master data entity for the inventory management system, representing any product, material, component, or service that can be tracked, transacted, or referenced within the organization. It implements a composition-based architecture that provides unprecedented flexibility compared to traditional inheritance-based item hierarchies.
This aggregate is fundamentally responsible for maintaining item identity, classification, measurement specifications, and optional inventory tracking capabilities through behavior composition. The Item aggregate ensures that all transactions, assemblies, and inventory operations reference valid, properly configured items that conform to the organization's catalog standards.
The Item aggregate acts as the cornerstone of inventory operations, providing the foundational master data that enables accurate inventory tracking, transaction processing, assembly operations, and financial integration.
Business Rules & Invariants
Core Business Rules
-
Unique Item Identification: Each item must have a unique ItemNumber that serves as the primary business identifier, distinct from the system-generated ID. Item numbers are immutable after creation.
-
Composition-Based Behaviors: Items use composition instead of inheritance for capabilities. Behaviors (like StockableBehavior) can be added, removed, or modified independently from the core item entity.
-
Stockable vs Non-Stockable: Items without StockableBehavior are non-stockable (e.g., services, labor). Only items with StockableBehavior can participate in inventory transactions and have stock levels tracked.
-
Unit Class Flexibility: Items reference a UnitClass (e.g., "Weight", "Volume") rather than a specific UnitOfMeasure, allowing transactions to use any compatible unit within that class at runtime.
-
Category Classification: Items can optionally belong to a hierarchical category structure for organization, reporting, and search purposes. Category assignment can be changed or cleared at any time.
-
Tax Integration: Items can reference a TaxGroup for integration with finance module tax calculations on purchase and sales transactions.
Domain Invariants
- Item Number Required: ItemNumber cannot be null or empty
- Item Name Required: Name cannot be null or empty
- Behavior Uniqueness: An item can have at most one StockableBehavior instance
- No Self-Reference: Items cannot reference themselves in relationships
- Active Status Control: Soft delete via IsActive flag instead of hard delete
- Immutable Identifier: ItemNumber cannot be modified after creation
State Management
Item Lifecycle States
[Creation] → [Configuration] → [Active Use] → [Archive]
↓
[Behavior Management]
↓
[Stockable/Non-Stockable Transitions]
Behavior Lifecycle
[Item Created (No Behavior)] → [Add Stockable Behavior] → [Active Stockable Item]
↓
[Update Behavior Settings]
↓
[Remove Behavior] → [Non-Stockable Item]
Status Transitions
[Active] ←→ [Archived]
↓
[Used in Transactions] → [Cannot Delete]
Aggregate Components
1. Item (Aggregate Root)
Properties:
Core Identification:
ItemNumber: Unique business identifier (immutable, user-defined)Name: Display name for the itemDescription: Detailed description or specifications
Classification:
CategoryId: Optional foreign key to Category aggregateCategory: Navigation property to hierarchical category structure
Measurement:
DefaultUnitClassId: Optional foreign key to UnitClass aggregateDefaultUnitClass: Navigation property specifying the class of units (Weight, Volume, etc.)
Tax Integration:
TaxGroupId: Optional foreign key for tax calculation integration with finance module
Behavior Composition:
StockableBehavior: Optional owned entity enabling inventory tracking capabilities
Computed Properties:
IsStockable: Boolean indicator derived from presence of StockableBehavior
Base Properties (from AggregateRoot):
Id: System-generated unique identifierCreatedDate: Timestamp of creationModifiedDate: Timestamp of last modificationIsActive: Soft delete flag
Key Behaviors:
Create(): Static factory method creating new item with validationUpdateBaseInfo(): Modifies name and description with validationUpdateCategory(): Assigns or clears category relationshipUpdateDefaultUnitClass(): Changes unit class specificationUpdateTaxGroup(): Modifies tax group assignmentAddStockableBehavior(): Adds inventory tracking capabilityRemoveStockableBehavior(): Removes inventory tracking capabilityUpdateStockableBehavior(): Modifies existing stockable behavior settingsAddOrUpdateBehavior(): Smart method that adds, updates, or removes behavior based on inputs
Validation Logic:
The Item aggregate enforces validation at multiple levels:
- Construction Validation: Factory method validates required fields
- Update Validation: Update methods validate state transitions
- Behavior Validation: Ensures behavior can be added/removed safely
- Business Rule Enforcement: Prevents invalid configurations
Central Master Data Role:
The Item serves as the single source of truth for:
- Product Catalog: Complete catalog of all items in the organization
- Transaction Reference: All inventory transactions reference items
- BOM Components: Items serve as both parent items and components in BOMs
- Inventory Tracking: Stockable items enable inventory record creation
- Financial Integration: Item tax groups drive GL posting and tax calculations
2. StockableBehavior (Owned Entity)
Purpose: Composition-based behavior that enables inventory tracking capabilities for an item. When present, the item can participate in inventory transactions and have stock levels maintained.
Properties:
ItemId: Foreign key reference to parent Item (aggregate root)AllowNegativeStock: Boolean controlling whether inventory can go negativeTrackByLocation: Boolean controlling whether location-specific tracking is requiredItem: Navigation property back to parent Item
Key Behaviors:
Create(): Static factory method for creating behaviorUpdateInventoryRules(): Modifies tracking settings
Business Significance:
This composition pattern provides critical flexibility:
- Dynamic Capabilities: Items can transition between stockable and non-stockable
- Configuration Control: Each item has independent inventory tracking rules
- Transaction Validation: Stockable behavior settings control transaction processing
- Inventory Impact: Only items with this behavior create inventory records
Inventory Tracking Rules:
-
AllowNegativeStock = false:
- Transactions that would result in negative inventory are rejected
- Used for items requiring strict physical inventory control
- Prevents over-commitment of inventory
-
AllowNegativeStock = true:
- Inventory can go negative (backorder scenarios)
- Used for high-velocity items or when backorders are acceptable
- Enables flexibility in transaction processing
-
TrackByLocation = true:
- Inventory maintained separately for each location
- All transactions must specify source/destination locations
- Enables detailed location-based inventory management
-
TrackByLocation = false:
- Inventory tracked at organization level only
- Location specification optional on transactions
- Simplified tracking for non-warehouse items
Domain Events
Item Lifecycle Events (Future Enhancement)
-
ItemCreatedDomainEvent: Raised when new item is created
- Contains ItemId, ItemNumber, Name for integration tracking
- Enables cache invalidation and search index updates
- Supports audit trail maintenance
-
ItemArchivedDomainEvent: Raised when item is archived
- Triggers impact analysis for existing transactions and BOMs
- Enables workflow automation for archive procedures
- Supports compliance and audit requirements
Behavior Management Events (Future Enhancement)
-
ItemStockableBehaviorAddedDomainEvent: Raised when stockable behavior is added
- Contains ItemId and behavior configuration
- Triggers initialization of inventory records
- Enables integration with warehouse management systems
-
ItemStockableBehaviorRemovedDomainEvent: Raised when stockable behavior is removed
- Requires validation that no inventory exists
- Triggers cleanup of inventory records
- Supports audit trail for significant configuration changes
-
ItemStockableBehaviorUpdatedDomainEvent: Raised when behavior settings change
- Contains old and new configuration for comparison
- Enables impact analysis for transaction validation
- Supports workflow automation for approval processes
Integration and Audit Events
These events enable:
- Compliance Tracking: Complete audit trail of item changes
- Impact Analysis: Understanding downstream effects of configuration changes
- Cache Management: Intelligent invalidation of performance optimizations
- Workflow Automation: Triggering approval processes for critical changes
- Search Index Updates: Keeping search systems synchronized
Repository Contract
IItemRepository
Core Query Methods:
GetByIdAsync(Guid id): Retrieves single item with all related dataGetAllAsync(): Retrieves all active items with related entitiesGetByItemNumberAsync(string itemNumber): Finds item by business identifierGetArchivedAsync(): Retrieves archived (inactive) items
Specialized Query Methods:
GetStockableItemsAsync(): Returns only items with StockableBehaviorGetNonStockableItemsAsync(): Returns items without StockableBehaviorGetByCategoryAsync(Guid categoryId): Finds all items in specific categoryGetByUnitClassAsync(Guid unitClassId): Finds items using specific unit classGetByTaxGroupAsync(Guid taxGroupId): Finds items with specific tax group
Validation Methods:
ExistsByItemNumberAsync(string itemNumber): Checks if item number already existsIsItemNumberUniqueAsync(string itemNumber, Guid? excludeId): Validates uniqueness for updatesCanBeArchivedAsync(Guid itemId): Validates if item can be safely archived
Command Methods:
AddAsync(Item item): Persists new item with validationUpdate(Item item): Updates existing itemArchive(Item item): Soft delete (sets IsActive = false)UnArchive(Item item): Restores archived item
Performance Optimization:
- Eager Loading: Include related entities in single query
- Selective Loading: Load only required relationships based on use case
- Caching Support: Query patterns support caching strategies
- Indexed Queries: Optimized queries on ItemNumber and Category
Domain Services
IItemService
Purpose: Provides cross-aggregate coordination and complex validation logic involving items and related entities.
Key Methods:
Transaction Validation:
Task<bool> CanUseInTransactionAsync(Guid itemId, TransactionType transactionType)
- Validates item can be used in specific transaction type
- Checks stockable status for inventory transactions
- Verifies item is active and not archived
Unit Compatibility:
Task<IEnumerable<UnitOfMeasure>> GetCompatibleUnitsAsync(Guid itemId)
- Returns all units compatible with item's UnitClass
- Used for transaction unit dropdowns
- Validates unit compatibility for conversions
Behavior Conversion:
Task<Result> ConvertToStockableAsync(Guid itemId, bool allowNegativeStock, bool trackByLocation)
- Safely converts non-stockable item to stockable
- Validates prerequisites (no conflicting transactions)
- Initializes inventory records if needed
Task<Result> ConvertToNonStockableAsync(Guid itemId)
- Safely converts stockable item to non-stockable
- Validates no inventory exists
- Cleans up related inventory records
Business Logic Integration:
- Coordinates with Transaction aggregate for validation
- Coordinates with Inventory aggregate for record management
- Coordinates with BOM aggregate for component validation
Usage Patterns
Creating Basic Item
// 1. Create item with required properties
var item = Item.Create(
itemNumber: "WIDGET-100",
name: "Standard Widget",
description: "General purpose widget for assembly operations");
// 2. Optional: Assign category
item.UpdateCategory(widgetCategoryId);
// 3. Optional: Assign unit class
item.UpdateDefaultUnitClass(weightUnitClassId);
// 4. Optional: Assign tax group
item.UpdateTaxGroup(standardTaxGroupId);
// 5. Save via repository
await itemRepository.AddAsync(item);
await unitOfWork.CommitAsync();
Creating Stockable Item
// 1. Create base item
var item = Item.Create(
itemNumber: "RM-STEEL-001",
name: "Steel Plate 1/4 inch",
description: "Cold rolled steel plate, 1/4 inch thickness",
categoryId: rawMaterialsCategoryId,
defaultUnitClassId: weightUnitClassId,
taxGroupId: rawMaterialsTaxGroupId);
// 2. Add stockable behavior for inventory tracking
item.AddStockableBehavior(
allowNegativeStock: false, // Prevent negative inventory
trackByLocation: true); // Require location tracking
// 3. Persist
await itemRepository.AddAsync(item);
await unitOfWork.CommitAsync();
// At this point:
// - Item can be used in inventory transactions
// - Inventory records will be created for this item
// - All transactions must specify locations
// - Negative stock is prevented
Creating Non-Stockable Item (Service)
// Create service item without stockable behavior
var serviceItem = Item.Create(
itemNumber: "SRV-CONSULTING",
name: "Consulting Services",
description: "Professional consulting services - hourly rate",
categoryId: servicesCategoryId);
// No stockable behavior added
// Service items don't track inventory
await itemRepository.AddAsync(serviceItem);
await unitOfWork.CommitAsync();
// Service item can be:
// - Referenced in documents
// - Used for billing
// - Tracked for revenue
// But will NOT:
// - Create inventory records
// - Participate in stock transactions
// - Have stock quantities
Modifying Stockable Behavior
// 1. Retrieve item
var item = await itemRepository.GetByIdAsync(itemId);
// 2. Update stockable behavior settings
if (item.IsStockable)
{
item.UpdateStockableBehavior(
allowNegativeStock: true, // Now allow backorders
trackByLocation: false); // Simplify to org-level tracking
}
else
{
// Convert to stockable
item.AddStockableBehavior(
allowNegativeStock: false,
trackByLocation: true);
}
// 3. Save changes
await unitOfWork.CommitAsync();
Using AddOrUpdateBehavior (Smart Method)
// Smart method handles all scenarios
item.AddOrUpdateBehavior(
allowNegativeStock: true,
trackByLocation: false);
// If behavior exists: updates settings
// If behavior doesn't exist: creates it
// If both params are null: removes behavior
Performance Considerations
Optimization Strategies
- Repository Patterns: Specialized queries for different use cases reduce over-fetching
- Eager Loading: Load related entities in single query to avoid N+1 problems
- Behavior Caching: StockableBehavior loaded with item to avoid separate queries
- Index Strategy: Clustered index on Id, non-clustered on ItemNumber for lookups
Scalability Metrics
- Item Lookup by Number: <5ms for indexed item number queries
- Category-Based Queries: <20ms for category-filtered item lists
- Stockable Item Queries: <10ms with filtered index on behavior presence
- Memory Footprint: ~2KB per item entity in memory
Database Optimization
- Primary Key: Clustered index on Id (Guid) for entity framework
- Item Number Index: Unique non-clustered index for business key lookups
- Category Index: Non-clustered index for category-based queries
- Foreign Keys: Indexed foreign keys for join performance
- Behavior Storage: Owned entity table with 1:1 relationship to items
Caching Strategies
- Item Catalog Cache: Cache entire item catalog for read-heavy scenarios
- Lookup Cache: Cache item number to ID mappings
- Category Cache: Cache items by category for navigation
- Invalidation: Cache invalidation on item updates via domain events (future)
Integration with Other Aggregates
Category Aggregate
- Hierarchical Classification: Items reference categories for organization
- Multi-Level Hierarchy: Categories support parent-child relationships
- Flexible Assignment: Category can be changed or cleared on items
- Reporting Integration: Category-based reports and analytics
UnitClass Aggregate
- Measurement Flexibility: Item references UnitClass, not specific units
- Transaction Compatibility: Transactions can use any unit in the class
- Conversion Support: UnitClass enables runtime unit conversions
- Display Preferences: Default unit class guides UI presentation
Transaction Aggregate
- Stockable Validation: Transactions validate item has StockableBehavior
- Negative Stock Control: Transaction processing checks AllowNegativeStock
- Location Requirements: TrackByLocation setting enforces location specification
- Item Reference: All transaction lines reference Item aggregate
Inventory Aggregate
- Record Creation: Only stockable items generate inventory records
- Location Granularity: TrackByLocation determines inventory record structure
- Quantity Updates: Inventory reacts to transaction domain events
- Stock Queries: Inventory lookups filter by stockable items
BOM Aggregate
- Parent Items: Items serve as parent items in BOMs (finished goods)
- Component Items: Items serve as components in BOMs (raw materials)
- Circular Reference Prevention: BOMs validate item isn't its own component
- Unit Compatibility: BOM component units must be compatible with item's unit class
Finance Module Integration
- Tax Calculation: Item TaxGroup drives tax calculations on purchase/sales
- GL Account Determination: Item properties help determine posting accounts
- Revenue Recognition: Item type influences revenue accounting
- Cost of Goods Sold: Stockable items impact COGS calculations
Design Patterns Applied
Domain-Driven Design Patterns
- Aggregate Pattern: Item maintains consistency boundary for item data
- Repository Pattern: Specialized data access with performance optimization
- Factory Method Pattern: Static Create() method enforces invariants
- Composition Pattern: Behaviors composed rather than inherited
- Domain Events Pattern: Future enhancement for change notification (planned)
Behavioral Patterns
- Strategy Pattern: StockableBehavior represents different inventory strategies
- Null Object Pattern: Missing StockableBehavior implies non-stockable strategy
- Template Method Pattern: Base item provides template, behaviors customize
Structural Patterns
- Composite Pattern: Used in Category relationships
- Owned Entity Pattern: StockableBehavior owned by Item aggregate
- Value Object Pattern: Planned for pricing, dimensions, identifiers
Security and Compliance Considerations
Access Control
- Item Creation: Restricted to authorized catalog management roles
- Behavior Changes: Stockable status changes require elevated privileges
- Archive Operations: Item archival controlled through authorized workflows
Audit and Compliance
- Complete Audit Trail: All item changes tracked via timestamps (future: domain events)
- Soft Delete: Archived items retained for historical reporting
- Change Tracking: ModifiedDate tracks last update timestamp
- Behavior History: StockableBehavior changes will be tracked via events (planned)
Data Integrity
- Referential Integrity: Foreign keys prevent orphaned relationships
- Constraint Validation: Comprehensive validation prevents inconsistent state
- Transaction Consistency: Unit of Work ensures atomic changes
- Uniqueness Enforcement: Unique index on ItemNumber prevents duplicates
Future Evolution
Planned Enhancements
- Domain Events: Full event-driven architecture for item changes
- Item Variants: Support size/color/style variants of base items
- Item Attributes: Flexible attribute system for specifications
- Pricing Integration: Built-in price lists and pricing tiers
- Supplier Relationships: Link items to preferred suppliers
- Digital Assets: Support for images, documents, specifications
- Serial/Lot Tracking: Enhanced StockableBehavior for serialized items
Extensibility Points
-
Additional Behaviors:
- PurchasableBehavior: Purchase-specific rules and lead times
- SellableBehavior: Sales-specific rules and pricing
- ManufacturableBehavior: Production-specific settings
- SerializableBehavior: Serial number tracking
-
Custom Validation: Domain service extensible for business-specific rules
-
Integration Events: Cross-module communication
- ItemCreated → Finance module
- ItemStockableStatusChanged → Warehouse module
- ItemArchived → All consuming modules
-
Reporting Extensions: Item-based reports and analytics
- Catalog reports
- Inventory valuation
- Movement analysis
- ABC classification
Architecture Decisions
Why Composition Over Inheritance?
Decision: Use composition (StockableBehavior) instead of inheritance (StockableItem : Item).
Rationale:
- Items need to transition between stockable and non-stockable states
- No rigid type hierarchy to manage
- Easy to add new behaviors without modifying base class
- Behaviors are self-contained and independently testable
- Aligns with modern DDD practices
Trade-offs:
- Slightly more complex queries (left join for behavior)
- Null checks required for optional behavior
- Benefits far outweigh minor complexity increase
Why UnitClass Instead of UnitOfMeasure?
Decision: Items reference UnitClass (e.g., "Weight") not specific UnitOfMeasure (e.g., "kg").
Rationale:
- Maximum flexibility at transaction time
- Different users/locations can use preferred units
- Supports international operations with different measurement systems
- Reduces item proliferation (one item, many transaction units)
Example:
- Item uses "Weight" class
- Transactions can use: kg, lb, g, oz, tons, etc.
- System handles conversions automatically
Why Soft Delete Instead of Hard Delete?
Decision: Use Archive/UnArchive (IsActive flag) instead of database delete.
Rationale:
- Preserve historical transaction references
- Audit trail requirements
- Enable restoration if archived by mistake
- Prevent referential integrity issues
- Support compliance and regulatory needs
Last Updated: 2025-10-24 | Status: Production | Version: 1.0