This document demonstrates the complete Soft Delete flow using the SoftDeleteShipping feature from the EBusiness application. The flow starts with a DELETE request to the controller and ends with event publishing and subscriber processing. Soft delete marks the entity as deleted without physically removing it from the database.
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
Query Filtering for Soft Deleted Items
Standard Query Filtering
Include Soft Deleted Items (Admin Queries)
Key Benefits
Data Safety: No accidental data loss
Compliance: Meets regulatory requirements
Audit Trail: Complete history of operations
Recovery: Can restore deleted data
Business Rules: Complex deletion logic support
Event-Driven: Notifies other systems of deletions
Testable: Each component can be tested independently
Maintainable: Clear separation of concerns
This SoftDeleteShipping example demonstrates how FlexBase enables clean, maintainable, and scalable soft delete operations with proper validation, data preservation, and event-driven architecture! 🚀
public async Task<CommandResult> SoftDeleteShipping(SoftDeleteShippingDto dto)
{
var packet = await ProcessBusinessRuleSequence<SoftDeleteShippingDataPacket, SoftDeleteShippingSequence, SoftDeleteShippingDto, FlexAppContextBridge>(dto);
if (packet.HasError)
{
return new CommandResult(Status.Failed, packet.Errors());
}
else
{
dto.SetGeneratedId(_pkGenerator.GenerateKey());
SoftDeleteShippingCommand cmd = new SoftDeleteShippingCommand
{
Dto = dto,
};
await ProcessCommand(cmd);
CommandResult cmdResult = new CommandResult(Status.Success);
SoftDeleteShippingResultModel outputResult = new SoftDeleteShippingResultModel();
cmdResult.result = outputResult;
return cmdResult;
}
}
public class SoftDeleteShippingSequence : FlexiBusinessRuleSequenceBase<SoftDeleteShippingDataPacket>
{
public SoftDeleteShippingSequence()
{
this.Add<IsValidShippingForSoftDelete>();
}
}
public partial class SoftDeleteShippingDataPacket : FlexiFlowDataPacketWithDtoBridge<SoftDeleteShippingDto, FlexAppContextBridge>
{
protected readonly ILogger<SoftDeleteShippingDataPacket> _logger;
public SoftDeleteShippingDataPacket(ILogger<SoftDeleteShippingDataPacket> logger)
{
_logger = logger;
}
#region "Properties
//Models and other properties goes here
#endregion
}
public partial class IsValidShippingForSoftDelete : FlexiBusinessRuleBase, IFlexiBusinessRule<SoftDeleteShippingDataPacket>
{
public override string Id { get; set; } = "3a1cd5b1fa98b4c63378de9607706082";
public override string FriendlyName { get; set; } = "IsValidShippingForSoftDelete";
protected readonly ILogger<IsValidShippingForSoftDelete> _logger;
protected readonly RepoFactory _repoFactory;
public IsValidShippingForSoftDelete(ILogger<IsValidShippingForSoftDelete> logger, RepoFactory repoFactory)
{
_logger = logger;
_repoFactory = repoFactory;
}
public virtual async Task Validate(SoftDeleteShippingDataPacket 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 SoftDeleteShippingHandler : ISoftDeleteShippingHandler
{
protected string EventCondition = "";
protected readonly ILogger<SoftDeleteShippingHandler> _logger;
protected readonly IFlexHost _flexHost;
protected ShippingRESTClient _restClient;
protected FlexAppContextBridge? _flexAppContext;
public SoftDeleteShippingHandler(ILogger<SoftDeleteShippingHandler> logger, IFlexHost flexHost, ShippingRESTClient restClient)
{
_logger = logger;
_flexHost = flexHost;
_restClient = restClient;
}
public virtual async Task Execute(SoftDeleteShippingCommand cmd, IFlexServiceBusContext serviceBusContext)
{
// Convert internal DTO to Request DTO
SoftDeleteShippingRequestDto requestParams = new SoftDeleteShippingRequestDto
{
Id = cmd.Dto.Id,
DeletionReason = cmd.Dto.DeletionReason ?? "User requested soft deletion",
SoftDeleteFlag = true
};
// Call external REST service
var response = await _restClient.SoftDeleteShipping(requestParams);
// Process response
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseDto = JsonConvert.DeserializeObject<SoftDeleteShippingResponseDto>(responseContent);
_logger.LogDebug("Shipping soft deleted successfully with external ID: {ExternalId}", responseDto?.Id);
// Set success condition for event firing
EventCondition = "CONDITION_ONSUCCESS";
}
else
{
_logger.LogError("Failed to soft delete shipping. Status: {StatusCode}", response.StatusCode);
EventCondition = "CONDITION_ONFAILED";
}
await this.Fire(EventCondition, serviceBusContext);
}
}
public virtual Shipping SoftDeleteShipping(SoftDeleteShippingCommand cmd)
{
Guard.AgainstNull("Shipping model cannot be empty", cmd);
this.Id = cmd.Dto.Id;
this.SetSoftDeleted();
//Set your appropriate SetSoftDeleted for the inner object here
return this;
}
public class SoftDeleteShippingNsbHandler : NsbCommandHandler<SoftDeleteShippingCommand>
{
readonly ILogger<SoftDeleteShippingNsbHandler> _logger;
readonly IFlexHost _flexHost;
readonly ISoftDeleteShippingHandler _handler;
public SoftDeleteShippingNsbHandler(ILogger<SoftDeleteShippingNsbHandler> logger, IFlexHost flexHost, ISoftDeleteShippingHandler handler)
{
_logger = logger;
_flexHost = flexHost;
_handler = handler;
}
public override async Task Handle(SoftDeleteShippingCommand message, IMessageHandlerContext context)
{
_logger.LogTrace($"Executing {nameof(SoftDeleteShippingNsbHandler)}");
await _handler.Execute(message, new NsbHandlerContextBridge(context));
}
}
public class ShippingSoftDeletedEvent : FlexEventBridge<FlexAppContextBridge>
{
// Event data is automatically populated by FlexBase
}
public partial class NotifyLogisticsOnShippingSoftDeleted : INotifyLogisticsOnShippingSoftDeleted
{
protected readonly ILogger<NotifyLogisticsOnShippingSoftDeleted> _logger;
protected string EventCondition = "";
public NotifyLogisticsOnShippingSoftDeleted(ILogger<NotifyLogisticsOnShippingSoftDeleted> logger)
{
_logger = logger;
}
public virtual async Task Execute(ShippingSoftDeletedEvent @event, IFlexServiceBusContext serviceBusContext)
{
_flexAppContext = @event.AppContext;
//TODO: Write your business logic here:
// - Update logistics systems
// - Notify carriers of cancellation
// - Update analytics
// - Archive related data
await this.Fire<NotifyLogisticsOnShippingSoftDeleted>(EventCondition, serviceBusContext);
}
}
public partial class SoftDeleteShippingDto : DtoBridge
{
public string Id { get; set; }
// Optional: Reason for soft delete
public string DeletionReason { get; set; }
}
public class SoftDeleteShippingCommand : FlexCommandBridge<SoftDeleteShippingDto, FlexAppContextBridge>
{
// Command data is automatically populated by FlexBase
}
// In query handlers, always filter out soft deleted items
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
// Always exclude soft deleted items
query = query.Where(s => !s.IsSoftDeleted);
// Add other filtering logic here
return query;
}
// For admin queries that need to see soft deleted items
protected override IQueryable<T> Build<T>()
{
_repoFactory.Init(_params);
IQueryable<T> query = _repoFactory.GetRepo().FindAll<T>();
// Only include soft deleted items if specifically requested
if (_params.IncludeSoftDeleted == true)
{
// Don't filter out soft deleted items
}
else
{
query = query.Where(s => !s.IsSoftDeleted);
}
return query;
}