ποΈFeatures and Modules
π― Understanding Modules and Features
Flexbase organizes enterprise applications into Modules and Features - a clear, business-focused approach that maps directly to your requirements and user interface actions.
π¦ What is a Module?
A Module represents a complete business domain or functional area of your application. Think of it as a self-contained unit that handles all operations related to a specific business concept.
Examples of Modules:
π Orders Module - Complete order management
π¦ Products Module - Product catalog and inventory
π³ Payments Module - Payment processing
π₯ Customers Module - Customer management
π Reports Module - Analytics and reporting
β‘ What is a Feature?
A Feature represents a single business action or operation within a module. Every button click, form submission, or user action in your application typically maps to one feature.
Feature Examples:
Insert (POST):
AddOrder,CreateProduct,RegisterCustomerQuery (GET):
GetOrders,SearchProducts,GetCustomerDetailsUpdate (PUT):
UpdateOrder,ModifyProduct,EditCustomerDelete (DELETE):
CancelOrder,RemoveProduct,DeactivateCustomer
π Business Requirements β Technical Mapping
The Mapping Process:
Identify Business Domains β Create Modules
Identify User Actions β Create Features
Define Data Requirements β Create DTOs
Specify Business Rules β Create Domain Logic
π Real-World Example: E-Commerce Application
Let's walk through a complete example of how business requirements translate to Flexbase modules and features.
Business Requirements:
"We need an e-commerce system where customers can browse products, place orders, and make payments. Administrators should be able to manage products and view order reports."
π Module 1: Orders Module
Business Domain: Order Management
Purpose: Handle all order-related operations
Features in Orders Module:
1. AddOrder Feature (POST)
Business Requirement: "Customer clicks 'Place Order' button"
Screen Action: Customer fills order form and clicks "Place Order"
Technical Implementation:
// Generated Controller Action
[HttpPost]
[Route("AddOrder")]
public async Task<IActionResult> AddOrder([FromBody] AddOrderDto dto)
{
return await RunService(201, dto, _processOrdersService.AddOrder);
}
// Generated DTO
public partial class AddOrderDto : DtoBridge
{
[Required]
public string CustomerId { get; set; }
[Required]
public List<OrderItemDto> OrderItems { get; set; }
[Range(0.01, double.MaxValue)]
public decimal TotalAmount { get; set; }
public string? Notes { get; set; }
}
// Generated Command Handler
public partial class AddOrderHandler : IAddOrderHandler
{
public virtual async Task Execute(AddOrderCommand cmd, IFlexServiceBusContext serviceBusContext)
{
_model = _flexHost.GetDomainModel<Order>().AddOrder(cmd);
_repoFactory.GetRepo().InsertOrUpdate(_model);
await _repoFactory.GetRepo().SaveAsync();
await this.Fire(EventCondition, serviceBusContext);
}
}
// Domain Logic (You Write This)
public partial class Order : DomainModelBridge
{
public virtual Order AddOrder(AddOrderCommand cmd)
{
// Business validation
if (cmd.Dto.TotalAmount <= 0)
throw new BusinessException("Order total must be greater than zero");
// Business rules
if (cmd.Dto.OrderItems.Count == 0)
throw new BusinessException("Order must contain at least one item");
// Apply business logic
this.Convert(cmd.Dto);
this.SetAdded(cmd.Dto.GetGeneratedId());
this.CalculateTotal();
this.SetOrderStatus("Pending");
return this;
}
}API Endpoint: POST /api/Orders/AddOrder
2. GetOrders Feature (GET)
Business Requirement: "Admin clicks 'View Orders' to see all orders"
Screen Action: Admin navigates to Orders page
Technical Implementation:
// Generated Controller Action
[HttpGet]
[Route("GetOrders")]
public async Task<IActionResult> GetOrders([FromQuery] GetOrdersQuery query)
{
return await RunService(200, query, _processOrdersService.GetOrders);
}
// Generated Query DTO
public partial class GetOrdersDto : DtoBridge
{
public Guid Id { get; set; }
public string CustomerId { get; set; }
public string CustomerName { get; set; }
public decimal TotalAmount { get; set; }
public string Status { get; set; }
public DateTime OrderDate { get; set; }
public int ItemCount { get; set; }
}
// Generated Query Handler
public class GetOrders : FlexiQueryEnumerableBridge<GetOrdersDto>
{
public override IEnumerable<GetOrdersDto> Fetch()
{
return _repoFactory.GetRepo()
.FindAll<Order>()
.Include(o => o.Customer)
.SelectTo<GetOrdersDto>()
.ToList();
}
}
// Custom Query with Filters (You Can Extend)
public class GetOrdersByStatus : FlexiQueryEnumerableBridge<GetOrdersDto>
{
public string Status { get; set; }
public override IEnumerable<GetOrdersDto> Fetch()
{
return _repoFactory.GetRepo()
.FindAll<Order>()
.Where(o => o.Status == Status)
.Include(o => o.Customer)
.SelectTo<GetOrdersDto>()
.ToList();
}
}π Database Optimization Magic - No Complex SQL Required!
The beauty of Flexbase queries is that you don't write complex SELECT statements! Here's what happens automatically:
1. Automatic Projection to Output Model
// You define what you want in the output DTO
public partial class GetOrdersDto : DtoBridge
{
public Guid Id { get; set; } // Only these fields
public string CustomerName { get; set; } // are fetched from DB
public decimal TotalAmount { get; set; } // automatically!
public string Status { get; set; }
public DateTime OrderDate { get; set; }
// Note: No complex SELECT statements needed!
}2. Database-Level Optimization
What Flexbase generates automatically:
-- Instead of: SELECT * FROM Orders (fetches ALL columns)
-- Flexbase generates: SELECT only the fields you need
SELECT
o.Id,
c.Name AS CustomerName, -- From joined Customer table
o.TotalAmount,
o.Status,
o.OrderDate
FROM Orders o
LEFT JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Status = @Status3. Performance Benefits
β Only Required Fields - Database fetches only what's in your DTO
β Automatic Joins - Include() statements become optimized SQL JOINs
β No N+1 Queries - Single query with proper joins
β Memory Efficient - No unnecessary data loaded into memory
β Network Optimized - Less data transferred over the network
4. Complex Projections Made Simple
// You can add calculated fields without complex SQL
public partial class GetOrdersDto : DtoBridge
{
public Guid Id { get; set; }
public string CustomerName { get; set; }
public decimal TotalAmount { get; set; }
public string Status { get; set; }
public DateTime OrderDate { get; set; }
// Calculated fields - Flexbase handles the SQL automatically
public int DaysSinceOrder { get; set; }
public string StatusDisplay { get; set; }
public bool IsHighValue { get; set; }
}
// AutoMapper configuration (generated automatically)
CreateMap<Order, GetOrdersDto>()
.ForMember(dest => dest.CustomerName, opt => opt.MapFrom(src => src.Customer.Name))
.ForMember(dest => dest.DaysSinceOrder, opt => opt.MapFrom(src => (DateTime.UtcNow - src.OrderDate).Days))
.ForMember(dest => dest.StatusDisplay, opt => opt.MapFrom(src => GetStatusDisplay(src.Status)))
.ForMember(dest => dest.IsHighValue, opt => opt.MapFrom(src => src.TotalAmount > 1000));5. What You DON'T Have to Write
-- β You DON'T write this complex SQL:
SELECT
o.Id,
c.Name AS CustomerName,
o.TotalAmount,
o.Status,
o.OrderDate,
CASE
WHEN o.Status = 'Pending' THEN 'Awaiting Processing'
WHEN o.Status = 'Processing' THEN 'Being Processed'
WHEN o.Status = 'Shipped' THEN 'On the Way'
ELSE 'Unknown'
END AS StatusDisplay,
DATEDIFF(day, o.OrderDate, GETDATE()) AS DaysSinceOrder,
CASE
WHEN o.TotalAmount > 1000 THEN 1
ELSE 0
END AS IsHighValue
FROM Orders o
LEFT JOIN Customers c ON o.CustomerId = c.Id
WHERE o.Status = @Status
ORDER BY o.OrderDate DESC6. What You DO Write (Simple and Clean)
// β
You write this simple, readable code:
public class GetOrdersByStatus : FlexiQueryEnumerableBridge<GetOrdersDto>
{
public string Status { get; set; }
public override IEnumerable<GetOrdersDto> Fetch()
{
return _repoFactory.GetRepo()
.FindAll<Order>()
.Where(o => o.Status == Status)
.Include(o => o.Customer)
.SelectTo<GetOrdersDto>() // Magic happens here!
.ToList();
}
}π― Key Benefits:
No SQL Knowledge Required - Write C# LINQ, get optimized SQL
Automatic Optimization - Only fetch what you need
Type Safety - Compile-time validation of all projections
Maintainable - Changes to DTO automatically update SQL
Performance - Database-level optimization without complexity
Readable - Business logic is clear and understandable
π‘ The Magic of SelectTo()
The SelectTo<GetOrdersDto>() method:
β Analyzes your DTO - Sees what fields you need
β Generates optimized SQL - Only selects required columns
β Handles joins automatically - Based on Include() statements
β Applies projections - Maps complex calculations
β Optimizes performance - Database-level efficiency
Result: You focus on business logic while Flexbase handles all the complex SQL optimization automatically!
API Endpoint: GET /api/Orders/GetOrders
π¦ Module 2: Products Module
Business Domain: Product Catalog Management
Purpose: Handle all product-related operations
Features in Products Module:
1. AddProduct Feature (POST)
Business Requirement: "Admin clicks 'Add New Product' button"
Screen Action: Admin fills product form and clicks "Save Product"
Technical Implementation:
// Generated Controller Action
[HttpPost]
[Route("AddProduct")]
public async Task<IActionResult> AddProduct([FromBody] AddProductDto dto)
{
return await RunService(201, dto, _processProductsService.AddProduct);
}
// Generated DTO
public partial class AddProductDto : DtoBridge
{
[Required]
[StringLength(100)]
public string Name { get; set; }
[Required]
[StringLength(500)]
public string Description { get; set; }
[Range(0.01, double.MaxValue)]
public decimal Price { get; set; }
[Range(0, int.MaxValue)]
public int StockQuantity { get; set; }
[Required]
public string Category { get; set; }
public string? ImageUrl { get; set; }
}
// Domain Logic (You Write This)
public partial class Product : DomainModelBridge
{
public virtual Product AddProduct(AddProductCommand cmd)
{
// Business validation
if (cmd.Dto.Price <= 0)
throw new BusinessException("Product price must be greater than zero");
if (cmd.Dto.StockQuantity < 0)
throw new BusinessException("Stock quantity cannot be negative");
// Apply business logic
this.Convert(cmd.Dto);
this.SetAdded(cmd.Dto.GetGeneratedId());
this.SetProductStatus("Active");
this.GenerateSku();
return this;
}
}API Endpoint: POST /api/Products/AddProduct
2. GetProducts Feature (GET)
Business Requirement: "Customer clicks 'Browse Products' to see available products"
Screen Action: Customer navigates to Products page
Technical Implementation:
// Generated Controller Action
[HttpGet]
[Route("GetProducts")]
public async Task<IActionResult> GetProducts([FromQuery] GetProductsQuery query)
{
return await RunService(200, query, _processProductsService.GetProducts);
}
// Generated Query DTO
public partial class GetProductsDto : DtoBridge
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int StockQuantity { get; set; }
public string Category { get; set; }
public string Status { get; set; }
public string Sku { get; set; }
public string? ImageUrl { get; set; }
}
// Generated Query Handler
public class GetProducts : FlexiQueryEnumerableBridge<GetProductsDto>
{
public override IEnumerable<GetProductsDto> Fetch()
{
return _repoFactory.GetRepo()
.FindAll<Product>()
.Where(p => p.Status == "Active")
.SelectTo<GetProductsDto>()
.ToList();
}
}
// Custom Search Query (You Can Extend)
public class SearchProducts : FlexiQueryEnumerableBridge<GetProductsDto>
{
public string SearchTerm { get; set; }
public string Category { get; set; }
public decimal? MinPrice { get; set; }
public decimal? MaxPrice { get; set; }
public override IEnumerable<GetProductsDto> Fetch()
{
var query = _repoFactory.GetRepo().FindAll<Product>()
.Where(p => p.Status == "Active");
if (!string.IsNullOrEmpty(SearchTerm))
query = query.Where(p => p.Name.Contains(SearchTerm) || p.Description.Contains(SearchTerm));
if (!string.IsNullOrEmpty(Category))
query = query.Where(p => p.Category == Category);
if (MinPrice.HasValue)
query = query.Where(p => p.Price >= MinPrice.Value);
if (MaxPrice.HasValue)
query = query.Where(p => p.Price <= MaxPrice.Value);
return query.SelectTo<GetProductsDto>().ToList();
}
}API Endpoint: GET /api/Products/GetProducts
π― Screen-to-Feature Mapping Examples
Example 1: Customer Order Screen
"Place Order" Button
Click
AddOrder
POST
/api/Orders/AddOrder
"View My Orders" Link
Click
GetCustomerOrders
GET
/api/Orders/GetCustomerOrders
"Order Details" Link
Click
GetOrderDetails
GET
/api/Orders/GetOrderDetails
"Cancel Order" Button
Click
CancelOrder
PUT
/api/Orders/CancelOrder
Example 2: Admin Product Management Screen
"Add Product" Button
Click
AddProduct
POST
/api/Products/AddProduct
"Edit Product" Button
Click
UpdateProduct
PUT
/api/Products/UpdateProduct
"Delete Product" Button
Click
DeleteProduct
DELETE
/api/Products/DeleteProduct
"Search Products" Input
Type + Enter
SearchProducts
GET
/api/Products/SearchProducts
"Filter by Category" Dropdown
Select
GetProductsByCategory
GET
/api/Products/GetProductsByCategory
Example 3: Customer Dashboard Screen
"View Profile" Link
Click
GetCustomerProfile
GET
/api/Customers/GetCustomerProfile
"Edit Profile" Button
Click
UpdateCustomerProfile
PUT
/api/Customers/UpdateCustomerProfile
"Order History" Tab
Click
GetOrderHistory
GET
/api/Orders/GetOrderHistory
"Wishlist" Tab
Click
GetWishlist
GET
/api/Products/GetWishlist
ποΈ Complete Module Structure
Typical Module Contains:
π¦ OrdersModule/
βββ π― Domain/
β βββ Order.cs (Domain Model)
β βββ AddOrder.cs (Business Logic)
β βββ Order_SeedData.cs (Test Data)
βββ π Controllers/
β βββ OrdersController.cs (REST API)
βββ π DTOs/
β βββ AddOrderDto.cs (Input)
β βββ GetOrdersDto.cs (Output)
β βββ OrderDto.cs (Domain)
βββ π§ Handlers/
β βββ AddOrderHandler.cs (Command)
β βββ GetOrdersHandler.cs (Query)
βββ π‘ Events/
β βββ OrderCreatedEvent.cs
β βββ OrderUpdatedEvent.cs
βββ π§ͺ Tests/
βββ AddOrderTests.cs
βββ GetOrdersTests.csπ― Feature Development Workflow
Step 1: Identify Business Requirement
"Customer needs to be able to place an order"
Step 2: Map to Screen Action
"Customer clicks 'Place Order' button on order form"
Step 3: Define Feature
"AddOrder feature in Orders module"
Step 4: Generate Code
dotnet run --generate-feature Orders AddOrderStep 5: Add Business Logic
public virtual Order AddOrder(AddOrderCommand cmd)
{
// Your business rules here
ValidateOrder(cmd.Dto);
ApplyBusinessRules(cmd.Dto);
return this;
}Step 6: Test and Deploy
β Unit tests generated automatically
β Integration tests ready
β API documentation generated
β Ready for deployment
π Module Planning Template
For Each Business Domain, Ask:
What is the main business concept? β Module Name
What actions can users perform? β Features
What data is needed for each action? β DTOs
What business rules apply? β Domain Logic
What events should be published? β Domain Events
Example: Customer Management Module
Main Business Concept
Customer Management
User Actions
Register, Login, Update Profile, View Profile, Deactivate
Data Requirements
Name, Email, Phone, Address, Preferences
Business Rules
Email validation, Password requirements, Profile completeness
Events
CustomerRegistered, ProfileUpdated, CustomerDeactivated
π Best Practices
Module Design:
β Single Responsibility - One business domain per module
β Clear Boundaries - Minimal coupling between modules
β Consistent Naming - Follow business terminology
β Complete Coverage - All related features in one module
Feature Design:
β One Action Per Feature - Each feature does one thing
β Clear Input/Output - Well-defined DTOs
β Business Logic in Domain - Keep controllers thin
β Proper Validation - Validate at multiple levels
Naming Conventions:
Modules:
OrdersModule,ProductsModule,CustomersModuleFeatures:
AddOrder,GetOrders,UpdateProduct,DeleteCustomerDTOs:
AddOrderDto,GetOrdersDto,OrderDtoHandlers:
AddOrderHandler,GetOrdersHandler
π Summary
Flexbase Modules and Features provide a clear, business-focused approach to enterprise application development:
Modules = Business domains (Orders, Products, Customers)
Features = User actions (AddOrder, GetProducts, UpdateCustomer)
Screen Actions = Direct mapping to features
Business Requirements = Clear path to technical implementation
The result: A maintainable, scalable application that directly reflects your business needs and user interface requirements.
Start mapping your business requirements to Flexbase modules and features today. Transform your ideas into working enterprise applications in minutes, not months.
Last updated