using System; using System.Collections.Generic; using System.Threading; namespace EventSourcing.Demo { public class WarehouseProductState { public int QuantityOnHand { get; set; } } public record Quantity { private int Value { get; } public Quantity(int value) { if (value <= 0) { throw new InvalidOperationException("Quantity must be greater than zero."); } Value = value; } public static implicit operator int(Quantity quantity) => quantity.Value; } public class WarehouseProduct : AggregateRoot { public string Sku { get; } private readonly WarehouseProductState _warehouseProductState = new(); public WarehouseProduct(string sku) { Sku = sku; } public override void Load(IEnumerable<IEvent> events) { foreach (var evnt in events) { Apply(evnt as dynamic); } } public static WarehouseProduct Load(string sku, IEnumerable<IEvent> events) { var warehouseProduct = new WarehouseProduct(sku); warehouseProduct.Load(events); return warehouseProduct; } public void ShipProduct(Quantity quantity) { // Business Rule if (quantity > _warehouseProductState.QuantityOnHand) { throw new InvalidDomainException("Cannot Ship to a negative Quantity on Hand."); } var productShipped = new ProductShipped(Sku, quantity, DateTime.UtcNow); Apply(productShipped); Add(productShipped); } private void Apply(ProductShipped evnt) { _warehouseProductState.QuantityOnHand -= evnt.Quantity; } public void ReceiveProduct(Quantity quantity) { var productReceived = new ProductReceived(Sku, quantity, DateTime.UtcNow); Apply(productReceived); Add(productReceived); } private void Apply(ProductReceived evnt) { _warehouseProductState.QuantityOnHand += evnt.Quantity; } public void AdjustInventory(int quantity, string reason) { if (_warehouseProductState.QuantityOnHand + quantity < 0) { throw new InvalidDomainException("Cannot adjust to a negative Quantity on Hand."); } var inventoryAdjusted = new InventoryAdjusted(Sku, quantity, reason, DateTime.UtcNow); Apply(inventoryAdjusted); Add(inventoryAdjusted); } private void Apply(InventoryAdjusted evnt) { _warehouseProductState.QuantityOnHand += evnt.Quantity; } public WarehouseProductState GetState() { return _warehouseProductState; } public int GetQuantityOnHand() { return _warehouseProductState.QuantityOnHand; } } public class InvalidDomainException : Exception { public InvalidDomainException(string message) : base(message) { } } }