namespace BloatedHandler.Pipeline; public class Shipment { public Guid Id { get; private set; } public ShipmentStatus Status { get; private set; } public DateTime? DispatchedAt { get; private set; } public void Dispatch() { if (Status != ShipmentStatus.Ready) throw new InvalidOperationException("Shipment is not ready."); Status = ShipmentStatus.Dispatched; DispatchedAt = DateTime.UtcNow; } } public interface IShipmentDispatchStep { void Execute(DispatchShipmentContext context, NextStepDelegate next); } public delegate Task NextStepDelegate(DispatchShipmentContext context); public class ShipmentDispatchPipeline { private readonly List<IShipmentDispatchStep> _steps; public ShipmentDispatchPipeline(IEnumerable<IShipmentDispatchStep> steps) { _steps = steps.ToList(); } public async Task ExecuteAsync(Guid shipmentId) { } } public class DispatchShipmentHandler : ICommandHandler<DispatchShipment> { private readonly ShipmentDispatchPipeline _pipeline; public DispatchShipmentHandler(ShipmentDispatchPipeline pipeline) { _pipeline = pipeline; } public async Task Handle(DispatchShipment command) { await _pipeline.ExecuteAsync(command.ShipmentId); } } public class DispatchShipmentContext { public Guid ShipmentId { get; set; } public Shipment Shipment { get; set; } public List<object> Events { get; } = new(); } public class LoadShipmentStep : IShipmentDispatchStep { private readonly DB _db; public LoadShipmentStep(DB repository) { _db = repository; } public void Execute(DispatchShipmentContext context, NextStepDelegate next) { var shipment = _db.GetById(context.ShipmentId); if (shipment == null) { throw new InvalidOperationException("Shipment not found."); } context.Shipment = shipment; next(context); _db.Save(context.Shipment); } } public class DispatchShipmentLogicStep : IShipmentDispatchStep { public void Execute(DispatchShipmentContext context, NextStepDelegate next) { context.Shipment.Dispatch(); context.Events.Add(new ShipmentDispatched(context.Shipment.Id, context.Shipment.DispatchedAt!.Value)); next(context); } } public class PublishEventStep : IShipmentDispatchStep { private readonly IEventPublisher _eventPublisher; public PublishEventStep(IEventPublisher eventPublisher) { _eventPublisher = eventPublisher; } public void Execute(DispatchShipmentContext context, NextStepDelegate next) { foreach (var @event in context.Events) { _eventPublisher.Publish(@event); } next(context); } } public class ShipmentDispatchedCustomerEmail { private readonly IEmailer _emailer; private readonly DB _repository; public ShipmentDispatchedCustomerEmail(IEmailer emailer, DB repository) { _emailer = emailer; _repository = repository; } public void Handle(ShipmentDispatched evnt) { var shipment = _repository.GetById(evnt.ShipmentId); _emailer.NotifyCustomer(shipment); } } public class DispatchShipment { public DispatchShipment(Guid shipmentId) { ShipmentId = shipmentId; } public Guid ShipmentId { get; set; } } public class ShipmentDispatched { public Guid ShipmentId { get; } public object DispatchedAt { get; } public ShipmentDispatched(Guid shipmentId, object dispatchedAt) { ShipmentId = shipmentId; DispatchedAt = dispatchedAt; } } public enum ShipmentStatus { Ready, Dispatched } public interface ILogger { void Log(string s); } public interface IEventPublisher { void Publish(object shipmentDispatched); } public interface IEmailer { void NotifyCustomer(object shipment); } public interface DB { void Save(object shipment); Shipment GetById(object shipmentId); } public interface ICommandHandler<T> { }