{"id":30196266,"url":"https://github.com/lukasz-strus/domainsmith","last_synced_at":"2026-06-08T16:02:24.050Z","repository":{"id":308114982,"uuid":"1030877940","full_name":"lukasz-strus/DomainSmith","owner":"lukasz-strus","description":"Roslyn Source Generator for .NET that automates Domain-Driven Design patterns and Clean Architecture components.","archived":false,"fork":false,"pushed_at":"2026-01-26T07:53:55.000Z","size":281,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-09T05:46:53.014Z","etag":null,"topics":["clean-architecture","code-generator","csharp","domain-driven-design","dotnet","dotnetstandart20","roslyn"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lukasz-strus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-08-02T14:20:16.000Z","updated_at":"2026-02-04T11:27:50.000Z","dependencies_parsed_at":"2025-08-20T20:26:04.083Z","dependency_job_id":null,"html_url":"https://github.com/lukasz-strus/DomainSmith","commit_stats":null,"previous_names":["lukasz-strus/domainsmith"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lukasz-strus/DomainSmith","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukasz-strus%2FDomainSmith","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukasz-strus%2FDomainSmith/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukasz-strus%2FDomainSmith/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukasz-strus%2FDomainSmith/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lukasz-strus","download_url":"https://codeload.github.com/lukasz-strus/DomainSmith/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukasz-strus%2FDomainSmith/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34069501,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-08T02:00:07.615Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["clean-architecture","code-generator","csharp","domain-driven-design","dotnet","dotnetstandart20","roslyn"],"created_at":"2025-08-13T05:17:23.463Z","updated_at":"2026-06-08T16:02:24.044Z","avatar_url":"https://github.com/lukasz-strus.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DomainSmith\n\n[![CICD](https://github.com/lukasz-strus/DomainSmith/actions/workflows/main.yml/badge.svg)](https://github.com/lukasz-strus/DomainSmith/actions/workflows/main.yml)\n[![codecov](https://codecov.io/gh/lukasz-strus/DomainSmith/branch/main/graph/badge.svg?token=DAD5PSBP23)](https://codecov.io/gh/lukasz-strus/DomainSmith)\n[![NuGet Badge](https://img.shields.io/nuget/v/DomainSmith.Abstraction.svg)](https://www.nuget.org/packages/DomainSmith.Abstraction/)\n\nRoslyn Source Generator for .NET that automates Domain-Driven Design patterns and Clean Architecture components.\n\n## General\n\n`DomainSmith` is a set of Roslyn source generators that generate DDD/Clean Architecture boilerplate **from simple domain types** annotated with attributes:\n\n- `ValueObject` (immutability / value-based equality + factories and updates)\n- `Entity` (ID + create/update API)\n- `AggregateRoot` (ID + support for entity collections inside the aggregate)\n- `Repository` (repository interface for an Aggregate Root)\n\nThe generator does not replace your domain logic. Its goal is to generate a consistent API and extensions (for example `Create(...)`, `Update(...)`, entity-collection helpers, etc.) from the fields/properties you already defined.\n\nPackages in `src/` are independent and target `netstandard2.0` (generators are consumed by application projects). Additionally, `DomainSmith.Abstraction` provides shared DDD primitives (`ValueObject`, `Entity\u003cTId\u003e`, `AggregateRoot\u003cTId\u003e`) and a lightweight `Result` pattern.\n\n## Value Object (`[ValueObject]`)\n\nAttribute: `DomainSmith.ValueObject.ValueObjectAttribute`\n\nGenerator input:\n\n- a type (class/record) annotated with `[ValueObject]`\n- properties on that type (excluding `[ExcludeFromGeneration]`)\n- whether the `Result pattern` is enabled\n\n```csharp\nusing DomainSmith.Abstraction.Core.Result;\n\nnamespace DomainSmith.ValueObject.Examples.ValueObjects;\n\n[ValueObject]\npublic partial record Money\n{\n    public static int MaxAmount = 10000;\n    public static int MinAmount = 0;\n    public static int MaxCurrencyLength = 3;\n    public decimal Amount { get; init; }\n    public string Currency { get; init; } = \"USD\";\n\n    static partial void OnCreating(ref decimal amount, ref string currency, ref bool canCreate, ref Error error)\n    {\n        if (amount \u003e MaxAmount)\n        {\n            canCreate = false;\n            error = new Error(\"Money.Amount.ExceedsMax\", $\"Amount cannot exceed {MaxAmount}.\");\n            return;\n        }\n\n        if (amount \u003c MinAmount)\n        {\n            canCreate = false;\n            error = new Error(\"Money.Amount.BelowMin\", $\"Amount cannot be below {MinAmount}.\");\n            return;\n        }\n\n        if (currency.Length \u003e MaxCurrencyLength)\n        {\n            canCreate = false;\n            error = new Error(\"Money.Currency.ExceedsMaxLength\",\n                $\"Currency cannot exceed {MaxCurrencyLength} characters.\");\n            return;\n        }\n    }\n}\n```\n\nWhat gets generated:\n\n```csharp\n// \u003cauto-generated/\u003e\nusing DomainSmith.Abstraction.Core.Result;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Text.Json;\nusing DomainSmith.Abstraction.Core.Result;\n\nnamespace DomainSmith.ValueObject.Examples.ValueObjects;\n\npartial record Money\n{\n    private Money(decimal amount, string currency)\n    {\n        Amount = amount;\n\t\tCurrency = currency;\n    }\n\n    public static Result\u003cMoney\u003e Create(decimal amount, string currency)\n    {\n        bool canCreate = true;\n\t\tError error = Error.None;\n\n        OnCreating(ref amount, ref currency, ref canCreate, ref error);\n        \n        var result = new Money(amount, currency);\n\n        OnCreated(result, ref canCreate, ref error);\n        if (!canCreate) return Result.Failure\u003cMoney\u003e(error);\n\n        return Result.Success(result);\n    }\n\n    static partial void OnCreating(ref decimal amount, ref string currency, ref bool canCreate, ref Error error);\n    static partial void OnCreated(Money instance, ref bool canCreate, ref Error error);\n}\n```\n\n- code that provides a consistent way to create and update the object based on defined properties,\n- factory/update methods like `Create(...)` and `Update(...)` (variants depend on `Result pattern`),\n- an extension/partial API for the type (the generator also tracks “extension name” and type reference).\n\n`Result pattern`:\n\n- by default, the generator produces APIs returning `Result` / `Result\u003cT\u003e`.\n- if you use `[NoResultPattern]`, the generator switches to APIs without `Result`.\n\n## Entity (`[Entity(typeof(TId))]`)\n\nAttribute: `DomainSmith.Entity.EntityAttribute`\n\nGenerator input:\n\n```csharp\nusing DomainSmith.Abstraction.Common;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing DomainSmith.Abstraction.Core.Result;\n\nnamespace DomainSmith.Entity.Examples.Entities;\n\n[Entity(typeof(OwnerId))]\npublic partial class Owner\n{\n    public string FirstName { get; private set; }\n    public string LastName { get; private set; }\n    public string Email { get; private set; }\n    [ExcludeFromGeneration] public bool IsEnabled { get; private set; }\n    [AutoGenerated] public DateTime? ModifiedAt { get; private set; }\n    [AutoGenerated] public DateTime CreatedAt { get; private set; }\n\n    public Result Activate()\n    {\n        IsEnabled = true;\n        ModifiedAt = DateTime.Now;\n        return Result.Success();\n    }\n\n    public Result Deactivate()\n    {\n        IsEnabled = false;\n        ModifiedAt = DateTime.Now;\n        return Result.Success();\n    }\n\n    partial void CreateCreatedAt() =\u003e CreatedAt = DateTime.Now;\n    partial void UpdateCreatedAt() =\u003e ModifiedAt = DateTime.Now;\n    partial void UpdateModifiedAt() =\u003e ModifiedAt = DateTime.Now;\n}\n\npublic record OwnerId(Guid Value) : EntityIdRecord\u003cGuid\u003e(Value);\n```\n\n- a class annotated with `[Entity(typeof(SomeIdType))]` – the generator reads the ID type from the attribute argument,\n- ID metadata (for example whether it is a `record`/`class` and the underlying value type) is used to tailor generation,\n- entity properties (excluding `[ExcludeFromGeneration]`),\n- `Result pattern` configuration.\n\nWhat gets generated:\n\n```csharp\n// \u003cauto-generated/\u003e\nusing DomainSmith.Abstraction.Common;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing DomainSmith.Abstraction.Core.Result;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Text.Json;\nusing DomainSmith.Abstraction.Core.Result;\n\nnamespace DomainSmith.Entity.Examples.Entities;\n\npartial class Owner : Entity\u003cOwnerId\u003e\n{\n    private Owner(OwnerId id, string firstname, string lastname, string email) : base(id)\n    {\n        FirstName = firstname;\n\t\tLastName = lastname;\n\t\tEmail = email;\n    }\n\n    internal static Result\u003cOwner\u003e Create(string firstname, string lastname, string email)\n    {\n        bool canCreate = true;\n\t\tError error = Error.None;\n        OnCreatingAuto(ref firstname, ref lastname, ref email, ref canCreate, ref error);\n        OnCreating(ref firstname, ref lastname, ref email, ref canCreate, ref error);\n\n        var result = new Owner(new OwnerId(Guid.NewGuid()), firstname, lastname, email);\n\n        OnCreated(result, ref canCreate, ref error);\n        if(!canCreate) return Result.Failure\u003cOwner\u003e(error);\n\n        result.CreateModifiedAt();\n\t\tresult.CreateCreatedAt();\n\n        return Result.Success(result);\n    }\n\n    internal Result\u003cbool\u003e Update(string firstname, string lastname, string email)\n    {\n        bool canUpdate = true;\n\t\tError error = Error.None;\n        OnUpdatingAuto(ref firstname, ref lastname, ref email, ref canUpdate, ref error);\n        OnUpdating(ref firstname, ref lastname, ref email, ref canUpdate, ref error);\n\n        var tmpFirstName = FirstName;\n\t\tvar tmpLastName = LastName;\n\t\tvar tmpEmail = Email;\n        FirstName = firstname;\n\t\tLastName = lastname;\n\t\tEmail = email;\n\n        OnUpdated(ref canUpdate, ref error);\n        if(!canUpdate)\n        {\n           FirstName = tmpFirstName;\n\t\t\tLastName = tmpLastName;\n\t\t\tEmail = tmpEmail;\n           return Result.Failure\u003cbool\u003e(error);\n        }\n        UpdateModifiedAt();\n\t\tUpdateCreatedAt();\n\n        return Result.Success(true);\n    }\n\n    private static void OnCreatingAuto(ref string firstname, ref string lastname, ref string email, ref bool canCreate, ref Error error)\n    {\n        OnCreatingFirstName(ref firstname, ref canCreate, ref error);\n\t\tOnCreatingLastName(ref lastname, ref canCreate, ref error);\n\t\tOnCreatingEmail(ref email, ref canCreate, ref error);\n    }\n\n    private void OnUpdatingAuto(ref string firstname, ref string lastname, ref string email, ref bool canUpdate, ref Error error)\n    {\n        OnUpdatingFirstName(ref firstname, ref canUpdate, ref error);\n\t\tOnUpdatingLastName(ref lastname, ref canUpdate, ref error);\n\t\tOnUpdatingEmail(ref email, ref canUpdate, ref error);\n    }\n\n    static partial void OnCreating(ref string firstname, ref string lastname, ref string email, ref bool canCreate, ref Error error);\n    static partial void OnCreated(Owner result, ref bool canCreate, ref Error error);\n\n    partial void OnUpdating(ref string firstname, ref string lastname, ref string email, ref bool canUpdate, ref Error error);\n    partial void OnUpdated(ref bool canUpdate, ref Error error);\n\n    partial void CreateModifiedAt();\n\tpartial void CreateCreatedAt();\n    partial void UpdateModifiedAt();\n\tpartial void UpdateCreatedAt();\n\n    static partial void OnCreatingFirstName(ref string firstname, ref bool canCreate, ref Error error);\n\tstatic partial void OnCreatingLastName(ref string lastname, ref bool canCreate, ref Error error);\n\tstatic partial void OnCreatingEmail(ref string email, ref bool canCreate, ref Error error);\n    partial void OnUpdatingFirstName(ref string firstname, ref bool canUpdate, ref Error error);\n\tpartial void OnUpdatingLastName(ref string lastname, ref bool canUpdate, ref Error error);\n\tpartial void OnUpdatingEmail(ref string email, ref bool canUpdate, ref Error error);\n}\n```\n\n- a consistent API for entity creation (`Create(...)`) and modification (`Update(...)`),\n- integration with `Entity\u003cTId\u003e` (from `DomainSmith.Abstraction`),\n- `Result`-based variants (default) or non-`Result` variants (with `[NoResultPattern]`).\n\n## Aggregate Root (`[AggregateRoot(typeof(TId))]`)\n\nAttribute: `DomainSmith.AggregateRoot.AggregateRootAttribute`\n\nGenerator input:\n\n```csharp\nusing DomainSmith.Abstraction.Common;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing DomainSmith.Entity;\n\nnamespace DomainSmith.AggregateRoot.Examples.AggregateRoots;\n\npublic record OwnerId(Guid Value) : EntityIdRecord\u003cGuid\u003e(Value);\n\npublic record CarId(Guid Value) : EntityIdRecord\u003cGuid\u003e(Value);\n\n[Entity(typeof(CarId))]\npublic partial class Car\n{\n    public string Name { get; private set; }\n    public string Type { get; private set; }\n    public decimal Price { get; private set; }\n}\n\n[AggregateRoot(typeof(OwnerId))]\npublic partial class Owner\n{\n    private readonly HashSet\u003cCar\u003e _newCars = [];\n    private readonly HashSet\u003cCar\u003e _oldCars = [];\n    [ExcludeFromGeneration] private readonly HashSet\u003cCar\u003e _allCars = [];\n\n    public string FirstName { get; private set; }\n    public string LastName { get; private set; }\n    public string Email { get; private set; }\n}\n```\n\n- a class annotated with `[AggregateRoot(typeof(SomeIdType))]` (similar to Entity),\n- aggregate properties (excluding `[ExcludeFromGeneration]`),\n- `Result pattern` configuration,\n- **EntityCollections** – the generator analyzes the class and detects entity collections for which it should generate collection-management APIs.\n\nWhat gets generated:\n```csharp\n// \u003cauto-generated/\u003e\nusing DomainSmith.Abstraction.Common;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing DomainSmith.Entity;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Text.Json;\nusing DomainSmith.Abstraction.Core.Result;\nusing System.Linq;\nusing DomainSmith.Abstraction.Common;\nusing DomainSmith.Abstraction.Core.Result;\n\nnamespace DomainSmith.AggregateRoot.Examples.AggregateRoots;\n\npartial class Owner : AggregateRoot\u003cOwnerId\u003e\n{\n    private Owner(OwnerId id, string firstname, string lastname, string email) : base(id)\n    {\n        FirstName = firstname;\n\t\tLastName = lastname;\n\t\tEmail = email;\n    }\n\n    public static Result\u003cOwner\u003e Create(string firstname, string lastname, string email)\n    {\n        bool canCreate = true;\n\t\tError error = Error.None;\n        OnCreatingAuto(ref firstname, ref lastname, ref email, ref canCreate, ref error);\n        OnCreating(ref firstname, ref lastname, ref email, ref canCreate, ref error);\n\n        var result = new Owner(new OwnerId(Guid.NewGuid()), firstname, lastname, email);\n\n        OnCreated(result, ref canCreate, ref error);\n        if(!canCreate) return Result.Failure\u003cOwner\u003e(error);\n\n        \n\n        return Result.Success(result);\n    }\n\n    public Result\u003cbool\u003e Update(string firstname, string lastname, string email)\n    {\n        bool canUpdate = true;\n\t\tError error = Error.None;\n        OnUpdatingAuto(ref firstname, ref lastname, ref email, ref canUpdate, ref error);\n        OnUpdating(ref firstname, ref lastname, ref email, ref canUpdate, ref error);\n\n        var tmpFirstName = FirstName;\n\t\tvar tmpLastName = LastName;\n\t\tvar tmpEmail = Email;\n        FirstName = firstname;\n\t\tLastName = lastname;\n\t\tEmail = email;\n\n        OnUpdated(ref canUpdate, ref error);\n        if(!canUpdate)\n        {\n           FirstName = tmpFirstName;\n\t\t\tLastName = tmpLastName;\n\t\t\tEmail = tmpEmail;\n           return Result.Failure\u003cbool\u003e(error);\n        }\n        \n\n        return Result.Success(true);\n    }\n\n    private static void OnCreatingAuto(ref string firstname, ref string lastname, ref string email, ref bool canCreate, ref Error error)\n    {\n        OnCreatingFirstName(ref firstname, ref canCreate, ref error);\n\t\tOnCreatingLastName(ref lastname, ref canCreate, ref error);\n\t\tOnCreatingEmail(ref email, ref canCreate, ref error);\n    }\n\n    private void OnUpdatingAuto(ref string firstname, ref string lastname, ref string email, ref bool canUpdate, ref Error error)\n    {\n        OnUpdatingFirstName(ref firstname, ref canUpdate, ref error);\n\t\tOnUpdatingLastName(ref lastname, ref canUpdate, ref error);\n\t\tOnUpdatingEmail(ref email, ref canUpdate, ref error);\n    }\n\n    static partial void OnCreating(ref string firstname, ref string lastname, ref string email, ref bool canCreate, ref Error error);\n    static partial void OnCreated(Owner result, ref bool canCreate, ref Error error);\n\n    partial void OnUpdating(ref string firstname, ref string lastname, ref string email, ref bool canUpdate, ref Error error);\n    partial void OnUpdated(ref bool canUpdate, ref Error error);\n\n    \n    \n\n    static partial void OnCreatingFirstName(ref string firstname, ref bool canCreate, ref Error error);\n\tstatic partial void OnCreatingLastName(ref string lastname, ref bool canCreate, ref Error error);\n\tstatic partial void OnCreatingEmail(ref string email, ref bool canCreate, ref Error error);\n    partial void OnUpdatingFirstName(ref string firstname, ref bool canUpdate, ref Error error);\n\tpartial void OnUpdatingLastName(ref string lastname, ref bool canUpdate, ref Error error);\n\tpartial void OnUpdatingEmail(ref string email, ref bool canUpdate, ref Error error);\n\n    \n\tpublic IReadOnlyCollection\u003cCar\u003e NewCars =\u003e _newCars;\n\n\tpublic Result\u003cCar\u003e AddNewElementToNewCars(string name, string type, decimal price)\n\t{\n\t\tvar result = Car.Create(name, type, price);\n\t\tif (result.IsFailure)\n\t\t\treturn result;\n\n\t\tvar entity = result.Value();\n\t\t_newCars.Add(entity);\n\n\t\treturn entity;\n\t}\n\n\tpublic Result UpdateElementInNewCars(CarId id, string name, string type, decimal price)\n\t{\n\t\tvar entity = _newCars.FirstOrDefault(a =\u003e a.Id == id);\n\t\tif (entity is null)\n\t\t\treturn Result.Failure(new Error(\"Owner.NewCars\", \"Not found\"));\n\n\t\tvar result = entity.Update(name, type, price);\n\n\t\treturn result;\n\t}\n\n\tpublic Result DeleteElementFromNewCars(CarId id)\n\t{\n\t\tvar entity = _newCars.FirstOrDefault(a =\u003e a.Id == id);\n\t\tif (entity is null)\n\t\t\treturn Result.Failure(new Error(\"Owner.NewCars\", \"Not found\"));\n\n\t\t_newCars.Remove(entity);\n\n\t\treturn Result.Success();\n\t}\n\n\tpublic IReadOnlyCollection\u003cCar\u003e OldCars =\u003e _oldCars;\n\n\tpublic Result\u003cCar\u003e AddNewElementToOldCars(string name, string type, decimal price)\n\t{\n\t\tvar result = Car.Create(name, type, price);\n\t\tif (result.IsFailure)\n\t\t\treturn result;\n\n\t\tvar entity = result.Value();\n\t\t_oldCars.Add(entity);\n\n\t\treturn entity;\n\t}\n\n\tpublic Result UpdateElementInOldCars(CarId id, string name, string type, decimal price)\n\t{\n\t\tvar entity = _oldCars.FirstOrDefault(a =\u003e a.Id == id);\n\t\tif (entity is null)\n\t\t\treturn Result.Failure(new Error(\"Owner.OldCars\", \"Not found\"));\n\n\t\tvar result = entity.Update(name, type, price);\n\n\t\treturn result;\n\t}\n\n\tpublic Result DeleteElementFromOldCars(CarId id)\n\t{\n\t\tvar entity = _oldCars.FirstOrDefault(a =\u003e a.Id == id);\n\t\tif (entity is null)\n\t\t\treturn Result.Failure(new Error(\"Owner.OldCars\", \"Not found\"));\n\n\t\t_oldCars.Remove(entity);\n\n\t\treturn Result.Success();\n\t}\n\n}\n```\n\n- a consistent API for aggregate creation (`Create(...)`) and modification (`Update(...)`),\n- integration with `AggregateRoot\u003cTId\u003e` (from `DomainSmith.Abstraction`),\n- `Result`-based variants (default) or non-`Result` variants (with `[NoResultPattern]`),\n- helpers for managing entity collections inside the aggregate.\n\n### EntityCollections in AggregateRoot\n\nIf the generator detects that the aggregate has an entity collection (for example with a backing field), it generates:\n\n- optionally, an `IReadOnlyCollection\u003cT\u003e` property exposing the collection,\n- helper methods for collection management:\n  \t- `AddNewElementTo{CollectionName}(...)`\n  - `UpdateElementIn{CollectionName}(id, ...)`\n  - `DeleteElementFrom{CollectionName}(id)`\n\n`Result pattern` variants:\n\n- with `Result pattern`:\n  - `Add...` returns `Result\u003cTElement\u003e`\n  - `Update...` / `Delete...` return `Result`\n  - when an element is not found, it returns `Result.Failure(new Error(\"{Aggregate}.{Collection}\", \"Not found\"))`\n- without `Result pattern`:\n  - `Add...` returns `TElement?` (`null` on failure)\n  - `Update...` / `Delete...` are `void` (silent failure, e.g., element not found)\n\nAdditionally, the generator detects whether the element entity itself uses the `Result pattern` (for example `TElement.Create(...)` may return `Result\u003cTElement\u003e`). In that case the generated code can:\n\n- call `TElement.Create(...)`\n- short-circuit and propagate the failure (or return `null`) if creation/update fails.\n\n## Repository (for an Aggregate Root)\n\nThe repository generator is based on types annotated with `[AggregateRoot]`.\n\nInput:\n\n```csharp\nusing DomainSmith.AggregateRoot;\n\nnamespace DomainSmith.Repository.Examples.AggregateRoots;\n\n[AggregateRoot(typeof(Guid))]\npublic partial class Owner\n{\n    public string FirstName { get; private set; }\n    public string LastName { get; private set; }\n    public string Email { get; private set; }\n}\n```\n\n- an Aggregate Root class annotated with `[AggregateRoot(typeof(TId))]`\n- the ID type `TId`\n\nWhat gets generated:\n\n```csharp\n// \u003cauto-generated/\u003e\nusing DomainSmith.AggregateRoot;\nusing DomainSmith.Abstraction.Core.Primitives;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Text.Json;\nusing DomainSmith.Abstraction.Core.Maybe;\n\nnamespace DomainSmith.Repository.Examples.AggregateRoots;\n\npublic interface IOwnerRepository\n{\n    Task\u003cDomainSmith.Abstraction.Core.Maybe.Maybe\u003cOwner\u003e\u003e GetByIdAsync(Guid id, CancellationToken cancellationToken = default);\n\n    Task\u003cIReadOnlyCollection\u003cOwner\u003e\u003e GetAllAsync(Guid id, CancellationToken cancellationToken = default);\n\n    Task AddAsync(Owner owner, CancellationToken cancellationToken = default);\n\n    void Remove(Owner owner);\n}\n```\n\n- a repository interface named `I{AggregateRootName}Repository` (the output filename is set explicitly in the generator),\n- method signatures tailored to the aggregate ID,\n- Maybe-based `GetByIdAsync(...)` to represent the possibility of a missing entity.\n\n## Practical notes\n\n- For source generators, the target type should typically be `partial` and have stable properties that define the generated API.\n- If you do not want a member to participate in generation (technical fields, caches, etc.), mark it with `[ExcludeFromGeneration]`.\n- If you do not use the `Result pattern`, add `[assembly: NoResultPattern]` or annotate individual types with `[NoResultPattern]`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasz-strus%2Fdomainsmith","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukasz-strus%2Fdomainsmith","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukasz-strus%2Fdomainsmith/lists"}