This document demonstrates the complete Insert flow using the AddOrder 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 → Domain Model → Database → Event Publishing → Subscribers
Side Effects → Emails, notifications, inventory updates
Additional Events → Can trigger more business processes
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 AddOrder example demonstrates how FlexBase enables clean, maintainable, and scalable insert operations with proper separation of concerns and event-driven architecture! 🚀
public async Task<CommandResult> AddOrder(AddOrderDto dto)
{
var packet = await ProcessBusinessRuleSequence<AddOrderDataPacket, AddOrderSequence, AddOrderDto, FlexAppContextBridge>(dto);
if (packet.HasError)
{
return new CommandResult(Status.Failed, packet.Errors());
}
else
{
dto.SetGeneratedId(_pkGenerator.GenerateKey());
AddOrderCommand cmd = new AddOrderCommand
{
Dto = dto,
};
await ProcessCommand(cmd);
CommandResult cmdResult = new CommandResult(Status.Success);
AddOrderResultModel outputResult = new AddOrderResultModel();
outputResult.Id = dto.GetGeneratedId();
cmdResult.result = outputResult;
return cmdResult;
}
}
public class AddOrderSequence : FlexiBusinessRuleSequenceBase<AddOrderDataPacket>
{
public AddOrderSequence()
{
this.Add<ValidateCustomer>();
}
}
public partial class AddOrderDataPacket : FlexiFlowDataPacketWithDtoBridge<AddOrderDto, FlexAppContextBridge>
{
protected readonly ILogger<AddOrderDataPacket> _logger;
public AddOrderDataPacket(ILogger<AddOrderDataPacket> logger)
{
_logger = logger;
}
#region "Properties
//Models and other properties goes here
#endregion
}
public partial class ValidateCustomer : FlexiBusinessRuleBase, IFlexiBusinessRule<AddOrderDataPacket>
{
public override string Id { get; set; } = "3a1cd0401f931dd4a01587e229b720dc";
public override string FriendlyName { get; set; } = "ValidateCustomer";
protected readonly ILogger<ValidateCustomer> _logger;
protected readonly RepoFactory _repoFactory;
public ValidateCustomer(ILogger<ValidateCustomer> logger, RepoFactory repoFactory)
{
_logger = logger;
_repoFactory = repoFactory;
}
public virtual async Task Validate(AddOrderDataPacket 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 virtual async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
_flexAppContext = cmd.Dto.GetAppContext();
_repoFactory.Init(cmd.Dto);
_model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
_repoFactory.GetRepo().InsertOrUpdate(_model);
int records = await _repoFactory.GetRepo().SaveAsync();
if (records > 0)
{
_logger.LogDebug("{Entity} with {EntityId} inserted into Database: ", typeof(Order).Name, _model.Id);
}
else
{
_logger.LogWarning("No records inserted for {Entity} with {EntityId}", typeof(Order).Name, _model.Id);
}
await this.Fire(EventCondition, serviceBusContext);
}
public virtual Order AddOrder(AddOrderCommand cmd)
{
Guard.AgainstNull("Order command cannot be empty", cmd);
this.Convert(cmd.Dto);
this.CreatedBy = cmd.Dto.GetAppContext()?.UserId;
this.LastModifiedBy = cmd.Dto.GetAppContext()?.UserId;
this.SetAdded(cmd.Dto.GetGeneratedId());
//Set your appropriate SetAdded for the inner object here
this.OrderState = new OrderIsCreated().SetTFlexId(this.Id).SetStateChangedBy("");
this.TotalAmount = this.OrderItems.Select(s => s.SellingPrice).Sum();
this.TotalQty = this.OrderItems.Select(s => s.Qty).Sum();
this.OrderItems.SetAddedOrModified();
return this;
}
public class AddOrderNsbHandler : NsbCommandHandler<AddOrderCommand>
{
readonly ILogger<AddOrderNsbHandler> _logger;
readonly IFlexHost _flexHost;
readonly IAddOrderHandler _handler;
public AddOrderNsbHandler(ILogger<AddOrderNsbHandler> logger, IFlexHost flexHost, IAddOrderHandler handler)
{
_logger = logger;
_flexHost = flexHost;
_handler = handler;
}
public override async Task Handle(AddOrderCommand message, IMessageHandlerContext context)
{
_logger.LogTrace($"Executing {nameof(AddOrderNsbHandler)}");
await _handler.Execute(message, new NsbHandlerContextBridge(context));
}
}
public class OrderAddedEvent : FlexEventBridge<FlexAppContextBridge>
{
// Event data is automatically populated by FlexBase
}
public partial class NotifyAccountsOnOrderAdded : INotifyAccountsOnOrderAdded
{
protected readonly ILogger<NotifyAccountsOnOrderAdded> _logger;
protected string EventCondition = "";
public NotifyAccountsOnOrderAdded(ILogger<NotifyAccountsOnOrderAdded> logger)
{
_logger = logger;
}
public virtual async Task Execute(OrderAddedEvent @event, IFlexServiceBusContext serviceBusContext)
{
_flexAppContext = @event.AppContext;
//TODO: Write your business logic here:
// - Send confirmation email
// - Update inventory
// - Notify accounting system
// - Update analytics
await this.Fire<NotifyAccountsOnOrderAdded>(EventCondition, serviceBusContext);
}
}
public partial class AddOrderDto : DtoBridge
{
[StringLength(100)]
public string CustomerId { get; set; }
public ICollection<AddOrderDto_OrderItem> OrderItems { get; set; }
}
public class AddOrderCommand : FlexCommandBridge<AddOrderDto, FlexAppContextBridge>
{
// Command data is automatically populated by FlexBase
}