24 KiB
Software Data Model Specification
Centron Enterprise Application - Data Architecture and Implementation
Document Control
- Project: Centron Enterprise Application
- Version: 1.0
- Date: 2025-09-30
- Standard: ISO/IEC/IEEE 29148:2018
- Classification: Software Data Model and Schema Documentation
Table of Contents
- Data Model Overview
- Entity Framework Architecture
- Domain Entity Categories
- Database Schema Implementation
- NHibernate Mapping Configuration
- Data Relationships and Constraints
- Data Validation and Business Rules
- Data Flow and Transformation
1. Data Model Overview
1.1 Data Architecture Statistics
- Total Entity Files: 1,145 entity classes
- NHibernate Mappings: 956 FluentNHibernate mapping files
- Domain Categories: 268 business domain categories
- Database Tables: Estimated 800+ tables
- Entity Relationships: Complex web of one-to-many, many-to-many associations
1.2 Data Architecture Principles
SW-DATA-ARCH-001: The software SHALL implement domain-driven design principles
- Entity Aggregates: Related entities grouped into aggregate roots
- Bounded Contexts: Clear separation between business domains
- Value Objects: Immutable objects representing domain concepts
SW-DATA-ARCH-002: The software SHALL use standardized entity base classes
- Base Entity:
BaseEntitywith primary key I3D - Persisted Entity:
PersistedEntitywith audit trail fields - Soft Delete Pattern: IsDeleted flag for logical deletion
1.3 Data Storage Technologies
| Technology | Purpose | Implementation Location |
|---|---|---|
| SQL Server | Primary database | Production and development |
| NHibernate | ORM Framework | src/backend/Centron.DAO/ |
| FluentNHibernate | Mapping Configuration | src/backend/Centron.DAO/Mappings/ |
| Entity Framework | Limited use | Some specialized components |
2. Entity Framework Architecture
2.1 Base Entity Hierarchy
SW-ENT-001: Base Entity Structure
// File: src/backend/Centron.Entities/BaseEntity.cs
public abstract class BaseEntity
{
public virtual int I3D { get; set; } // Primary Key
}
// File: src/backend/Centron.Entities/PersistedEntity.cs
public class PersistedEntity : BaseEntity
{
public virtual DateTime? CreatedDate { get; set; }
public virtual DateTime? ChangedDate { get; set; }
public virtual int? CreatedByI3D { get; set; }
public virtual int? ChangedByI3D { get; set; }
public virtual bool IsDeleted { get; set; }
public virtual DateTime? DeletedDate { get; set; }
public virtual int? DeletedByI3D { get; set; }
}
Design Requirements:
- SW-ENT-001.1: All entities MUST inherit from BaseEntity
- SW-ENT-001.2: Business entities SHOULD inherit from PersistedEntity
- SW-ENT-001.3: Primary keys MUST use I3D naming convention
- SW-ENT-001.4: Foreign keys MUST end with I3D suffix
2.2 Entity Naming Conventions
SW-ENT-002: Naming Standard Implementation
- Entity Classes: PascalCase (e.g.,
Account,AccountAddress) - Properties: PascalCase (e.g.,
Name,CreatedDate) - Foreign Keys: EntityNameI3D (e.g.,
AccountI3D,EmployeeI3D) - Collection Properties: Plural names (e.g.,
Addresses,AccountTypes)
File Organization:
- Entities:
src/backend/Centron.Entities/Entities/{Domain}/{EntityName}.cs - Mappings:
src/backend/Centron.DAO/Mappings/{Domain}/{EntityName}Maps.cs
3. Domain Entity Categories
3.1 Account Management Domain
SW-DOM-001: Account Entity Model
- Root Entity:
Account(src/backend/Centron.Entities/Entities/Accounts/Account.cs) - Related Entities: 45+ account-related entity classes
Core Account Entities:
// Account aggregate root
public class Account : BaseEntity, IAccount
{
public virtual int Number { get; set; }
public virtual string Name { get; set; }
public virtual string Matchcode { get; set; }
public virtual string Email { get; set; }
public virtual bool IsActive { get; set; }
public virtual bool IsLocked { get; set; }
// Navigation properties
public virtual IList<AccountAddress> Addresses { get; set; }
public virtual IList<AccountTypeToAccount> AccountTypes { get; set; }
}
// Account address value object
public class AccountAddress : PersistedEntity
{
public virtual int AccountI3D { get; set; }
public virtual string Street { get; set; }
public virtual string PostalCode { get; set; }
public virtual string City { get; set; }
public virtual int? CountryI3D { get; set; }
public virtual bool IsMainAddress { get; set; }
// Navigation properties
public virtual Account Account { get; set; }
public virtual IList<AccountAddressContact> Contacts { get; set; }
}
Account Domain Entities:
| Entity | Purpose | Key Relationships |
|---|---|---|
| Account | Core account data | → AccountAddress, AccountType |
| AccountAddress | Address information | → Account, Country, Contacts |
| AccountAddressContact | Contact persons | → AccountAddress, Employee |
| AccountContract | Service contracts | → Account, ContractKind |
| AccountType | Account classification | ← AccountTypeToAccount |
| AccountActivity | Activity logging | → Account, Employee |
3.2 Sales and Receipt Domain
SW-DOM-002: Receipt Entity Model
- Entity Count: 150+ receipt-related entities
- Root Entities: Receipt types (Offer, Order, Invoice, DeliveryList)
- Complex Hierarchy: Receipt → ReceiptItem → ReceiptItemArticle
Receipt Entity Hierarchy:
// Abstract receipt base
public abstract class ReceiptBase : PersistedEntity, IReceiptBase
{
public virtual int Number { get; set; }
public virtual DateTime? CreatedAt { get; set; }
public virtual decimal CurrencyFactor { get; set; }
public virtual string Comment { get; set; }
public virtual ReceiptState State { get; set; }
// Customer relationship
public virtual int? CustomerI3D { get; set; }
public virtual Customer Customer { get; set; }
// Receipt items
public virtual IList<ReceiptItemBase> Items { get; set; }
}
// Specific receipt types
public class Offer : ReceiptBase
{
public virtual DateTime? ValidUntil { get; set; }
public virtual bool IsConverted { get; set; }
}
public class Order : ReceiptBase
{
public virtual DateTime? DesiredDeliveryDate { get; set; }
public virtual bool IsUrgent { get; set; }
}
public class Invoice : ReceiptBase
{
public virtual DateTime? DueDate { get; set; }
public virtual decimal TotalAmount { get; set; }
public virtual InvoiceType Type { get; set; }
}
Receipt Domain Statistics:
- Receipt Types: 8 main types (Offer, Order, Invoice, etc.)
- Receipt Items: 20+ item-related entities
- Article Integration: Links to merchandise management
- Price Calculation: Complex pricing entity relationships
3.3 Customer Asset Domain
SW-DOM-003: Asset Management Entities
- Entity Count: 80+ asset-related entities
- Asset Lifecycle: Purchase → Installation → Service → Disposal
- Service Integration: Links to helpdesk and support systems
Core Asset Entities:
public class CustomerAsset : PersistedEntity
{
public virtual int CustomerI3D { get; set; }
public virtual int ArticleI3D { get; set; }
public virtual string SerialNumber { get; set; }
public virtual DateTime? PurchaseDate { get; set; }
public virtual DateTime? WarrantyEndDate { get; set; }
public virtual AssetCondition Condition { get; set; }
public virtual decimal PurchasePrice { get; set; }
public virtual string Location { get; set; }
// Navigation properties
public virtual Customer Customer { get; set; }
public virtual Article Article { get; set; }
public virtual IList<ServiceOrder> ServiceOrders { get; set; }
public virtual IList<AssetContract> Contracts { get; set; }
}
3.4 Administration Domain
SW-DOM-004: System Administration Entities
- Entity Count: 200+ administration entities
- Categories: Users, Rights, Settings, Company, Logging
- Security Model: Role-based access control implementation
Key Administration Entities:
| Entity Category | Entity Count | Key Entities |
|---|---|---|
| User Management | 25+ | Employee, User, UserLogin, UserRights |
| Company Structure | 30+ | Mandator, Branch, CompanyGroup |
| System Settings | 40+ | ApplicationSetting, SystemConfiguration |
| Document Management | 35+ | Document, Directory, FileReference |
| Audit and Logging | 20+ | AuditLog, SystemLog, ChangeTracking |
4. Database Schema Implementation
4.1 Primary Key Strategy
SW-SCHEMA-001: Primary Key Implementation
- Column Name: I3D (Integer 3 Digits, legacy naming)
- Data Type: INT IDENTITY(1,1) NOT NULL
- Clustering: Clustered primary key on I3D
SQL Schema Pattern:
CREATE TABLE [dbo].[Accounts] (
[I3D] INT IDENTITY(1,1) NOT NULL,
[Number] INT NOT NULL,
[Name] NVARCHAR(255) NOT NULL,
[Matchcode] NVARCHAR(64) NULL,
[Email] NVARCHAR(255) NULL,
[IsActive] BIT NOT NULL DEFAULT(1),
[CreatedDate] DATETIME2(2) NULL,
[ChangedDate] DATETIME2(2) NULL,
[CreatedByI3D] INT NULL,
[ChangedByI3D] INT NULL,
[IsDeleted] BIT NOT NULL DEFAULT(0),
CONSTRAINT [PK_Accounts] PRIMARY KEY CLUSTERED ([I3D])
);
4.2 Foreign Key Conventions
SW-SCHEMA-002: Foreign Key Implementation
- Naming: {ReferencedEntity}I3D
- Nullable: Most foreign keys nullable to support partial data
- Constraints: Declarative referential integrity
Foreign Key Examples:
-- Account to Employee reference
ALTER TABLE [Accounts] ADD CONSTRAINT [FK_Accounts_CreatedBy]
FOREIGN KEY ([CreatedByI3D]) REFERENCES [Employees]([I3D]);
-- Account Address to Account reference
ALTER TABLE [AccountAddresses] ADD CONSTRAINT [FK_AccountAddresses_Account]
FOREIGN KEY ([AccountI3D]) REFERENCES [Accounts]([I3D]);
4.3 Audit Trail Implementation
SW-SCHEMA-003: Audit Field Standard
- Creation: CreatedDate, CreatedByI3D, CreatedVersion
- Modification: ChangedDate, ChangedByI3D, ChangedVersion
- Deletion: IsDeleted, DeletedDate, DeletedByI3D
Audit Trigger Pattern (Implemented where needed):
CREATE TRIGGER [TR_Accounts_Audit] ON [Accounts]
AFTER INSERT, UPDATE
AS
BEGIN
UPDATE a SET
ChangedDate = GETUTCDATE(),
ChangedByI3D = SYSTEM_USER
FROM [Accounts] a
INNER JOIN inserted i ON a.I3D = i.I3D
END
5. NHibernate Mapping Configuration
5.1 FluentNHibernate Mapping Pattern
SW-MAP-001: Standard Mapping Implementation
- Location:
src/backend/Centron.DAO/Mappings/{Domain}/{Entity}Maps.cs - Pattern: ClassMap inheritance
- Configuration: Table name, column mappings, relationships
Example Mapping Implementation:
// File: src/backend/Centron.DAO/Mappings/Accounts/AccountMaps.cs
public class AccountMaps : ClassMap<Account>
{
public AccountMaps()
{
// Table mapping
Table("Accounts");
// Primary key
Id(m => m.I3D).Column("I3D");
// Simple properties
Map(m => m.Number).Column("Number").Not.Nullable();
Map(m => m.Name).Column("Name").Length(255);
Map(m => m.Email).Column("Email").Length(255).Nullable();
Map(m => m.IsActive).Column("IsActive");
// Audit fields
Map(m => m.CreatedDate).Column("CreatedDate").Nullable();
Map(m => m.CreatedByI3D).Column("CreatedByI3D").Nullable();
Map(m => m.ChangedDate).Column("ChangedDate").Nullable();
Map(m => m.ChangedByI3D).Column("ChangedByI3D").Nullable();
// One-to-many relationships
HasMany(m => m.Addresses)
.KeyColumn("AccountI3D")
.Cascade.All()
.Lazy();
HasMany(m => m.AccountTypes)
.KeyColumn("AccountI3D")
.Cascade.All()
.Lazy();
}
}
5.2 Relationship Mapping Patterns
SW-MAP-002: Association Mapping Strategy
- One-to-Many: HasMany() with KeyColumn
- Many-to-One: References() with Column
- Many-to-Many: HasManyToMany() with intermediate table
- Component: Component() for value objects
Complex Relationship Example:
public class ReceiptMaps : ClassMap<Receipt>
{
public ReceiptMaps()
{
Table("Receipts");
Id(m => m.I3D).Column("I3D");
// Many-to-one customer reference
References(m => m.Customer)
.Column("CustomerI3D")
.Nullable()
.Lazy();
// One-to-many receipt items
HasMany(m => m.Items)
.KeyColumn("ReceiptI3D")
.Cascade.AllDeleteOrphan()
.Lazy();
// Component mapping for value object
Component(m => m.DeliveryAddress, address =>
{
address.Map(a => a.Street).Column("DeliveryStreet");
address.Map(a => a.City).Column("DeliveryCity");
address.Map(a => a.PostalCode).Column("DeliveryPostalCode");
});
}
}
5.3 Lazy Loading Configuration
SW-MAP-003: Performance Optimization Strategy
- Default: Lazy loading enabled for collections
- Eager Loading: Fetch joins for frequently accessed associations
- Batch Size: Configured batch sizes for N+1 query prevention
Lazy Loading Implementation:
// Lazy loading (default)
HasMany(m => m.Items).Lazy();
// Eager loading for critical paths
HasMany(m => m.Items)
.Fetch.Join()
.BatchSize(50);
// No lazy loading for small datasets
HasMany(m => m.StatusHistory)
.Not.LazyLoad()
.Cascade.All();
6. Data Relationships and Constraints
6.1 Domain Model Relationships
SW-REL-001: Core Business Relationships
erDiagram
ACCOUNT ||--o{ ACCOUNT_ADDRESS : has
ACCOUNT_ADDRESS ||--o{ ACCOUNT_ADDRESS_CONTACT : has
ACCOUNT ||--o{ RECEIPT : creates
RECEIPT ||--o{ RECEIPT_ITEM : contains
RECEIPT_ITEM }o--|| ARTICLE : references
CUSTOMER_ASSET }o--|| CUSTOMER : belongs_to
CUSTOMER_ASSET }o--|| ARTICLE : is_instance_of
CUSTOMER_ASSET ||--o{ SERVICE_ORDER : requires
Key Relationship Patterns:
- Account Hierarchy: Account → Address → Contact
- Receipt Processing: Customer → Receipt → Item → Article
- Asset Management: Customer → Asset → Service → Contract
- User Management: Employee → User → Rights → Functions
6.2 Referential Integrity
SW-REL-002: Database Constraint Implementation
- Primary Keys: Enforced at database level
- Foreign Keys: Declarative constraints with appropriate actions
- Check Constraints: Business rule enforcement
- Unique Constraints: Data uniqueness requirements
Constraint Examples:
-- Unique account number constraint
ALTER TABLE [Accounts] ADD CONSTRAINT [UQ_Accounts_Number]
UNIQUE ([Number]);
-- Check constraint for valid email format
ALTER TABLE [Accounts] ADD CONSTRAINT [CK_Accounts_Email]
CHECK ([Email] LIKE '%_@_%.__%' OR [Email] IS NULL);
-- Foreign key with cascade delete
ALTER TABLE [AccountAddresses] ADD CONSTRAINT [FK_AccountAddresses_Account]
FOREIGN KEY ([AccountI3D]) REFERENCES [Accounts]([I3D])
ON DELETE CASCADE;
6.3 Data Consistency Rules
SW-REL-003: Business Rule Enforcement
- Account Status: Active accounts cannot be deleted, only deactivated
- Receipt Workflow: State transitions must follow defined workflow
- Financial Integrity: Invoice totals must match item sum plus tax
- Asset Tracking: Assets must have valid customer and article references
7. Data Validation and Business Rules
7.1 Entity Validation Framework
SW-VAL-001: Validation Implementation
- Level 1: Property-level validation in entity setters
- Level 2: Entity-level validation in business logic
- Level 3: Cross-entity validation in aggregates
- Level 4: Database constraint validation
Validation Pattern Example:
public class Account : BaseEntity, IValidatableObject
{
private string _name;
public virtual string Name
{
get => _name;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Account name cannot be empty");
if (value.Length > 255)
throw new ArgumentException("Account name too long");
_name = value;
}
}
public IEnumerable<ValidationResult> Validate(ValidationContext context)
{
var results = new List<ValidationResult>();
// Email validation
if (!string.IsNullOrEmpty(Email) && !IsValidEmail(Email))
{
results.Add(new ValidationResult("Invalid email format",
new[] { nameof(Email) }));
}
// Account number validation
if (Number <= 0)
{
results.Add(new ValidationResult("Account number must be positive",
new[] { nameof(Number) }));
}
return results;
}
}
7.2 Business Rule Implementation
SW-VAL-002: Domain-Specific Validation Rules
- Account Management: Name uniqueness, valid contact information
- Receipt Processing: State transition validation, price consistency
- Asset Management: Serial number uniqueness, warranty date logic
- Financial: Tax calculation correctness, currency constraints
Business Rule Examples:
public class AccountBusinessRules
{
public Result<bool> ValidateAccountCreation(Account account)
{
// Rule 1: Account name must be unique within mandator
if (IsAccountNameDuplicate(account.Name, account.MandatorI3D))
{
return Result.Error("Account name already exists in this mandator");
}
// Rule 2: At least one address must be marked as main address
if (account.Addresses?.Any(a => a.IsMainAddress) != true)
{
return Result.Error("Account must have at least one main address");
}
// Rule 3: Customer accounts must have valid tax number if required
if (account.AccountTypes.Any(t => t.Type.RequiresTaxNumber) &&
string.IsNullOrEmpty(account.TaxNumber))
{
return Result.Error("Tax number required for this account type");
}
return Result.Success(true);
}
}
8. Data Flow and Transformation
8.1 Entity-DTO Transformation Pipeline
SW-FLOW-001: Data Transformation Architecture
- Inbound: DTO → Entity conversion for API requests
- Outbound: Entity → DTO conversion for API responses
- Validation: Data validation at transformation boundaries
- Security: Sensitive field filtering during transformation
Transformation Pipeline:
public class AccountDataTransformer
{
public Account ConvertDTOToEntity(AccountDTO dto)
{
// 1. Create entity instance
var entity = new Account();
// 2. Map simple properties
entity.Number = dto.Number;
entity.Name = dto.Name;
entity.Email = dto.Email;
// 3. Handle complex properties
entity.Addresses = dto.Addresses?
.Select(ConvertAddressDTO)
.ToList() ?? new List<AccountAddress>();
// 4. Apply business rules during conversion
ValidateAndApplyBusinessRules(entity);
return entity;
}
public AccountDTO ConvertEntityToDTO(Account entity)
{
// 1. Ensure entity is not connected to NHibernate session
NHibernateUtil.Initialize(entity);
// 2. Use ObjectMapper for conversion
var dto = ObjectMapper.Map<AccountDTO>(entity);
// 3. Filter sensitive information
FilterSensitiveFields(dto);
return dto;
}
}
8.2 Data Synchronization Patterns
SW-FLOW-002: Multi-Source Data Management
- External APIs: Sync product data from suppliers
- Legacy Systems: Import existing customer data
- Web Services: Real-time data exchange with client applications
- Batch Processing: Nightly data consolidation and cleanup
Synchronization Example:
public class DataSynchronizationService
{
public async Task<Result<bool>> SynchronizeCustomerData()
{
// 1. Load pending sync items
var pendingSyncs = await LoadPendingSynchronizations();
// 2. Process each sync item
foreach (var sync in pendingSyncs)
{
try
{
// 3. Transform external data format
var entity = await TransformExternalData(sync.ExternalData);
// 4. Validate and merge with existing data
var mergeResult = await MergeWithExistingEntity(entity);
if (!mergeResult.IsSuccess) continue;
// 5. Save changes and mark sync complete
await SaveEntity(mergeResult.Value);
await MarkSyncComplete(sync.I3D);
}
catch (Exception ex)
{
await LogSyncError(sync.I3D, ex);
}
}
return Result.Success(true);
}
}
8.3 Change Tracking Implementation
SW-FLOW-003: Entity Change Detection
- NHibernate Events: Automatic change detection via interceptors
- Audit Logging: Complete audit trail of all entity modifications
- Version Control: Optimistic locking with version stamps
- Change Notifications: Event publishing for interested subscribers
Change Tracking Pattern:
public class EntityChangeTracker : IInterceptor
{
public bool OnSave(object entity, object id, object[] state,
string[] propertyNames, IType[] types)
{
if (entity is PersistedEntity persistedEntity)
{
// Set audit fields for new entities
var now = DateTime.UtcNow;
var userId = GetCurrentUserId();
persistedEntity.CreatedDate = now;
persistedEntity.CreatedByI3D = userId;
// Log entity creation
LogEntityChange("CREATE", entity.GetType().Name, id, state);
}
return false;
}
public bool OnFlushDirty(object entity, object id, object[] currentState,
object[] previousState, string[] propertyNames, IType[] types)
{
if (entity is PersistedEntity persistedEntity)
{
// Set audit fields for modified entities
persistedEntity.ChangedDate = DateTime.UtcNow;
persistedEntity.ChangedByI3D = GetCurrentUserId();
// Log detailed field changes
LogFieldChanges(entity.GetType().Name, id, propertyNames,
previousState, currentState);
}
return false;
}
}
Performance and Scalability
Database Performance Optimization
Indexing Strategy:
- Primary Keys: Clustered indexes on I3D columns
- Foreign Keys: Non-clustered indexes on all FK columns
- Search Fields: Composite indexes on frequently searched combinations
- Query Optimization: Index hints and query plan analysis
Sample Index Strategy:
-- Primary key (clustered)
CREATE CLUSTERED INDEX [PK_Accounts] ON [Accounts]([I3D]);
-- Foreign key indexes
CREATE NONCLUSTERED INDEX [IX_Accounts_CreatedBy]
ON [Accounts]([CreatedByI3D]);
-- Search optimization indexes
CREATE NONCLUSTERED INDEX [IX_Accounts_Search]
ON [Accounts]([Name], [Matchcode], [Email])
INCLUDE ([Number], [IsActive]);
-- Date range queries
CREATE NONCLUSTERED INDEX [IX_Accounts_DateRange]
ON [Accounts]([CreatedDate], [ChangedDate])
WHERE [IsDeleted] = 0;
Memory and Session Management
NHibernate Performance:
- Session Scope: Session-per-request pattern
- Lazy Loading: Optimized lazy loading configuration
- Batch Processing: Batch sizes configured for large datasets
- Cache Strategy: Second-level cache for reference data
Document Approval
- Database Architect: Schema design and constraints verified
- Data Modeler: Entity relationships and mappings validated
- Performance Engineer: Query optimization and indexing approved
- Security Officer: Data protection and audit requirements confirmed
- Date: 2025-09-30
- Version Control: Committed to repository requirements/software/