Features

Summary

A Feature groups business logic by namespace. Features tell the source generator what order to register [Logic] classes in.

You write a Feature class. The generator does the rest.


One folder = one feature

Sales/
├── Feature.cs                ← IFeature, LogicNameSpace = "...Sales"
├── SalesEntity.cs
└── SalesBL.cs                ← belongs to Sales

Sales/Doc/
├── Feature.cs                ← IFeature, LogicNameSpace = "...Sales.Doc"
├── DocEntity.cs
└── DocBL.cs                  ← belongs to Sales.Doc

The folder = the namespace. The namespace = the feature.


The Feature class

public class Feature : IFeature
{
    public string Name => "Sales Doc";
    public string Description => "Sales document management.";
    public string LogicNameSpace => "Benevia.ERP.Sales.Doc";
}

No singleton needed — the runtime constructs features on demand via the registry.


Two ways to declare dependencies

1. Implicit (by namespace)

Sales.Doc is automatically after Sales — its namespace is a child.

graph LR
    Sales["Sales<br/>(namespace: ...Sales)"]
    SalesDoc["Sales.Doc<br/>(namespace: ...Sales.Doc)"]
    Sales -->|"automatic<br/>(namespace prefix)"| SalesDoc

No code needed.

2. Explicit (across namespaces)

For features in different namespace branches, use [FeatureDependsOn].

[FeatureDependsOn(typeof(Products.Feature), typeof(Customers.Feature))]
public class Feature : IFeature
{
     public string Name => "Sales Doc";
     public string LogicNameSpace => "Benevia.ERP.Sales.Doc";
     public string Description => "...";
}
graph LR
    Products["Products"]
    Customers["Customers"]
    SalesDoc["Sales.Doc"]
    Products -->|"FeatureDependsOn"| SalesDoc
    Customers -->|"FeatureDependsOn"| SalesDoc

Resulting order

graph TB
    A["services.AddBeneviaERPSalesDocBL()"]
    A --> B["Products BL registers"]
    B --> C["Customers BL registers"]
    C --> D["Sales.Doc BL registers"]
    D --> E["LogicProvider invokes<br/>methods in registered order"]

The order in BusinessLogicConfig._items is now:

  1. Products
  2. Customers
  3. Sales.Doc

That order propagates straight into compute subscriber order — see Multiple subscribers on one property.


Rules

Rule What happens
Sales.Doc namespace child of Sales Sales runs first (automatic).
[FeatureDependsOn(typeof(X))] X runs first.
Manual dep contradicts namespace rule Throws FeatureDependencyException at build/startup.
Cycle (A → B → A) Throws FeatureDependencyException.