@atlashub/smartstack-mcp 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -148,6 +148,10 @@ var defaultConfig = {
148
148
  "loc_",
149
149
  "lic_"
150
150
  ],
151
+ codePrefixes: {
152
+ core: "core_",
153
+ extension: "ext_"
154
+ },
151
155
  migrationFormat: "{context}_v{version}_{sequence}_{Description}",
152
156
  namespaces: {
153
157
  domain: "SmartStack.Domain",
@@ -226,6 +230,10 @@ function mergeConfig(base, override) {
226
230
  ...base.conventions.schemas,
227
231
  ...override.conventions?.schemas
228
232
  },
233
+ codePrefixes: {
234
+ ...base.conventions.codePrefixes,
235
+ ...override.conventions?.codePrefixes
236
+ },
229
237
  namespaces: {
230
238
  ...base.conventions.namespaces,
231
239
  ...override.conventions?.namespaces
@@ -280,6 +288,10 @@ var ConventionsConfigSchema = z.object({
280
288
  "loc_",
281
289
  "lic_"
282
290
  ]),
291
+ codePrefixes: z.object({
292
+ core: z.string().default("core_"),
293
+ extension: z.string().default("ext_")
294
+ }),
283
295
  migrationFormat: z.string().default("{context}_v{version}_{sequence}_{Description}"),
284
296
  namespaces: z.object({
285
297
  domain: z.string(),
@@ -323,7 +335,7 @@ var ConfigSchema = z.object({
323
335
  });
324
336
  var ValidateConventionsInputSchema = z.object({
325
337
  path: z.string().optional().describe("Project path to validate (default: SmartStack.app path)"),
326
- checks: z.array(z.enum(["tables", "migrations", "services", "namespaces", "all"])).default(["all"]).describe("Types of checks to perform")
338
+ checks: z.array(z.enum(["tables", "migrations", "services", "namespaces", "entities", "all"])).default(["all"]).describe("Types of checks to perform")
327
339
  });
328
340
  var CheckMigrationsInputSchema = z.object({
329
341
  projectPath: z.string().optional().describe("EF Core project path"),
@@ -337,7 +349,10 @@ var ScaffoldExtensionInputSchema = z.object({
337
349
  namespace: z.string().optional().describe("Custom namespace"),
338
350
  baseEntity: z.string().optional().describe("Base entity to extend (for entity type)"),
339
351
  methods: z.array(z.string()).optional().describe("Methods to generate (for service type)"),
340
- outputPath: z.string().optional().describe("Custom output path")
352
+ outputPath: z.string().optional().describe("Custom output path"),
353
+ isSystemEntity: z.boolean().optional().describe("If true, creates a system entity without TenantId"),
354
+ tablePrefix: z.string().optional().describe('Domain prefix for table name (e.g., "auth_", "nav_", "cfg_")'),
355
+ schema: z.enum(["core", "extensions"]).optional().describe("Database schema (default: core)")
341
356
  }).optional()
342
357
  });
343
358
  var ApiDocsInputSchema = z.object({
@@ -1135,6 +1150,19 @@ var scaffoldExtensionTool = {
1135
1150
  outputPath: {
1136
1151
  type: "string",
1137
1152
  description: "Custom output path"
1153
+ },
1154
+ isSystemEntity: {
1155
+ type: "boolean",
1156
+ description: "If true, creates a system entity without TenantId (for entity type)"
1157
+ },
1158
+ tablePrefix: {
1159
+ type: "string",
1160
+ description: 'Domain prefix for table name (e.g., "auth_", "nav_", "cfg_")'
1161
+ },
1162
+ schema: {
1163
+ type: "string",
1164
+ enum: ["core", "extensions"],
1165
+ description: "Database schema (default: core)"
1138
1166
  }
1139
1167
  }
1140
1168
  }
@@ -1258,40 +1286,166 @@ services.AddScoped<I{{name}}Service, {{name}}Service>();
1258
1286
  async function scaffoldEntity(name, options, structure, config, result) {
1259
1287
  const namespace = options?.namespace || config.conventions.namespaces.domain;
1260
1288
  const baseEntity = options?.baseEntity;
1289
+ const isSystemEntity = options?.isSystemEntity || false;
1290
+ const tablePrefix = options?.tablePrefix || "ref_";
1291
+ const schema = options?.schema || config.conventions.schemas.platform;
1261
1292
  const entityTemplate = `using System;
1293
+ using SmartStack.Domain.Common;
1294
+ using SmartStack.Domain.Common.Interfaces;
1262
1295
 
1263
1296
  namespace {{namespace}};
1264
1297
 
1265
1298
  /// <summary>
1266
1299
  /// {{name}} entity{{#if baseEntity}} extending {{baseEntity}}{{/if}}
1267
1300
  /// </summary>
1268
- public class {{name}}{{#if baseEntity}} : {{baseEntity}}{{/if}}
1301
+ {{#if isSystemEntity}}
1302
+ public class {{name}} : SystemEntity
1303
+ {{else}}
1304
+ public class {{name}} : BaseEntity
1305
+ {{/if}}
1269
1306
  {
1270
- {{#unless baseEntity}}
1271
- public Guid Id { get; set; }
1307
+ {{#if baseEntity}}
1308
+ /// <summary>
1309
+ /// Foreign key to {{baseEntity}}
1310
+ /// </summary>
1311
+ public Guid {{baseEntity}}Id { get; private set; }
1312
+
1313
+ /// <summary>
1314
+ /// Navigation property to {{baseEntity}}
1315
+ /// </summary>
1316
+ public virtual {{baseEntity}}? {{baseEntity}} { get; private set; }
1272
1317
 
1273
- public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
1318
+ {{/if}}
1319
+ // === BUSINESS PROPERTIES ===
1320
+ // TODO: Add {{name}} specific properties here
1274
1321
 
1275
- public DateTime? UpdatedAt { get; set; }
1322
+ /// <summary>
1323
+ /// Private constructor for EF Core
1324
+ /// </summary>
1325
+ private {{name}}() { }
1276
1326
 
1277
- {{/unless}}
1278
- // TODO: Add {{name}} specific properties
1327
+ /// <summary>
1328
+ /// Factory method to create a new {{name}}
1329
+ /// </summary>
1330
+ {{#if isSystemEntity}}
1331
+ public static {{name}} Create(
1332
+ string code,
1333
+ string? createdBy = null)
1334
+ {
1335
+ return new {{name}}
1336
+ {
1337
+ Id = Guid.NewGuid(),
1338
+ Code = code.ToLowerInvariant(),
1339
+ CreatedAt = DateTime.UtcNow,
1340
+ CreatedBy = createdBy
1341
+ };
1342
+ }
1343
+ {{else}}
1344
+ public static {{name}} Create(
1345
+ Guid tenantId,
1346
+ string code,
1347
+ string? createdBy = null)
1348
+ {
1349
+ return new {{name}}
1350
+ {
1351
+ Id = Guid.NewGuid(),
1352
+ TenantId = tenantId,
1353
+ Code = code.ToLowerInvariant(),
1354
+ CreatedAt = DateTime.UtcNow,
1355
+ CreatedBy = createdBy
1356
+ };
1357
+ }
1358
+ {{/if}}
1359
+
1360
+ /// <summary>
1361
+ /// Update the entity
1362
+ /// </summary>
1363
+ public void Update(string? updatedBy = null)
1364
+ {
1365
+ UpdatedAt = DateTime.UtcNow;
1366
+ UpdatedBy = updatedBy;
1367
+ }
1368
+
1369
+ /// <summary>
1370
+ /// Soft delete the entity
1371
+ /// </summary>
1372
+ public void SoftDelete(string? deletedBy = null)
1373
+ {
1374
+ IsDeleted = true;
1375
+ DeletedAt = DateTime.UtcNow;
1376
+ DeletedBy = deletedBy;
1377
+ }
1378
+
1379
+ /// <summary>
1380
+ /// Restore a soft-deleted entity
1381
+ /// </summary>
1382
+ public void Restore(string? restoredBy = null)
1383
+ {
1384
+ IsDeleted = false;
1385
+ DeletedAt = null;
1386
+ DeletedBy = null;
1387
+ UpdatedAt = DateTime.UtcNow;
1388
+ UpdatedBy = restoredBy;
1389
+ }
1279
1390
  }
1280
1391
  `;
1281
1392
  const configTemplate = `using Microsoft.EntityFrameworkCore;
1282
1393
  using Microsoft.EntityFrameworkCore.Metadata.Builders;
1394
+ using {{domainNamespace}};
1283
1395
 
1284
1396
  namespace {{infrastructureNamespace}}.Persistence.Configurations;
1285
1397
 
1286
- public class {{name}}Configuration : IEntityTypeConfiguration<{{domainNamespace}}.{{name}}>
1398
+ public class {{name}}Configuration : IEntityTypeConfiguration<{{name}}>
1287
1399
  {
1288
- public void Configure(EntityTypeBuilder<{{domainNamespace}}.{{name}}> builder)
1400
+ public void Configure(EntityTypeBuilder<{{name}}> builder)
1289
1401
  {
1290
- builder.ToTable("{{tablePrefix}}{{name}}s");
1402
+ // Table name with schema and domain prefix
1403
+ builder.ToTable("{{tablePrefix}}{{name}}s", "{{schema}}");
1291
1404
 
1405
+ // Primary key
1292
1406
  builder.HasKey(e => e.Id);
1293
1407
 
1294
- // TODO: Add {{name}} specific configuration
1408
+ {{#unless isSystemEntity}}
1409
+ // Multi-tenant
1410
+ builder.Property(e => e.TenantId).IsRequired();
1411
+ builder.HasIndex(e => e.TenantId);
1412
+
1413
+ // Code: lowercase, unique per tenant (filtered for soft delete)
1414
+ builder.Property(e => e.Code).HasMaxLength(100).IsRequired();
1415
+ builder.HasIndex(e => new { e.TenantId, e.Code })
1416
+ .IsUnique()
1417
+ .HasFilter("[IsDeleted] = 0")
1418
+ .HasDatabaseName("IX_{{tablePrefix}}{{name}}s_Tenant_Code_Unique");
1419
+ {{else}}
1420
+ // Code: lowercase, unique (filtered for soft delete)
1421
+ builder.Property(e => e.Code).HasMaxLength(100).IsRequired();
1422
+ builder.HasIndex(e => e.Code)
1423
+ .IsUnique()
1424
+ .HasFilter("[IsDeleted] = 0")
1425
+ .HasDatabaseName("IX_{{tablePrefix}}{{name}}s_Code_Unique");
1426
+ {{/unless}}
1427
+
1428
+ // Concurrency token
1429
+ builder.Property(e => e.RowVersion).IsRowVersion();
1430
+
1431
+ // Audit fields
1432
+ builder.Property(e => e.CreatedBy).HasMaxLength(256);
1433
+ builder.Property(e => e.UpdatedBy).HasMaxLength(256);
1434
+ builder.Property(e => e.DeletedBy).HasMaxLength(256);
1435
+
1436
+ {{#if baseEntity}}
1437
+ // Relationship to {{baseEntity}} (1:1)
1438
+ builder.HasOne(e => e.{{baseEntity}})
1439
+ .WithOne()
1440
+ .HasForeignKey<{{name}}>(e => e.{{baseEntity}}Id)
1441
+ .OnDelete(DeleteBehavior.Cascade);
1442
+
1443
+ // Index on foreign key
1444
+ builder.HasIndex(e => e.{{baseEntity}}Id)
1445
+ .IsUnique();
1446
+ {{/if}}
1447
+
1448
+ // TODO: Add additional configuration
1295
1449
  }
1296
1450
  }
1297
1451
  `;
@@ -1299,9 +1453,11 @@ public class {{name}}Configuration : IEntityTypeConfiguration<{{domainNamespace}
1299
1453
  namespace,
1300
1454
  name,
1301
1455
  baseEntity,
1456
+ isSystemEntity,
1457
+ tablePrefix,
1458
+ schema,
1302
1459
  infrastructureNamespace: config.conventions.namespaces.infrastructure,
1303
- domainNamespace: config.conventions.namespaces.domain,
1304
- schema: config.conventions.schemas.platform
1460
+ domainNamespace: config.conventions.namespaces.domain
1305
1461
  };
1306
1462
  const entityContent = Handlebars.compile(entityTemplate)(context);
1307
1463
  const configContent = Handlebars.compile(configTemplate)(context);
@@ -1319,7 +1475,13 @@ public class {{name}}Configuration : IEntityTypeConfiguration<{{domainNamespace}
1319
1475
  result.instructions.push(`public DbSet<${name}> ${name}s => Set<${name}>();`);
1320
1476
  result.instructions.push("");
1321
1477
  result.instructions.push("Create migration:");
1322
- result.instructions.push(`dotnet ef migrations add Add${name}`);
1478
+ result.instructions.push(`dotnet ef migrations add core_vX.X.X_XXX_Add${name} --context ApplicationDbContext`);
1479
+ result.instructions.push("");
1480
+ result.instructions.push("Required fields from BaseEntity:");
1481
+ result.instructions.push(`- Id (Guid), ${isSystemEntity ? "" : "TenantId (Guid), "}Code (string, lowercase)`);
1482
+ result.instructions.push("- CreatedAt, UpdatedAt, CreatedBy, UpdatedBy (audit)");
1483
+ result.instructions.push("- IsDeleted, DeletedAt, DeletedBy (soft delete)");
1484
+ result.instructions.push("- RowVersion (concurrency)");
1323
1485
  }
1324
1486
  async function scaffoldController(name, options, structure, config, result) {
1325
1487
  const namespace = options?.namespace || `${config.conventions.namespaces.api}.Controllers`;
@@ -1905,7 +2067,7 @@ var conventionsResourceTemplate = {
1905
2067
  mimeType: "text/markdown"
1906
2068
  };
1907
2069
  async function getConventionsResource(config) {
1908
- const { schemas, tablePrefixes, migrationFormat, namespaces, servicePattern } = config.conventions;
2070
+ const { schemas, tablePrefixes, codePrefixes, migrationFormat, namespaces, servicePattern } = config.conventions;
1909
2071
  return `# AtlasHub SmartStack Conventions
1910
2072
 
1911
2073
  ## Overview
@@ -1944,6 +2106,37 @@ Tables are organized by domain using prefixes:
1944
2106
  | \`loc_\` | Localization | loc_Languages, loc_Translations |
1945
2107
  | \`lic_\` | Licensing | lic_Licenses |
1946
2108
 
2109
+ ### Navigation Code Prefixes
2110
+
2111
+ All navigation data (Context, Application, Module, Section, Resource) uses code prefixes to distinguish system data from client extensions:
2112
+
2113
+ | Origin | Prefix | Usage | Example |
2114
+ |--------|--------|-------|---------|
2115
+ | SmartStack (system) | \`${codePrefixes.core}\` | Protected, delivered with SmartStack | \`${codePrefixes.core}administration\` |
2116
+ | Client (extension) | \`${codePrefixes.extension}\` | Custom, added by clients | \`${codePrefixes.extension}it\` |
2117
+
2118
+ **Navigation Hierarchy (5 levels):**
2119
+
2120
+ \`\`\`
2121
+ Context \u2192 Application \u2192 Module \u2192 Section \u2192 Resource
2122
+ \`\`\`
2123
+
2124
+ **Examples for each level:**
2125
+
2126
+ | Level | Core Example | Extension Example |
2127
+ |-------|--------------|-------------------|
2128
+ | Context | \`${codePrefixes.core}administration\` | \`${codePrefixes.extension}it\` |
2129
+ | Application | \`${codePrefixes.core}settings\` | \`${codePrefixes.extension}custom_app\` |
2130
+ | Module | \`${codePrefixes.core}users\` | \`${codePrefixes.extension}inventory\` |
2131
+ | Section | \`${codePrefixes.core}management\` | \`${codePrefixes.extension}reports\` |
2132
+ | Resource | \`${codePrefixes.core}user_list\` | \`${codePrefixes.extension}stock_view\` |
2133
+
2134
+ **Rules:**
2135
+ 1. \`${codePrefixes.core}*\` codes are **protected** - clients cannot create or modify them
2136
+ 2. \`${codePrefixes.extension}*\` codes are **free** - clients can create custom navigation
2137
+ 3. SmartStack updates will never overwrite \`${codePrefixes.extension}*\` data
2138
+ 4. Codes must be unique within their level (e.g., no two Contexts with same code)
2139
+
1947
2140
  ### Entity Configuration
1948
2141
 
1949
2142
  \`\`\`csharp
@@ -2187,6 +2380,197 @@ public interface IUserServiceHooks
2187
2380
 
2188
2381
  ---
2189
2382
 
2383
+ ## 7. Entity Conventions
2384
+
2385
+ ### Required Interfaces
2386
+
2387
+ All entities MUST implement the following interfaces:
2388
+
2389
+ | Interface | Properties | Required |
2390
+ |-----------|------------|----------|
2391
+ | \`ITenantEntity\` | \`TenantId\` (Guid) | **YES** (except system entities) |
2392
+ | \`IHasCode\` | \`Code\` (string, lowercase, max 100) | **YES** |
2393
+ | \`ISoftDeletable\` | \`IsDeleted\`, \`DeletedAt\`, \`DeletedBy\` | **YES** |
2394
+ | \`IVersioned\` | \`RowVersion\` (byte[]) | **YES** |
2395
+ | \`IHistoryEntity<T>\` | \`SourceId\`, \`ChangeType\`, \`OldValues\`, \`NewValues\`, \`ChangedAt\`, \`ChangedBy\` | For history tables |
2396
+
2397
+ ### Required Fields (All Entities)
2398
+
2399
+ | Field | Type | Description |
2400
+ |-------|------|-------------|
2401
+ | \`Id\` | \`Guid\` | Primary key |
2402
+ | \`TenantId\` | \`Guid\` | **Tenant identifier** (required for multi-tenant isolation) |
2403
+ | \`Code\` | \`string\` | Unique identifier per tenant (lowercase, max 100) |
2404
+ | \`CreatedAt\` | \`DateTime\` | Creation timestamp |
2405
+ | \`UpdatedAt\` | \`DateTime?\` | Last update timestamp |
2406
+ | \`CreatedBy\` | \`string?\` | User who created the entity |
2407
+ | \`UpdatedBy\` | \`string?\` | User who last updated the entity |
2408
+ | \`IsDeleted\` | \`bool\` | Soft delete flag |
2409
+ | \`DeletedAt\` | \`DateTime?\` | Deletion timestamp |
2410
+ | \`DeletedBy\` | \`string?\` | User who deleted the entity |
2411
+ | \`RowVersion\` | \`byte[]\` | Concurrency token |
2412
+
2413
+ ### Multi-Tenant Strategy
2414
+
2415
+ \`\`\`csharp
2416
+ // Interface for tenant-scoped entities
2417
+ public interface ITenantEntity
2418
+ {
2419
+ Guid TenantId { get; set; }
2420
+ }
2421
+
2422
+ // Global query filter (applied automatically)
2423
+ builder.HasQueryFilter(e => e.TenantId == _currentTenantService.TenantId);
2424
+ \`\`\`
2425
+
2426
+ **System Entities (without TenantId):**
2427
+ - Licenses (\`lic_\`)
2428
+ - Global configuration (\`cfg_\` system level)
2429
+ - Shared reference tables
2430
+
2431
+ Use \`ISystemEntity\` marker interface for these entities.
2432
+
2433
+ ### Code Field Rules
2434
+
2435
+ 1. **Always lowercase** - Normalized via setter and value converter
2436
+ 2. **Prefix required**: \`${codePrefixes.core}\` (system) or \`${codePrefixes.extension}\` (extensions)
2437
+ 3. **Unique composite index**: \`(TenantId, Code) WHERE IsDeleted = 0\`
2438
+ 4. **Max length**: 100 characters
2439
+
2440
+ \`\`\`csharp
2441
+ // Code property with automatic lowercase normalization
2442
+ private string _code = string.Empty;
2443
+ public string Code
2444
+ {
2445
+ get => _code;
2446
+ set => _code = value?.ToLowerInvariant() ?? string.Empty;
2447
+ }
2448
+ \`\`\`
2449
+
2450
+ ### Soft Delete with Restore
2451
+
2452
+ \`\`\`csharp
2453
+ public interface ISoftDeletable
2454
+ {
2455
+ bool IsDeleted { get; set; }
2456
+ DateTime? DeletedAt { get; set; }
2457
+ string? DeletedBy { get; set; }
2458
+ }
2459
+
2460
+ // Domain methods
2461
+ public void SoftDelete(string? deletedBy = null)
2462
+ {
2463
+ IsDeleted = true;
2464
+ DeletedAt = DateTime.UtcNow;
2465
+ DeletedBy = deletedBy;
2466
+ }
2467
+
2468
+ public void Restore(string? restoredBy = null)
2469
+ {
2470
+ IsDeleted = false;
2471
+ DeletedAt = null;
2472
+ DeletedBy = null;
2473
+ UpdatedAt = DateTime.UtcNow;
2474
+ UpdatedBy = restoredBy;
2475
+ }
2476
+ \`\`\`
2477
+
2478
+ ### History Tables (JSON Pattern)
2479
+
2480
+ \`\`\`csharp
2481
+ public interface IHistoryEntity<TSourceId>
2482
+ {
2483
+ Guid Id { get; set; }
2484
+ TSourceId SourceId { get; set; }
2485
+ ChangeType ChangeType { get; set; } // Created, Updated, Deleted
2486
+ DateTime ChangedAt { get; set; }
2487
+ string? ChangedBy { get; set; }
2488
+ string? OldValues { get; set; } // JSON snapshot before change
2489
+ string? NewValues { get; set; } // JSON snapshot after change
2490
+ }
2491
+
2492
+ public enum ChangeType { Created = 1, Updated = 2, Deleted = 3 }
2493
+ \`\`\`
2494
+
2495
+ ### Complete Entity Example
2496
+
2497
+ \`\`\`csharp
2498
+ public class MyEntity : BaseEntity
2499
+ {
2500
+ // === REQUIRED (from BaseEntity) ===
2501
+ // public Guid Id { get; set; }
2502
+ // public Guid TenantId { get; set; }
2503
+ // public string Code { get; set; } // lowercase, unique per tenant
2504
+ // public DateTime CreatedAt { get; set; }
2505
+ // public DateTime? UpdatedAt { get; set; }
2506
+ // public string? CreatedBy { get; set; }
2507
+ // public string? UpdatedBy { get; set; }
2508
+ // public bool IsDeleted { get; set; }
2509
+ // public DateTime? DeletedAt { get; set; }
2510
+ // public string? DeletedBy { get; set; }
2511
+ // public byte[] RowVersion { get; set; }
2512
+
2513
+ // === BUSINESS PROPERTIES ===
2514
+ public string Name { get; private set; } = null!;
2515
+
2516
+ private MyEntity() { }
2517
+
2518
+ public static MyEntity Create(Guid tenantId, string code, string name, string? createdBy = null)
2519
+ {
2520
+ return new MyEntity
2521
+ {
2522
+ Id = Guid.NewGuid(),
2523
+ TenantId = tenantId,
2524
+ Code = code.ToLowerInvariant(),
2525
+ Name = name,
2526
+ CreatedAt = DateTime.UtcNow,
2527
+ CreatedBy = createdBy
2528
+ };
2529
+ }
2530
+ }
2531
+ \`\`\`
2532
+
2533
+ ### EF Core Configuration
2534
+
2535
+ \`\`\`csharp
2536
+ public class MyEntityConfiguration : IEntityTypeConfiguration<MyEntity>
2537
+ {
2538
+ public void Configure(EntityTypeBuilder<MyEntity> builder)
2539
+ {
2540
+ builder.ToTable("domain_MyEntities", "${schemas.platform}");
2541
+ builder.HasKey(e => e.Id);
2542
+
2543
+ // Multi-tenant
2544
+ builder.Property(e => e.TenantId).IsRequired();
2545
+ builder.HasIndex(e => e.TenantId);
2546
+
2547
+ // Code: lowercase, unique per tenant (filtered)
2548
+ builder.Property(e => e.Code).HasMaxLength(100).IsRequired();
2549
+ builder.HasIndex(e => new { e.TenantId, e.Code })
2550
+ .IsUnique()
2551
+ .HasFilter("[IsDeleted] = 0");
2552
+
2553
+ // Concurrency
2554
+ builder.Property(e => e.RowVersion).IsRowVersion();
2555
+
2556
+ // Audit fields
2557
+ builder.Property(e => e.CreatedBy).HasMaxLength(256);
2558
+ builder.Property(e => e.UpdatedBy).HasMaxLength(256);
2559
+ builder.Property(e => e.DeletedBy).HasMaxLength(256);
2560
+ }
2561
+ }
2562
+ \`\`\`
2563
+
2564
+ ### Entity Types Summary
2565
+
2566
+ | Type | Fields | Use Case |
2567
+ |------|--------|----------|
2568
+ | **BaseEntity** | All required fields | Standard tenant-scoped entities |
2569
+ | **SystemEntity** | All except TenantId | Shared system entities |
2570
+ | **HistoryEntity** | Id, TenantId, SourceId, ChangeType, Values, ChangedAt/By | Audit trail tables |
2571
+
2572
+ ---
2573
+
2190
2574
  ## Quick Reference
2191
2575
 
2192
2576
  | Category | Convention | Example |
@@ -2194,11 +2578,14 @@ public interface IUserServiceHooks
2194
2578
  | Platform schema | \`${schemas.platform}\` | \`.ToTable("auth_Users", "${schemas.platform}")\` |
2195
2579
  | Extensions schema | \`${schemas.extensions}\` | \`.ToTable("client_Custom", "${schemas.extensions}")\` |
2196
2580
  | Table prefixes | \`${tablePrefixes.slice(0, 5).join(", ")}\`, etc. | \`auth_Users\`, \`nav_Modules\` |
2581
+ | Core code prefix | \`${codePrefixes.core}\` | \`${codePrefixes.core}administration\` |
2582
+ | Extension code prefix | \`${codePrefixes.extension}\` | \`${codePrefixes.extension}custom_module\` |
2197
2583
  | Migration | \`{context}_v{version}_{seq}_{Desc}\` | \`core_v1.0.0_001_CreateAuthUsers\` |
2198
2584
  | Interface | \`I<Name>Service\` | \`IUserService\` |
2199
2585
  | Implementation | \`<Name>Service\` | \`UserService\` |
2200
2586
  | Domain namespace | \`${namespaces.domain}\` | - |
2201
2587
  | API namespace | \`${namespaces.api}\` | - |
2588
+ | Required entity fields | Id, TenantId, Code, Audit, SoftDelete, RowVersion | See Entity Conventions |
2202
2589
  `;
2203
2590
  }
2204
2591