# Update Example

## Overview

This document demonstrates the complete **Update flow** using the **UpdateShipping** feature from the EBusiness application. The flow starts with a PUT request to the controller and ends with event publishing and subscriber processing.

## Complete Flow Architecture

```
PUT Request → Controller → Service → PreBus Plugins → Command Handler → RESTClient → External System
                                                                                    ↓
Response ← Controller ← Service ← Command Handler ← RESTClient ← External System
```

### **Detailed Flow Breakdown**

```
1. PUT Request
   ↓
2. Controller (API Entry Point)
   ↓
3. Service Layer (Business Orchestration)
   ↓
4. PreBus Processing (Validation Pipeline)
   ├── UpdateShippingSequence (Plugin Registration)
   ├── UpdateShippingDataPacket (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_UpdateShipping.cs`

```csharp
[HttpPut]
[Route("UpdateShipping")]
[ProducesResponseType(typeof(BadRequestResult), 400)]
[ProducesResponseType(typeof(string), 200)]
public async Task<IActionResult> UpdateShipping([FromBody]UpdateShippingDto dto)
{
    return await RunService(200, dto, _processShippingService.UpdateShipping);
}
```

**What Happens:**

* **HTTP Method**: `PUT /api/Shipping/UpdateShipping`
* **Input**: `UpdateShippingDto` from request body
* **Action**: Calls the service layer to process the shipping update
* **Response**: HTTP 200 OK with success status

### 2. **Service Layer** - Business Orchestration

**File**: `ProcessShippingService_UpdateShipping.cs`

```csharp
public async Task<CommandResult> UpdateShipping(UpdateShippingDto dto)
{
    var packet = await ProcessBusinessRuleSequence<UpdateShippingDataPacket, UpdateShippingSequence, UpdateShippingDto, FlexAppContextBridge>(dto);

    if (packet.HasError)
    {
        return new CommandResult(Status.Failed, packet.Errors());
    }
    else
    {
        dto.SetGeneratedId(_pkGenerator.GenerateKey());
        UpdateShippingCommand cmd = new UpdateShippingCommand
        {
            Dto = dto,
        };

        await ProcessCommand(cmd);

        CommandResult cmdResult = new CommandResult(Status.Success);

        UpdateShippingResultModel outputResult = new UpdateShippingResultModel();
        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 `UpdateShippingCommand` with DTO
* **Command Processing**: Calls the command handler
* **Result**: Returns success status

### 2.1. **PreBus Business Rule Sequence** - Validation Pipeline

**File**: `UpdateShippingSequence.cs`

```csharp
public class UpdateShippingSequence : FlexiBusinessRuleSequenceBase<UpdateShippingDataPacket>
{
    public UpdateShippingSequence()
    {
        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**: `UpdateShippingDataPacket.cs`

```csharp
public partial class UpdateShippingDataPacket : FlexiFlowDataPacketWithDtoBridge<UpdateShippingDto, FlexAppContextBridge>
{
    protected readonly ILogger<UpdateShippingDataPacket> _logger;

    public UpdateShippingDataPacket(ILogger<UpdateShippingDataPacket> 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`

```csharp
public partial class IsValidShipping : FlexiBusinessRuleBase, IFlexiBusinessRule<UpdateShippingDataPacket>
{
    public override string Id { get; set; } = "3a1cd500a49d178e88f75ed6bccaed88";
    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(UpdateShippingDataPacket 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**: `UpdateShippingHandler.cs`

```csharp
public partial class UpdateShippingHandler : IUpdateShippingHandler
{
    protected string EventCondition = "";
    protected readonly ILogger<UpdateShippingHandler> _logger;
    protected readonly IFlexHost _flexHost;
    protected ShippingRESTClient _restClient;
    protected FlexAppContextBridge? _flexAppContext;

    public UpdateShippingHandler(ILogger<UpdateShippingHandler> logger, IFlexHost flexHost, ShippingRESTClient restClient)
    {
        _logger = logger;
        _flexHost = flexHost;
        _restClient = restClient;
    }

    public virtual async Task Execute(UpdateShippingCommand cmd, IFlexServiceBusContext serviceBusContext)
    {
        // Convert internal DTO to Request DTO
        UpdateShippingRequestDto requestParams = new UpdateShippingRequestDto
        {
            Id = cmd.Dto.Id,
            ShippingAddress = cmd.Dto.ShippingAddress,
            TrackingNumber = cmd.Dto.TrackingNumber,
            Status = cmd.Dto.Status.ToString(),
            TotalWeight = cmd.Dto.ShippingItems?.Sum(x => x.Weight) ?? 0,
            ShippingItems = cmd.Dto.ShippingItems?.Select(x => new ShippingItemRequestDto
            {
                ProductId = x.ProductId,
                Quantity = x.Qty,
                Weight = x.Weight,
                Volume = x.Volume
            }).ToList()
        };

        // Call external REST service
        var response = await _restClient.UpdateShipping(requestParams);

        // Process response
        if (response.IsSuccessStatusCode)
        {
            var responseContent = await response.Content.ReadAsStringAsync();
            var responseDto = JsonConvert.DeserializeObject<UpdateShippingResponseDto>(responseContent);
            
            _logger.LogDebug("Shipping updated successfully with external ID: {ExternalId}", responseDto?.Id);
            
            // Set success condition for event firing
            EventCondition = "CONDITION_ONSUCCESS";
        }
        else
        {
            _logger.LogError("Failed to update 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/UpdateShipping.cs`

```csharp
public virtual Shipping UpdateShipping(UpdateShippingCommand cmd)
{
    Guard.AgainstNull("Shipping model cannot be empty", cmd);

    this.Convert(cmd.Dto);
    this.LastModifiedBy = cmd.Dto.GetAppContext()?.UserId;

    //Map any other field not handled by Automapper config

    this.SetModified();

    //Set your appropriate SetModified for the inner object here

    return this;
}
```

**What Happens:**

* **Validation**: Guards against null commands
* **Data Mapping**: Converts DTO to domain model
* **Audit Fields**: Sets last modified by user
* **State Management**: Marks entity as modified
* **Child Objects**: Processes child object updates

### 5. **NServiceBus Handler** - Message Processing

**File**: `UpdateShippingNsbHandler.cs`

```csharp
public class UpdateShippingNsbHandler : NsbCommandHandler<UpdateShippingCommand>
{
    readonly ILogger<UpdateShippingNsbHandler> _logger;
    readonly IFlexHost _flexHost;
    readonly IUpdateShippingHandler _handler;

    public UpdateShippingNsbHandler(ILogger<UpdateShippingNsbHandler> logger, IFlexHost flexHost, IUpdateShippingHandler handler)
    {
        _logger = logger;
        _flexHost = flexHost;
        _handler = handler;
    }

    public override async Task Handle(UpdateShippingCommand message, IMessageHandlerContext context)
    {
        _logger.LogTrace($"Executing {nameof(UpdateShippingNsbHandler)}");

        await _handler.Execute(message, new NSbHandlerContextBridge(context));
    }
}
```

**What Happens:**

* **Message Reception**: Receives `UpdateShippingCommand` 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**: `ShippingUpdatedEvent`

```csharp
public class ShippingUpdatedEvent : 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**: `UpdateLogisticsOnUpdateShipping.cs`

```csharp
public partial class UpdateLogisticsOnUpdateShipping : IUpdateLogisticsOnUpdateShipping
{
    protected readonly ILogger<UpdateLogisticsOnUpdateShipping> _logger;
    protected string EventCondition = "";

    public UpdateLogisticsOnUpdateShipping(ILogger<UpdateLogisticsOnUpdateShipping> logger)
    {
        _logger = logger;
    }

    public virtual async Task Execute(ShippingUpdatedEvent @event, IFlexServiceBusContext serviceBusContext)
    {
        _flexAppContext = @event.AppContext;

        //TODO: Write your business logic here:
        // - Notify logistics of shipping changes
        // - Update tracking information
        // - Send update notifications
        // - Update analytics

        await this.Fire<UpdateLogisticsOnUpdateShipping>(EventCondition, serviceBusContext);
    }
}
```

**What Happens:**

* **Event Reception**: Receives `ShippingUpdatedEvent` from message bus
* **Side Effects**: Executes business logic (notifications, tracking updates, etc.)
* **Additional Events**: Can fire more events if needed

## Data Transfer Objects (DTOs)

### **Input DTO**: `UpdateShippingDto`

```csharp
public partial class UpdateShippingDto : DtoBridge 
{
    public string Id { get; set; }
    
    [StringLength(200)]
    public string ShippingAddress { get; set; }

    public string TrackingNumber { get; set; }
    
    public ShippingStatus Status { get; set; }
}
```

### **Command**: `UpdateShippingCommand`

```csharp
public class UpdateShippingCommand : FlexCommandBridge<UpdateShippingDto, FlexAppContextBridge>
{
    // Command data is automatically populated by FlexBase
}
```

## Key Differences from Insert Flow

### **Update-Specific Characteristics**

1. **Entity Lookup**: Must find existing entity before updating
2. **Null Check**: Validates entity exists before processing
3. **State Management**: Uses `SetModified()` instead of `SetAdded()`
4. **Audit Fields**: Updates `LastModifiedBy` instead of `CreatedBy`
5. **HTTP Method**: Uses `PUT` instead of `POST`
6. **Response Code**: Returns `200 OK` instead of `201 Created`

### **PreBus Validation Focus**

* **IsValidShipping**: Validates shipping exists and is in updatable state
* **Business Rules**: Ensures shipping can be modified (not delivered, not cancelled)
* **Data Integrity**: Validates update doesn't violate business constraints

## Flow Summary

### **Synchronous Flow (Immediate Response)**

1. **PUT Request** → Controller receives request
2. **Service Processing** → Business orchestration and PreBus validation
3. **PreBus Plugins** → Sequential validation of business rules
4. **Command Handler** → Entity lookup and data processing
5. **Domain Logic** → Business rules and state management
6. **Response** → HTTP 200 OK with success status

### **Asynchronous Flow (Event Processing)**

1. **Event Publishing** → ShippingUpdatedEvent published to message bus
2. **Subscriber Processing** → UpdateLogisticsOnUpdateShipping executes
3. **Side Effects** → Logistics notifications, tracking updates, analytics

## Key Benefits

* **Data Integrity**: Ensures entity exists before updating
* **Audit Trail**: Tracks who made changes and when
* **Business Rules**: Validates update is allowed
* **Event-Driven**: Notifies other systems of changes
* **Testable**: Each component can be tested independently
* **Maintainable**: Clear separation of concerns

***

**This UpdateShipping example demonstrates how FlexBase enables clean, maintainable, and scalable update operations with proper validation, audit trails, and event-driven architecture!** 🚀


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.flexbase.in/solution-structure/getting-started/features/rest-services/update-example.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
