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