Insert Example
Overview
This document demonstrates the complete Insert flow using the AddToShipping feature from the EBusiness application. The flow starts with a POST request to the controller and ends with event publishing and subscriber processing.
Complete Flow Architecture
POST Request β Controller β Service β PreBus Plugins β Command Handler β RESTClient β External System
β
Response β Controller β Service β Command Handler β RESTClient β External System
Detailed Flow Breakdown
1. POST Request
β
2. Controller (API Entry Point)
β
3. Service Layer (Business Orchestration)
β
4. PreBus Processing (Validation Pipeline)
βββ AddToShippingSequence (Plugin Registration)
βββ AddToShippingDataPacket (Validation Context)
βββ ValidateShipping 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_AddToShipping.cs
[HttpPost]
[Route("AddToShipping")]
[ProducesResponseType(typeof(BadRequestResult), 400)]
[ProducesResponseType(typeof(string), 201)]
public async Task<IActionResult> AddToShipping([FromBody]AddToShippingDto dto)
{
return await RunService(201, dto, _processShippingService.AddToShipping);
}
What Happens:
HTTP Method:
POST /api/Shipping/AddToShipping
Input:
AddToShippingDto
from request bodyAction: Calls the service layer to process the shipping
Response: HTTP 201 Created with shipping ID
2. Service Layer - Business Orchestration
File: ProcessShippingService_AddToShipping.cs
public async Task<CommandResult> AddToShipping(AddToShippingDto dto)
{
var packet = await ProcessBusinessRuleSequence<AddToShippingDataPacket, AddToShippingSequence, AddToShippingDto, FlexAppContextBridge>(dto);
if (packet.HasError)
{
return new CommandResult(Status.Failed, packet.Errors());
}
else
{
dto.SetGeneratedId(_pkGenerator.GenerateKey());
AddToShippingCommand cmd = new AddToShippingCommand
{
Dto = dto,
};
await ProcessCommand(cmd);
CommandResult cmdResult = new CommandResult(Status.Success);
AddToShippingResultModel outputResult = new AddToShippingResultModel();
outputResult.Id = dto.GetGeneratedId();
cmdResult.result = outputResult;
return cmdResult;
}
}
What Happens:
PreBus Processing: Executes business rule sequences (plugins)
Validation: Processes business rule sequences
ID Generation: Generates unique shipping ID
Command Creation: Creates
AddToShippingCommand
with DTOCommand Processing: Calls the command handler
Result: Returns success with generated shipping ID
2.1. PreBus Business Rule Sequence - Validation Pipeline
File: AddToShippingSequence.cs
public class AddToShippingSequence : FlexiBusinessRuleSequenceBase<AddToShippingDataPacket>
{
public AddToShippingSequence()
{
this.Add<ValidateShipping>();
}
}
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: AddToShippingDataPacket.cs
public partial class AddToShippingDataPacket : FlexiFlowDataPacketWithDtoBridge<AddToShippingDto, FlexAppContextBridge>
{
protected readonly ILogger<AddToShippingDataPacket> _logger;
public AddToShippingDataPacket(ILogger<AddToShippingDataPacket> 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: ValidateShipping.cs
public partial class ValidateShipping : FlexiBusinessRuleBase, IFlexiBusinessRule<AddToShippingDataPacket>
{
public override string Id { get; set; } = "3a1cd0401f931dd4a01587e229b720dc";
public override string FriendlyName { get; set; } = "ValidateShipping";
protected readonly ILogger<ValidateShipping> _logger;
protected readonly RepoFactory _repoFactory;
public ValidateShipping(ILogger<ValidateShipping> logger, RepoFactory repoFactory)
{
_logger = logger;
_repoFactory = repoFactory;
}
public virtual async Task Validate(AddToShippingDataPacket 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
2.4. PreBus Plugin Benefits
Modular Validation: Each plugin handles one validation concern
Reusable Rules: Plugins can be reused across different features
Testable: Each plugin can be unit tested independently
Configurable: Plugins can be enabled/disabled per feature
Extensible: Easy to add new validation rules
Ordered Execution: Plugins execute in defined sequence
3. Command Handler - RESTClient Integration
File: AddToShippingHandler.cs
public partial class AddToShippingHandler : IAddToShippingHandler
{
protected string EventCondition = "";
protected readonly ILogger<AddToShippingHandler> _logger;
protected readonly IFlexHost _flexHost;
protected ShippingRESTClient _restClient;
protected FlexAppContextBridge? _flexAppContext;
public AddToShippingHandler(ILogger<AddToShippingHandler> logger, IFlexHost flexHost, ShippingRESTClient restClient)
{
_logger = logger;
_flexHost = flexHost;
_restClient = restClient;
}
public virtual async Task Execute(AddToShippingCommand cmd, IFlexServiceBusContext serviceBusContext)
{
// Convert internal DTO to Request DTO
AddToShippingRequestDto requestParams = new AddToShippingRequestDto
{
OrderId = cmd.Dto.OrderId,
ShippingAddress = cmd.Dto.ShippingAddress,
TrackingNumber = cmd.Dto.TrackingNumber,
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.AddToShipping(requestParams);
// Process response
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var responseDto = JsonConvert.DeserializeObject<AddToShippingResponseDto>(responseContent);
_logger.LogDebug("Shipping created successfully with external ID: {ExternalId}", responseDto?.Id);
// Set success condition for event firing
EventCondition = "CONDITION_ONSUCCESS";
}
else
{
_logger.LogError("Failed to create 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. NServiceBus Handler - Message Processing
File: AddToShippingNsbHandler.cs
public class AddToShippingNsbHandler : NsbCommandHandler<AddToShippingCommand>
{
readonly ILogger<AddToShippingNsbHandler> _logger;
readonly IFlexHost _flexHost;
readonly IAddToShippingHandler _handler;
public AddToShippingNsbHandler(ILogger<AddToShippingNsbHandler> logger, IFlexHost flexHost, IAddToShippingHandler handler)
{
_logger = logger;
_flexHost = flexHost;
_handler = handler;
}
public override async Task Handle(AddToShippingCommand message, IMessageHandlerContext context)
{
_logger.LogTrace($"Executing {nameof(AddToShippingNsbHandler)}");
await _handler.Execute(message, new NsbHandlerContextBridge(context));
}
}
What Happens:
Message Reception: Receives
AddToShippingCommand
from message busLogging: Logs handler execution
Delegation: Calls the actual command handler
Context Bridge: Converts NServiceBus context to FlexBase context
6. Event Publishing - Asynchronous Processing
Event: ShippingAddedEvent
public class ShippingAddedEvent : 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. RESTClient Integration - External Communication
File: ShippingRESTClient_AddToShipping.cs
public async Task<HttpResponseMessage> AddToShipping(AddToShippingRequestDto model)
{
var httpClient = _httpClientFactory.CreateClient(ShippingServicesRESTClientMaster.Name);
var serializedModel = JsonConvert.SerializeObject(model);
var request = new HttpRequestMessage(
HttpMethod.Post,
$"yourUri");
request.Content = new StringContent(serializedModel, Encoding.UTF8, "application/json");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return await response.HandleHttpResponse();
}
What Happens:
HTTP Client: Creates HTTP client for external communication
Serialization: Converts Request DTO to JSON
HTTP Request: Sends POST to external shipping system
Response Handling: Processes HTTP response from external system
7.1. Request DTO - External Contract
File: AddToShippingRequestDto.cs
public partial class AddToShippingRequestDto
{
public string OrderId { get; set; }
public string ShippingAddress { get; set; }
public string TrackingNumber { get; set; }
public decimal TotalWeight { get; set; }
public ICollection<ShippingItemRequestDto> ShippingItems { get; set; }
}
What Happens:
External Contract: Designed for external system consumption
Serialization: Optimized for JSON serialization
Validation: May include external system specific validation
7.2. Response DTO - External Contract
File: AddToShippingResponseDto.cs
public partial class AddToShippingResponseDto
{
public string Id { get; set; }
public string Status { get; set; }
public string Message { get; set; }
public DateTime ProcessedDate { get; set; }
public string ExternalReferenceId { get; set; }
}
What Happens:
External Contract: Designed for external system responses
Deserialization: Optimized for JSON deserialization
External Fields: May include external system specific fields
7.3. Mapping Configuration - DTO Transformation
File: ShippingRESTClientMapperProfile.cs
public class ShippingRESTClientMapperProfile : Profile
{
public ShippingRESTClientMapperProfile()
{
// Internal DTO β Request DTO Mapping
CreateMap<AddToShippingDto, AddToShippingRequestDto>()
.ForMember(d => d.TotalWeight, opt => opt.MapFrom(s => s.ShippingItems.Sum(x => x.Weight)))
.ForMember(d => d.ShippingItems, opt => opt.MapFrom(s => s.ShippingItems));
// Response DTO β Internal DTO Mapping
CreateMap<AddToShippingResponseDto, AddToShippingDto>()
.ForMember(d => d.Status, opt => opt.MapFrom(s => ParseStatus(s.Status)))
.ForMember(d => d.ExternalReferenceId, opt => opt.MapFrom(s => s.ExternalReferenceId));
}
private ShippingStatus ParseStatus(string status)
{
return status?.ToLower() switch
{
"created" => ShippingStatus.Created,
"in_transit" => ShippingStatus.InTransit,
"delivered" => ShippingStatus.Delivered,
"cancelled" => ShippingStatus.Cancelled,
_ => ShippingStatus.Unknown
};
}
}
What Happens:
Request Mapping: Converts internal DTO to external request format
Response Mapping: Converts external response to internal DTO format
Field Transformation: Handles different field names and types
Business Logic: Applies business rules during mapping
8. Event Subscribers - Side Effects
File: NotifyLogisticsOnShippingAdded.cs
public partial class NotifyLogisticsOnShippingAdded : INotifyLogisticsOnShippingAdded
{
protected readonly ILogger<NotifyLogisticsOnShippingAdded> _logger;
protected string EventCondition = "";
public NotifyLogisticsOnShippingAdded(ILogger<NotifyLogisticsOnShippingAdded> logger)
{
_logger = logger;
}
public virtual async Task Execute(ShippingAddedEvent @event, IFlexServiceBusContext serviceBusContext)
{
_flexAppContext = @event.AppContext;
//TODO: Write your business logic here:
// - Send shipping confirmation email
// - Update inventory levels
// - Notify logistics system
// - Update tracking information
await this.Fire<NotifyLogisticsOnShippingAdded>(EventCondition, serviceBusContext);
}
}
What Happens:
Event Reception: Receives
ShippingAddedEvent
from message busSide Effects: Executes business logic (emails, notifications, etc.)
Additional Events: Can fire more events if needed
Data Transfer Objects (DTOs)
Internal DTOs - Application Contracts
Input DTO: AddToShippingDto
AddToShippingDto
public partial class AddToShippingDto : DtoBridge
{
[StringLength(100)]
public string OrderId { get; set; }
[StringLength(200)]
public string ShippingAddress { get; set; }
public ICollection<AddToShippingDto_ShippingItem> ShippingItems { get; set; }
}
Command: AddToShippingCommand
AddToShippingCommand
public class AddToShippingCommand : FlexCommandBridge<AddToShippingDto, FlexAppContextBridge>
{
// Command data is automatically populated by FlexBase
}
External DTOs - External System Contracts
Request DTO: AddToShippingRequestDto
AddToShippingRequestDto
public partial class AddToShippingRequestDto
{
public string OrderId { get; set; }
public string ShippingAddress { get; set; }
public string TrackingNumber { get; set; }
public decimal TotalWeight { get; set; }
public ICollection<ShippingItemRequestDto> ShippingItems { get; set; }
}
Response DTO: AddToShippingResponseDto
AddToShippingResponseDto
public partial class AddToShippingResponseDto
{
public string Id { get; set; }
public string Status { get; set; }
public string Message { get; set; }
public DateTime ProcessedDate { get; set; }
public string ExternalReferenceId { get; set; }
}
DTO Mapping Relationships
Internal DTO (AddToShippingDto) ββ Request DTO (AddToShippingRequestDto)
β β
β External System
β β
βββ Response DTO (AddToShippingResponseDto) ββ
Flow Summary
Synchronous Flow (Immediate Response)
POST Request β Controller receives request
Service Processing β Business orchestration and PreBus validation
PreBus Plugins β Sequential validation of business rules
Command Handler β RESTClient integration
Internal DTO β Request DTO mapping
External system API call
Response DTO processing
Response β HTTP 201 with shipping ID
Asynchronous Flow (Event Processing)
Event Publishing β ShippingAddedEvent published to message bus
Subscriber Processing β NotifyLogisticsOnShippingAdded executes
Side Effects β Emails, notifications, inventory updates
Additional Events β Can trigger more business processes
External Communication Flow
Internal DTO β Request DTO mapping
RESTClient Call β HTTP request to external system
External Response β Response DTO from external system
Response Mapping β Response DTO to Internal DTO
Data Integration β External data integrated with internal data
Key Benefits
Separation of Concerns: Each layer has a single responsibility
Testability: Each component can be tested independently
Scalability: Asynchronous processing handles high loads
Maintainability: Clear, readable code structure
Event-Driven: Loose coupling between components
This AddToShipping example demonstrates how FlexBase enables clean, maintainable, and scalable insert operations with proper separation of concerns and event-driven architecture! π
Last updated