{"id":21864777,"url":"https://github.com/fluxera/fluxera.valueobject","last_synced_at":"2025-04-14T21:04:35.092Z","repository":{"id":37855674,"uuid":"435733500","full_name":"fluxera/Fluxera.ValueObject","owner":"fluxera","description":"A value object library.","archived":false,"fork":false,"pushed_at":"2024-11-14T15:54:05.000Z","size":168,"stargazers_count":7,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-11-14T16:09:39.713Z","etag":null,"topics":["ddd","ddd-architecture","ddd-patterns","domain-driven-design","dotnet","dotnet7","value-object","value-objects"],"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/fluxera.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}},"created_at":"2021-12-07T03:44:44.000Z","updated_at":"2024-11-14T15:53:44.000Z","dependencies_parsed_at":"2023-11-15T17:42:50.217Z","dependency_job_id":"1d5e15e1-b596-4ad5-9fd9-20887b67dcee","html_url":"https://github.com/fluxera/Fluxera.ValueObject","commit_stats":null,"previous_names":[],"tags_count":47,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxera%2FFluxera.ValueObject","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxera%2FFluxera.ValueObject/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxera%2FFluxera.ValueObject/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fluxera%2FFluxera.ValueObject/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fluxera","download_url":"https://codeload.github.com/fluxera/Fluxera.ValueObject/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226857027,"owners_count":17693016,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["ddd","ddd-architecture","ddd-patterns","domain-driven-design","dotnet","dotnet7","value-object","value-objects"],"created_at":"2024-11-28T04:12:23.431Z","updated_at":"2024-11-28T04:12:24.114Z","avatar_url":"https://github.com/fluxera.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://dev.azure.com/fluxera/Foundation/_apis/build/status/GitHub/fluxera.Fluxera.ValueObject?branchName=main\u0026stageName=BuildAndTest)](https://dev.azure.com/fluxera/Foundation/_build/latest?definitionId=83\u0026branchName=main)\n\n# Fluxera.ValueObject\nA value object implementation.\n\nThis library helps in implementing **Value Object** classes in the context of **Domain-Driven Design**. \nA **Value Object** has several traits, some of which this library provides. \n\nA **Value Object**\n\n- is **immutable**. Every property must be read-only (i.e. no setter allowed) after instantiation.\n- contains **domain logic and behaviours**. It should encapsulate the domain complexity within it.\n- uses the **Ubiquitous Language** of the domain. A **Value Object** is an elegant way of embracing the language of the domain in the codebase.\n- exposes, uses and combines **functions** to provide domain value. Functions usually return new instances of a **Value Object**. _Closure of Operations_ describes an operation whose return type is the same as the type of it's arguments.\n\n```C#\n// The following function doesn't change any given Amount instance, it just returns a new one.\npublic Amount Add(Amount amount) \n{\n    if(this.Currency != amount.Currency)\n    {\n        throw new InvalidOperationException(\"Cannot add amounts with different currencies.\");\n    }\n\n    Amount result = new Amount(this.Quantity + amount.Quantity, this.Currency);\n    return result;\n}\n```\n\n- uses **all** of it's **attibutes** for **Equality** and **Uniqueness**.\n- is **automatically validated** upon instantiation using **domain validation** and throws exception if a validation fails.\n\n## Usage\n\n### ```ValueObject\u003cTValueObject\u003e```\n\nBy having your **Value Object** derive from the ```ValueObject\u003cTValueObject\u003e``` base class it properly implements **Equality** \n(```Equals()```) and **Uniqueness** (```GetHashCode()```). Automatically **all** public properties are used for the calculations \n**without** you having to write a single line of code.\n\nThis default implementation uses reflection to aquire the metadata and to get the values to use. You can provide your own\nimplementation simply by overriding the ```GetEqualityComponents()``` method.\n\nA simple implementation would look like the this:\n\n```C#\npublic class Amount : ValueObject\u003cAmount\u003e\n{\n    public Amount(decimal quantity, Currency currency)\n    {\n        this.Quantity = quantity;\n        this.Currency = currency;\n    }\n\n    public decimal Quantity { get; }\n\n    public Currency Currency { get; }\n\n    public Amount Add(Amount amount)\n    {\n        if(this.Currency != amount.Currency)\n        {\n            throw new InvalidOperationException(\"Cannot add amounts with different currencies.\");\n        }\n\n        Amount result = new Amount(this.Quantity + amount.Quantity, this.Currency);\n        return result;\n    }\n}\n```\n\nIf you decide not to use the relection-based approach, you can simply override ```GetEqualityComponents()``` and\nreturn the values manually. Keep in mind, that **all attibutes** should be used for **Equality** and **Uniqueness**.\n\n```C#\nprotected override IEnumerable\u003cobject\u003e GetEqualityComponents()\n{\n    yield return this.Quantity;\n    yield return this.Currency;\n}\n```\n\n### ```PrimitiveValueObject\u003cTValueObject, TValue```\n\nA specialized **Value Object**  that only holds a single primitive, string or enum value.\n\n### Collections\n\nThere are implementations of ```IList\u003cT\u003e```, ```ISet\u003cT\u003e``` and ```IDictionary\u003cTKey, TValue\u003e``` that determine\nequality based on content and not on the collection reference.\n\nWhen your **Value Object** contains a collection, this collection needs to be wrapped in one of the\navailable value collection to support the correct way for equality.\n\nIf you use the default behavior you can just wrap the collection in the constructor like below.\nThe default equality behavior will automatically pick the value up.\n\n```C#\npublic class Confederation : ValueObject\u003cConfederation\u003e\n{\n    public Confederation(string name, IList\u003cCountry\u003e memberCountries)\n    {\n        Guard.Against.NullOrWhiteSpace(name, nameof(name));\n        Guard.Against.NullOrEmpty(memberCountries, nameof(memberCountries));\n\n        this.Name = name;\n        this.MemberCountries = memberCountries.AsValueList(); // Wrap the list in a value list.\n    }\n\n    public string Name { get; }\n\n    public IList\u003cCountry\u003e MemberCountries { get; }\n}\n```\n\nIf you prefer to override ```GetEqualityComponents()``` and return the values manually you can wrap the list later.\n\n```C#\nprotected override IEnumerable\u003cobject\u003e GetEqualityComponents()\n{\n    yield return this.Name;\n    yield return this.MemberCountries.AsValueList(); // Wrap the list in a value list.\n}\n```\n\n**Important** Collections **must** be wrapped in value collections to ensure the correct equality behavior.\n\n#### ```ValueList\u003cT\u003e```\n\nA list with equality based on the content instead on the list's reference, i.e. two different list instances \ncontaining the same items in the same order will be equal.\n\n```C#\n// Wrap an IList in a ValueList. \n\nIList\u003cCountry\u003e countries = new List\u003cCountry\u003e \n{\n    Country.Create(\"DE\"),\n    Country.Create(\"US\"),\n};\n\nIList\u003cCountry\u003e valueList = countries.AsValueList();\n```\n\n#### ```ValueSet\u003cT\u003e```\n\nA set with equality based on the content instead on the set's reference, i.e two different set instances containing \nthe same items will be equal regardless of their order.\n\n```C#\n// Wrap an ISet in a ValueSet. \n\nISet\u003cCountry\u003e countries = new HashSet\u003cCountry\u003e\n{\n    Country.Create(\"DE\"), \n    Country.Create(\"US\"),\n};\n\nISet\u003cCountry\u003e valueSet = countries.AsValueSet();\n```\n\n#### ```ValueDictionary\u003cTKey, TValue\u003e```\n\nA dictionary with equality based on the content instead on the dictionary's reference, i.e. two different dictionary \ninstances containing the same items will be equal.\n\n```C#\n// Wrap an IDictionary in a ValueDictionary. \n\nIDictionary\u003cint, Country\u003e countries = new Dictionary\u003cint, Country\u003e\n{\n    { 1, Country.Create(\"DE\") }, \n    { 4, Country.Create(\"US\") },\n};\n\nIDictionary\u003cint, Country\u003e valueDictionary = countries.AsValueDictionary();\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluxera%2Ffluxera.valueobject","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffluxera%2Ffluxera.valueobject","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffluxera%2Ffluxera.valueobject/lists"}