Delete Example

Overview

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

Detailed Flow Breakdown

1. DELETE Request

2. Controller (API Entry Point)

3. Service Layer (Business Orchestration)

4. PreBus Processing (Validation Pipeline)
   ├── DeleteShippingSequence (Plugin Registration)
   ├── DeleteShippingDataPacket (Validation Context)
   └── IsValidShipping Plugin (Business Rules)

5. Command Handler (RESTClient Integration)
   ├── Internal DTO → Request DTO Mapping
   ├── RESTClient Call to External System
   └── Response DTO Processing

6. Event Publishing (Asynchronous Processing)

7. Subscribers (Side Effects)

Step-by-Step Implementation

1. API Controller - The Entry Point

File: ShippingController_DeleteShipping.cs

[HttpDelete()]
[Route("DeleteShipping/{id}")]
public async Task<IActionResult> DeleteShipping(string id)
{
    DeleteShippingDto dto = new DeleteShippingDto();
    dto.Id = id;
    return await RunService(200, dto, _processShippingService.DeleteShipping);
}

What Happens:

  • HTTP Method: DELETE /api/Shipping/DeleteShipping/{id}

  • Input: Shipping ID from URL parameter

  • DTO Creation: Creates DeleteShippingDto with the ID

  • Action: Calls the service layer to process the shipping deletion

  • Response: HTTP 200 OK with success status

2. Service Layer - Business Orchestration

File: ProcessShippingService_DeleteShipping.cs

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;
    }
}

What Happens:

  • PreBus Processing: Executes business rule sequences (plugins)

  • Validation: Processes business rule sequences

  • ID Generation: Generates unique key for tracking

  • Command Creation: Creates DeleteShippingCommand with DTO

  • Command Processing: Calls the command handler

  • Result: Returns success status

2.1. PreBus Business Rule Sequence - Validation Pipeline

File: DeleteShippingSequence.cs

public class DeleteShippingSequence : FlexiBusinessRuleSequenceBase<DeleteShippingDataPacket>
{
    public DeleteShippingSequence()
    {
        this.Add<IsValidShipping>(); 
    }
}

What Happens:

  • Plugin Registration: Registers validation plugins in execution order

  • Sequential Processing: Executes plugins one by one

  • Error Collection: Collects validation errors from all plugins

  • Early Exit: Stops processing if any plugin fails

2.2. PreBus Data Packet - Validation Context

File: DeleteShippingDataPacket.cs

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
}

What Happens:

  • Context Container: Holds DTO and application context

  • Error Collection: Collects validation errors from plugins

  • Data Sharing: Allows plugins to share data during validation

  • Logging: Provides logging capabilities for plugins

2.3. PreBus Validation Plugin - Business Rules

File: IsValidShipping.cs

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
    }
}

What Happens:

  • Business Rule Validation: Implements specific validation logic

  • Database Access: Can access repository for data validation

  • Error Reporting: Adds errors to the data packet if validation fails

  • Async Support: Supports asynchronous validation operations

  • Dependency Injection: Receives logger and repository factory

3. Command Handler - RESTClient Integration

File: DeleteShippingHandler.cs

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);
    }
}

What Happens:

  • DTO Mapping: Converts internal DTO to Request DTO for external system

  • RESTClient Call: Calls external shipping service via RESTClient

  • Response Processing: Handles success/failure responses from external system

  • Logging: Logs success/failure of external service call

  • Event Publishing: Fires events based on external service result

4. Domain Model - Business Logic

File: Shipping/DeleteShipping.cs

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;
}

What Happens:

  • Validation: Guards against null commands

  • ID Assignment: Sets the shipping ID from DTO

  • State Management: Marks entity as deleted (soft delete)

  • Child Objects: Processes child object deletions

5. NServiceBus Handler - Message Processing

File: DeleteShippingNsbHandler.cs

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));
    }
}

What Happens:

  • Message Reception: Receives DeleteShippingCommand from message bus

  • Logging: Logs handler execution

  • Delegation: Calls the actual command handler

  • Context Bridge: Converts NServiceBus context to FlexBase context

6. Event Publishing - Asynchronous Processing

Event: ShippingDeletedEvent (if implemented)

public class ShippingDeletedEvent : FlexEventBridge<FlexAppContextBridge>
{
    // Event data is automatically populated by FlexBase
}

What Happens:

  • Event Creation: FlexBase creates event with shipping data

  • Message Bus: Event is published to message bus

  • Subscriber Notification: All subscribers are notified

7. Event Subscribers - Side Effects

File: NotifyLogisticsOnShippingDeleted.cs (if implemented)

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);
    }
}

What Happens:

  • Event Reception: Receives ShippingDeletedEvent from message bus

  • Side Effects: Executes business logic (logistics updates, notifications, etc.)

  • Additional Events: Can fire more events if needed

Data Transfer Objects (DTOs)

Input DTO: DeleteShippingDto

public partial class DeleteShippingDto : DtoBridge 
{
    public string Id { get; set; }
}

Command: DeleteShippingCommand

public class DeleteShippingCommand : FlexCommandBridge<DeleteShippingDto, FlexAppContextBridge>
{
    // Command data is automatically populated by FlexBase
}

Key Differences from Insert/Update Flow

Delete-Specific Characteristics

  1. URL Parameter: ID comes from URL path instead of request body

  2. DTO Creation: Controller creates DTO with ID from URL

  3. Soft Delete: Uses SetDeleted() instead of SetAdded() or SetModified()

  4. State Management: Marks entity as deleted (soft delete pattern)

  5. HTTP Method: Uses DELETE instead of POST or PUT

  6. Minimal Data: Only requires ID for deletion

PreBus Validation Focus

  • IsValidShipping: Validates shipping exists and can be deleted

  • Business Rules: Ensures shipping can be removed (not delivered, not in transit)

  • Data Integrity: Validates deletion doesn't violate business constraints

Soft Delete Pattern

  • Database: Entity is marked as deleted, not physically removed

  • Queries: Soft-deleted entities are filtered out of normal queries

  • Recovery: Deleted entities can be restored if needed

  • Audit Trail: Maintains complete history of deletions

Flow Summary

Synchronous Flow (Immediate Response)

  1. DELETE Request → Controller receives request with ID

  2. Service Processing → Business orchestration and PreBus validation

  3. PreBus Plugins → Sequential validation of business rules

  4. Command Handler → Data processing and soft delete

  5. Domain Logic → Business rules and state management

  6. Response → HTTP 200 OK with success status

Asynchronous Flow (Event Processing)

  1. Event Publishing → ShippingDeletedEvent published to message bus

  2. Subscriber Processing → NotifyLogisticsOnShippingDeleted executes

  3. 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! 🚀

Last updated