πŸ”„Flexbase Control Flow

🎯 Overview

This document explains the complete control flow for Flexbase features - from API call to execution completion. We'll cover one Insert (POST) and one Query (GET) example.


πŸ“ Insert Feature Flow: AddOrder

1. API Request

POST /api/Orders/AddOrder
Content-Type: application/json

{
  "customerId": "12345",
  "totalAmount": 299.99,
  "orderItems": [...]
}

2. Controller Layer

[HttpPost]
[Route("AddOrder")]
public async Task<IActionResult> AddOrder([FromBody] AddOrderDto dto)
{
    return await RunService(201, dto, _processOrdersService.AddOrder);
}

What happens: Controller receives request, validates DTO, calls service

3. Service Layer

public async Task<IActionResult> AddOrder(AddOrderDto dto)
{
    var command = new AddOrderCommand { Dto = dto };
    await _addOrderHandler.Execute(command, _serviceBusContext);
    return new OkResult();
}

What happens: Service creates command, calls handler

4. Command Handler

public async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
    // 1. Initialize repository
    _repoFactory.Init(cmd.Dto);
    
    // 2. Call domain method
    _model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
    
    // 3. Persist to database
    _repoFactory.GetRepo().InsertOrUpdate(_model);
    await _repoFactory.GetRepo().SaveAsync();
    
    // 4. Publish events
    await this.Fire(EventCondition, serviceBusContext);
}

What happens: Handler orchestrates the operation, calls domain, persists data, publishes events

5. Domain Method (Your Business Logic)

public virtual Order AddOrder(AddOrderCommand cmd)
{
    // Your business rules
    if (cmd.Dto.TotalAmount <= 0)
        throw new BusinessException("Invalid amount");
    
    // Apply business logic
    this.Convert(cmd.Dto);
    this.SetAdded(cmd.Dto.GetGeneratedId());
    this.CalculateTotal();
    
    return this;
}

What happens: Domain validates business rules, applies logic, returns entity

6. Database Persistence

_repoFactory.GetRepo().InsertOrUpdate(_model);
await _repoFactory.GetRepo().SaveAsync();

What happens: Entity Framework saves to database

7. Event Publishing

await this.Fire(EventCondition, serviceBusContext);

What happens: Domain events published to message bus for side effects

8. Response

HTTP 201 Created
Location: /api/Orders/{orderId}

πŸ” Query Feature Flow: GetOrders

1. API Request

GET /api/Orders/GetOrders?status=Pending

2. Controller Layer

[HttpGet]
[Route("GetOrders")]
public async Task<IActionResult> GetOrders([FromQuery] GetOrdersQuery query)
{
    return await RunService(200, query, _processOrdersService.GetOrders);
}

What happens: Controller receives request, calls service

3. Service Layer

public async Task<IActionResult> GetOrders(GetOrdersQuery query)
{
    var result = _getOrdersHandler.Fetch();
    return new OkObjectResult(result);
}

What happens: Service calls query handler

4. Query Handler

public override IEnumerable<GetOrdersDto> Fetch()
{
    return _repoFactory.GetRepo()
        .FindAll<Order>()
        .Where(o => o.Status == query.Status)
        .Include(o => o.Customer)
        .SelectTo<GetOrdersDto>()
        .ToList();
}

What happens: Handler executes optimized database query with projection

5. Database Query

-- Generated SQL (optimized)
SELECT 
    o.Id,
    c.Name AS CustomerName,
    o.TotalAmount,
    o.Status,
    o.OrderDate
FROM Orders o
LEFT JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Status = @Status

What happens: Only required fields fetched from database

6. Projection Mapping

// AutoMapper automatically maps to DTO
CreateMap<Order, GetOrdersDto>()
    .ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.Name));

What happens: Database results mapped to DTO

7. Response

HTTP 200 OK
[
  {
    "id": "12345",
    "customerName": "John Doe",
    "totalAmount": 299.99,
    "status": "Pending",
    "orderDate": "2024-01-15T10:30:00Z"
  }
]

πŸ”„ Complete Flow Summary

Insert Flow:

API Request β†’ Controller β†’ Service β†’ Command Handler β†’ Domain Method β†’ Database β†’ Events β†’ Response

Query Flow:

API Request β†’ Controller β†’ Service β†’ Query Handler β†’ Database Query β†’ Projection β†’ Response

🎯 Key Points

Insert Features:

  • Command Pattern - Encapsulates operation

  • Domain Logic - Business rules in domain layer

  • Event Publishing - Side effects handled asynchronously

  • Transaction Management - Automatic rollback on errors

Query Features:

  • Projection-Based - Only fetch required fields

  • Optimized SQL - Generated automatically

  • Type Safety - Compile-time validation

  • Performance - Database-level optimization

Common Benefits:

  • Separation of Concerns - Each layer has single responsibility

  • Testability - Each layer can be tested independently

  • Maintainability - Clear, readable code structure

  • Consistency - Same pattern across all features


This control flow ensures reliable, maintainable, and performant enterprise applications with Flexbase.

Last updated