Newer
Older
BusinessRules / WarehouseProduct.cs
@Derek Comartin Derek Comartin on 22 Aug 2023 3 KB Init
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)
        {

        }
    }
}