This document demonstrates the complete Delete flow using the DeleteShipping feature from the EBusiness application. The flow starts with a DELETE request to the controller and ends with event publishing and subscriber processing.
Complete Flow Architecture
DELETE Request → Controller → Service → PreBus Plugins → Command Handler → RESTClient → External System
↓
Response ← Controller ← Service ← Command Handler ← RESTClient ← External System
Side Effects → Logistics updates, carrier notifications, analytics
Key Benefits
Data Safety: Soft delete preserves data for recovery
Business Rules: Validates deletion is allowed
Audit Trail: Tracks who deleted what and when
Event-Driven: Notifies other systems of deletions
Testable: Each component can be tested independently
Maintainable: Clear separation of concerns
This DeleteShipping example demonstrates how FlexBase enables clean, maintainable, and scalable delete operations with proper validation, soft delete patterns, and event-driven architecture! 🚀
public async Task<CommandResult> DeleteShipping(DeleteShippingDto dto)
{
var packet = await ProcessBusinessRuleSequence<DeleteShippingDataPacket, DeleteShippingSequence, DeleteShippingDto, FlexAppContextBridge>(dto);
if (packet.HasError)
{
return new CommandResult(Status.Failed, packet.Errors());
}
else
{
dto.SetGeneratedId(_pkGenerator.GenerateKey());
DeleteShippingCommand cmd = new DeleteShippingCommand
{
Dto = dto,
};
await ProcessCommand(cmd);
CommandResult cmdResult = new CommandResult(Status.Success);
DeleteShippingResultModel outputResult = new DeleteShippingResultModel();
cmdResult.result = outputResult;
return cmdResult;
}
}
public class DeleteShippingSequence : FlexiBusinessRuleSequenceBase<DeleteShippingDataPacket>
{
public DeleteShippingSequence()
{
this.Add<IsValidShipping>();
}
}
public partial class DeleteShippingDataPacket : FlexiFlowDataPacketWithDtoBridge<DeleteShippingDto, FlexAppContextBridge>
{
protected readonly ILogger<DeleteShippingDataPacket> _logger;
public DeleteShippingDataPacket(ILogger<DeleteShippingDataPacket> logger)
{
_logger = logger;
}
#region "Properties
//Models and other properties goes here
#endregion
}
public partial class IsValidShipping : FlexiBusinessRuleBase, IFlexiBusinessRule<DeleteShippingDataPacket>
{
public override string Id { get; set; } = "3a1cd5b1fa98b4c63378de9607706082";
public override string FriendlyName { get; set; } = "IsValidShipping";
protected readonly ILogger<IsValidShipping> _logger;
protected readonly RepoFactory _repoFactory;
public IsValidShipping(ILogger<IsValidShipping> logger, RepoFactory repoFactory)
{
_logger = logger;
_repoFactory = repoFactory;
}
public virtual async Task Validate(DeleteShippingDataPacket packet)
{
//Uncomment the below line if validating against a db data using your repo
//_repoFactory.Init(packet.Dto);
//If any validation fails, uncomment and use the below line of code to add error to the packet
//packet.AddError("key", "ErrorMessage");
await Task.CompletedTask; //If you have any await in the validation, remove this line
}
}
public partial class DeleteShippingHandler : IDeleteShippingHandler
{
protected string EventCondition = "";
protected readonly ILogger<DeleteShippingHandler> _logger;
protected readonly IFlexHost _flexHost;
protected ShippingRESTClient _restClient;
protected FlexAppContextBridge? _flexAppContext;
public DeleteShippingHandler(ILogger<DeleteShippingHandler> logger, IFlexHost flexHost, ShippingRESTClient restClient)
{
_logger = logger;
_flexHost = flexHost;
_restClient = restClient;
}
public virtual async Task Execute(DeleteShippingCommand cmd, IFlexServiceBusContext serviceBusContext)
{
// Convert internal DTO to Request DTO
DeleteShippingRequestDto requestParams = new DeleteShippingRequestDto
{
Id = cmd.Dto.Id,
DeletionReason = cmd.Dto.DeletionReason ?? "User requested deletion"
};
// Call external REST service
var response = await _restClient.DeleteShipping(requestParams);
// Process response
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseDto = JsonConvert.DeserializeObject<DeleteShippingResponseDto>(responseContent);
_logger.LogDebug("Shipping deleted successfully with external ID: {ExternalId}", responseDto?.Id);
// Set success condition for event firing
EventCondition = "CONDITION_ONSUCCESS";
}
else
{
_logger.LogError("Failed to delete shipping. Status: {StatusCode}", response.StatusCode);
EventCondition = "CONDITION_ONFAILED";
}
await this.Fire(EventCondition, serviceBusContext);
}
}
public virtual Shipping DeleteShipping(DeleteShippingCommand cmd)
{
Guard.AgainstNull("Shipping model cannot be empty", cmd);
this.Id = cmd.Dto.Id;
this.SetDeleted();
//Set your appropriate SetDeleted for the inner object here
return this;
}
public class DeleteShippingNsbHandler : NsbCommandHandler<DeleteShippingCommand>
{
readonly ILogger<DeleteShippingNsbHandler> _logger;
readonly IFlexHost _flexHost;
readonly IDeleteShippingHandler _handler;
public DeleteShippingNsbHandler(ILogger<DeleteShippingNsbHandler> logger, IFlexHost flexHost, IDeleteShippingHandler handler)
{
_logger = logger;
_flexHost = flexHost;
_handler = handler;
}
public override async Task Handle(DeleteShippingCommand message, IMessageHandlerContext context)
{
_logger.LogTrace($"Executing {nameof(DeleteShippingNsbHandler)}");
await _handler.Execute(message, new NsbHandlerContextBridge(context));
}
}
public class ShippingDeletedEvent : FlexEventBridge<FlexAppContextBridge>
{
// Event data is automatically populated by FlexBase
}
public partial class NotifyLogisticsOnShippingDeleted : INotifyLogisticsOnShippingDeleted
{
protected readonly ILogger<NotifyLogisticsOnShippingDeleted> _logger;
protected string EventCondition = "";
public NotifyLogisticsOnShippingDeleted(ILogger<NotifyLogisticsOnShippingDeleted> logger)
{
_logger = logger;
}
public virtual async Task Execute(ShippingDeletedEvent @event, IFlexServiceBusContext serviceBusContext)
{
_flexAppContext = @event.AppContext;
//TODO: Write your business logic here:
// - Update logistics systems
// - Notify carriers
// - Update analytics
// - Archive related data
await this.Fire<NotifyLogisticsOnShippingDeleted>(EventCondition, serviceBusContext);
}
}
public partial class DeleteShippingDto : DtoBridge
{
public string Id { get; set; }
}
public class DeleteShippingCommand : FlexCommandBridge<DeleteShippingDto, FlexAppContextBridge>
{
// Command data is automatically populated by FlexBase
}