Newer
Older
EventSourcing-ShipmentAggregate / EventSourced / ShipmentAggregateRoot.cs
@Derek Comartin Derek Comartin on 4 Aug 2021 4 KB Init
using System;
using System.Collections.Generic;
using System.Linq;

namespace AggregateConsistencyBoundary.EventSourced
{
    public class ShipmentAggregateRoot
    {
        private readonly ShipmentState _projection = new();

        public void Dispatch(int shipmentId, IEnumerable<ShipmentStop> stops)
        {
            var dispatched = new Dispatched(shipmentId, stops, DateTime.UtcNow);
            Apply(dispatched);
        }

        private void Apply(Dispatched dispatched)
        {
            _projection.Stops =  dispatched.Stops.OrderBy(x => x.Sequence).Select(x => new StopState(x.StopId, x.StopType, StopStatus.InTransit)).ToList();
            _projection.CurrentStopState = _projection.Stops.First();
        }

        public void Arrive(int stopId)
        {
            if (IsComplete())
            {
                throw new InvalidOperationException("Shipment is already complete.");
            }

            if (_projection.CurrentStopState.StopId != stopId)
            {
                throw new InvalidOperationException("Stop does not exist or is not the current stop.");
            }

            if (_projection.CurrentStopState.Status != StopStatus.InTransit)
            {
                throw new InvalidOperationException("Stop has already arrived.");
            }

            var arrived = new Arrived(stopId, DateTime.UtcNow);
            Apply(arrived);
        }

        private void Apply(Arrived arrived)
        {
            _projection.CurrentStopState.Status = StopStatus.Arrived;
        }

        public void Pickup(int stopId)
        {
            if (IsComplete())
            {
                throw new InvalidOperationException("Shipment is already complete.");
            }

            if (_projection.CurrentStopState.StopId != stopId)
            {
                throw new InvalidOperationException("Stop does not exist or is not the current stop.");
            }

            if (_projection.CurrentStopState.Type != StopType.Pickup)
            {
                throw new InvalidOperationException("Stop is not a pickup.");
            }

            if (_projection.CurrentStopState.Status == StopStatus.Departed)
            {
                throw new InvalidOperationException("Stop has already departed.");
            }

            if (_projection.CurrentStopState.Status == StopStatus.InTransit)
            {
                throw new InvalidOperationException("Stop hasn't arrived yet.");
            }

            var pickedUp = new PickedUp(stopId, DateTime.UtcNow);
            Apply(pickedUp);
        }

        private void Apply(PickedUp pickedUp)
        {
            _projection.CurrentStopState = _projection.Stops.SkipWhile(x => x.Status != StopStatus.InTransit).FirstOrDefault();
        }

        public void Deliver(int stopId)
        {
            if (IsComplete())
            {
                throw new InvalidOperationException("Shipment is already complete.");
            }

            if (_projection.CurrentStopState.StopId != stopId)
            {
                throw new InvalidOperationException("Stop does not exist or is not the current stop.");
            }

            if (_projection.CurrentStopState.Type != StopType.Delivery)
            {
                throw new InvalidOperationException("Stop is not a delivery.");
            }

            if (_projection.CurrentStopState.Status == StopStatus.Departed)
            {
                throw new InvalidOperationException("Stop has already departed.");
            }

            if (_projection.CurrentStopState.Status == StopStatus.InTransit)
            {
                throw new InvalidOperationException("Stop hasn't arrived yet.");
            }

            var delivered = new Delivered(stopId, DateTime.UtcNow);
            Apply(delivered);
        }

        private void Apply(Delivered delivered)
        {
            _projection.CurrentStopState = _projection.Stops.SkipWhile(x => x.Status != StopStatus.InTransit).FirstOrDefault();
        }

        public bool IsComplete()
        {
            return _projection.CurrentStopState == null;
        }
    }

    public record ShipmentStop(int StopId, StopType StopType, int Sequence);
}