diff --git a/docs/getting-started/choose-workflow.md b/docs/getting-started/choose-workflow.md index ea5a336..063c790 100644 --- a/docs/getting-started/choose-workflow.md +++ b/docs/getting-started/choose-workflow.md @@ -152,31 +152,26 @@ Start with an existing database, scaffold EF Core entities, then add domain rule ### Example ```csharp -// 1. Entities scaffolded from database +// 1. Entities scaffolded from database with [DomainEntity] attributes added +[assembly: GenerateManifest("ECommerce", Version = "1.0.0")] + +[DomainEntity(TableName = "Customers")] public class Customer { + [Key] public int Id { get; set; } - public string Name { get; set; } - public string Email { get; set; } + + [Required] + [MaxLength(200)] + public string Name { get; set; } = ""; + + [Required] + [MaxLength(500)] + public string Email { get; set; } = ""; } -// 2. Create manifest from existing entities -var manifest = new DomainManifest -{ - Name = "ECommerce", - Entities = [ - new EntityManifest - { - Name = "Customer", - TypeName = "MyApp.Customer", - Properties = [ - new PropertyManifest { Name = "Id", TypeName = "System.Int32" }, - new PropertyManifest { Name = "Name", TypeName = "System.String" }, - new PropertyManifest { Name = "Email", TypeName = "System.String" } - ] - } - ] -}; +// 2. Manifest is automatically generated - NO manual creation needed! +var manifest = ECommerceManifest.GeneratedManifest; // 3. Add rules to scaffolded entities var rules = new RuleSetBuilder("Default") @@ -189,6 +184,8 @@ var rules = new RuleSetBuilder("Default") ```bash dotnet add package JD.Domain.Abstractions +dotnet add package JD.Domain.ManifestGeneration +dotnet add package JD.Domain.ManifestGeneration.Generator dotnet add package JD.Domain.Rules dotnet add package JD.Domain.Runtime dotnet add package JD.Domain.DomainModel.Generator # Optional @@ -208,7 +205,7 @@ dotnet add package JD.Domain.FluentValidation.Generator # Optional - ❌ Database remains source of truth (potential drift) - ❌ Less control over domain design - ❌ EF scaffolding can produce suboptimal models -- ❌ Requires manual manifest creation or tooling +- ❌ Requires adding attributes to scaffolded entities ### Next Steps @@ -251,33 +248,41 @@ Mix code-first domain definitions with database-first scaffolded entities, using ### Example ```csharp +// All entities use source generation - NO manual manifest creation! +[assembly: GenerateManifest("ECommerce", Version = "1.1.0")] + // Code-first entity -var codeFirstPart = Domain.Create("ECommerce") - .Entity(e => e - .Property(c => c.Id) - .Property(c => c.Name)) - .Build(); +[DomainEntity] +public class Customer +{ + [Key] + public int Id { get; set; } -// Database-first entity -var dbFirstPart = new DomainManifest + [Required] + [MaxLength(200)] + public string Name { get; set; } = ""; +} + +// Database-first entity (scaffolded with attributes added) +[DomainEntity(TableName = "Orders")] +public class Order { - Name = "ECommerce", - Entities = [ - new EntityManifest - { - Name = "Order", - TypeName = "MyApp.Order", - Properties = [ /* scaffolded properties */ ] - } - ] -}; - -// Merge manifests -var merged = MergeManifests(codeFirstPart, dbFirstPart); + [Key] + public int Id { get; set; } + + [Required] + public int CustomerId { get; set; } + + [Required] + public decimal Total { get; set; } +} + +// Access auto-generated manifest +var manifest = ECommerceManifest.GeneratedManifest; // Create snapshot var writer = new SnapshotWriter(); -var snapshot = writer.CreateSnapshot(merged); +var snapshot = writer.CreateSnapshot(manifest); // Later: compare versions var diff = diffEngine.Compare(snapshotV1, snapshotV2); @@ -408,6 +413,8 @@ dotnet add package JD.Domain.AspNetCore ```bash dotnet add package JD.Domain.Abstractions +dotnet add package JD.Domain.ManifestGeneration +dotnet add package JD.Domain.ManifestGeneration.Generator dotnet add package JD.Domain.Rules dotnet add package JD.Domain.Runtime ``` diff --git a/samples/JD.Domain.Samples.DbFirst/AssemblyInfo.cs b/samples/JD.Domain.Samples.DbFirst/AssemblyInfo.cs new file mode 100644 index 0000000..c2192a3 --- /dev/null +++ b/samples/JD.Domain.Samples.DbFirst/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using JD.Domain.ManifestGeneration; + +[assembly: GenerateManifest("BloggingDb", Version = "1.0.0", Namespace = "JD.Domain.Samples.DbFirst")] diff --git a/samples/JD.Domain.Samples.DbFirst/JD.Domain.Samples.DbFirst.csproj b/samples/JD.Domain.Samples.DbFirst/JD.Domain.Samples.DbFirst.csproj index 8eb9ef9..0229914 100644 --- a/samples/JD.Domain.Samples.DbFirst/JD.Domain.Samples.DbFirst.csproj +++ b/samples/JD.Domain.Samples.DbFirst/JD.Domain.Samples.DbFirst.csproj @@ -11,6 +11,8 @@ + + diff --git a/samples/JD.Domain.Samples.DbFirst/Program.cs b/samples/JD.Domain.Samples.DbFirst/Program.cs index 39c1098..9f9dda6 100644 --- a/samples/JD.Domain.Samples.DbFirst/Program.cs +++ b/samples/JD.Domain.Samples.DbFirst/Program.cs @@ -1,4 +1,5 @@ -using JD.Domain.Abstractions; +using System.ComponentModel.DataAnnotations; +using JD.Domain.ManifestGeneration; using JD.Domain.Rules; using JD.Domain.Snapshot; @@ -6,18 +7,21 @@ namespace JD.Domain.Samples.DbFirst; /// /// Demonstrates database-first workflow: existing EF entities + JD rules as partials. +/// Manifests are now automatically generated from entity attributes - NO manual string writing! /// public static class Program { public static void Main() { - Console.WriteLine("=== JD.Domain Database-First Sample ===\n"); + Console.WriteLine("=== JD.Domain Database-First Sample (Automatic Manifest Generation) ===\n"); - // Step 1: Simulate loading manifest from scaffolded EF entities - Console.WriteLine("1. Loading manifest from existing EF entities..."); - var manifest = CreateManifestFromScaffoldedEntities(); - Console.WriteLine($" Loaded: {manifest.Name} v{manifest.Version}"); + // Step 1: Access auto-generated manifest from scaffolded EF entities + Console.WriteLine("1. Using auto-generated manifest from EF entities..."); + var manifest = BloggingDbManifest.GeneratedManifest; + Console.WriteLine($" Domain: {manifest.Name} v{manifest.Version}"); Console.WriteLine($" Entities: {manifest.Entities.Count}"); + Console.WriteLine($" Source: {(manifest.Sources.Count > 0 ? manifest.Sources[0].Type : "Unknown")}"); + Console.WriteLine($" NO MANUAL STRING WRITING REQUIRED!"); // Step 2: Add JD rules as partial classes (rules defined separately) Console.WriteLine("\n2. Defining rules for existing entities..."); @@ -63,63 +67,41 @@ public static void Main() } Console.WriteLine("\n=== Sample Complete ==="); - } - - /// - /// Simulates creating a manifest from EF Core scaffolded entities. - /// - private static DomainManifest CreateManifestFromScaffoldedEntities() - { - return new DomainManifest - { - Name = "BloggingDb", - Version = new Version(1, 0, 0), - Entities = - [ - new EntityManifest - { - Name = "Blog", - TypeName = "JD.Domain.Samples.DbFirst.Blog", - TableName = "Blogs", - Properties = - [ - new PropertyManifest { Name = "BlogId", TypeName = "System.Int32", IsRequired = true }, - new PropertyManifest { Name = "Url", TypeName = "System.String", IsRequired = true, MaxLength = 500 } - ], - KeyProperties = ["BlogId"] - }, - new EntityManifest - { - Name = "Post", - TypeName = "JD.Domain.Samples.DbFirst.Post", - TableName = "Posts", - Properties = - [ - new PropertyManifest { Name = "PostId", TypeName = "System.Int32", IsRequired = true }, - new PropertyManifest { Name = "Title", TypeName = "System.String", IsRequired = true, MaxLength = 200 }, - new PropertyManifest { Name = "Content", TypeName = "System.String", IsRequired = false }, - new PropertyManifest { Name = "BlogId", TypeName = "System.Int32", IsRequired = true } - ], - KeyProperties = ["PostId"] - } - ] - }; + Console.WriteLine("Manifest was generated automatically from entity attributes!"); } } -// These represent EF Core scaffolded entities +// These represent EF Core scaffolded entities with [DomainEntity] attributes added +// This demonstrates how you can add JD.Domain attributes to existing scaffolded entities +[DomainEntity(TableName = "Blogs")] public class Blog { + [Key] public int BlogId { get; set; } + + [Required] + [MaxLength(500)] public string Url { get; set; } = ""; + + [ExcludeFromManifest] public List Posts { get; set; } = new(); } +[DomainEntity(TableName = "Posts")] public class Post { + [Key] public int PostId { get; set; } + + [Required] + [MaxLength(200)] public string Title { get; set; } = ""; + public string? Content { get; set; } + + [Required] public int BlogId { get; set; } + + [ExcludeFromManifest] public Blog Blog { get; set; } = null!; }