Benevia Core AI Documentation Index
This directory (docs/public) is the canonical documentation source for Benevia Core.
Start here
- Top-level documentation index - Follow relative links recursively. Prefer documents in
docs/publicover package README files. - First create the entity model.
- Then create write event driven logic.
Topic map
- Entity modeling: EntityModel/README.md
- Events and business logic: Events/README.md
- RBAC: RBAC/README.md
- API / OData: API/README.md
- Interfaces: INTERFACES.md
- MCP exposure for AI clients: MCP.md
- Database upgrade: DatabaseUpgrade/README.md
- Client libraries: Client/README.md
Guidance for creating a model
- Properties that should not be set by users should only have a getter and be computed.
- When extending the functionality of an existing entity, use partial classes in your feature's folder. Do not just add the property to a class in another feature. See Feature in a box
- Create properties for anything you expect the user to see so that computations are done on the server rather than on the client.
- Mark properties
Virtualif they do not have a setter and can be computed deterministically. See Virtual properties - Only use
OppositeSideCollectionwhen you need a collection on the other side. If the collection could be large, then use paged. When in doubt, use paged. See Relationships - Most entities should have a
NaturalKeyattribute that specifies one unique property. See Entity basics - Consider adding an
Indexattribute for dates or other properties that will tune database filtering. Indexes are automatically generated for reference properties and GUIDs. Entity Framework index attributes are used. See Properties - Whenever a property is added, consider whether a database upgrade subscriber needs to be written. See DatabaseUpgrade/README.md
Guidance for which events to use when writing logic on the server
Where to write the business logic
- Logic is written in a separate class suffixed with BL. Write logic for a single feature in one class. This could include logic for multiple entities.
- Almost all business logic should be written by subscribing to an event.
- Avoid controllers and helper classes.
Defaulting properties
- As much as possible, try to default required properties.
- Static values should be defaulted with the
DefaultValueattribute on a property. See Properties - When a property is defaulted on entry of another property, then use the
Defaultevent. See Default event - When a property is defaulted based on settings or the current date or time, then use the
Addedevent. See Added event - There may be times when defaulting the value should not be done until saving. In this case, use the
PreSaveevent. Check to see if it is still empty and set the value. See PreSave event
React to a value that was set on a property
- Use
Computewhen it is clear what properties affect another property's value. See Compute event - Use
Computeeven when the property has a setter. - Properties that should not be changed by the user should only have a getter and should use the
Computeevent. - Prefer
ComputeoverChanged. UseChangedwhen creating new entities or setting many properties. See Changed event - Use
PreSaveif side effects should occur only at the time of saving. Most times immediate side effects should occur, so use this sparingly. See PreSave event
Validating data
- For required values, set the
Requiredattribute on the property. Do not write logic. See Properties - Avoid validation if you can automatically correct the value using the
AutoCorrectevent. See AutoCorrect event - When validating the value of one property, use the property
Validateevent. See Validate event - When validating the combination of multiple properties, use the entity
ValidateOnSaveevent. See Entity validate event - When validating a data type, use the data type
Validateevent. See Data type validate event
When to use methods
- Use Methods when there is a button on the client that the user needs to run. See Methods
- Use Methods when you want to expose functionality to other modules.
Similar logic in multiple entities
- Consider using interfaces when there is significant duplicate logic between two entities or when there is a trait that should be applied to multiple entities.
- Avoid helper classes to accomplish this. See Interfaces
Properties that are read-only based on other properties
- Properties like this must have a setter but should use
ReadOnlyto determine when they are writable. See ReadOnly event - The expression in the read-only condition should avoid using
args.Context.GetEntities()or any other non-performant operation.