https://github.com/Qowaiv/Qowaiv
Qowaiv is a Single Value Object library
https://github.com/Qowaiv/Qowaiv
ddd domain-driven-design value-object
Last synced: 7 months ago
JSON representation
Qowaiv is a Single Value Object library
- Host: GitHub
- URL: https://github.com/Qowaiv/Qowaiv
- Owner: Qowaiv
- License: mit
- Created: 2014-01-14T12:38:29.000Z (over 12 years ago)
- Default Branch: master
- Last Pushed: 2025-11-01T09:26:27.000Z (8 months ago)
- Last Synced: 2025-11-01T11:22:55.526Z (8 months ago)
- Topics: ddd, domain-driven-design, value-object
- Language: C#
- Size: 8.93 MB
- Stars: 96
- Watchers: 8
- Forks: 14
- Open Issues: 11
-
Metadata Files:
- Readme: README.Custom.SVO.md
- License: LICENSE.txt
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
- awesome-software-architecture - Qowaiv
README
# Custom Single Value Object
Creating SVO's for the scope of (just) a project, might feel like a burden that
is not worth the effort. Although Qowaiv has as [code generator](https://github.com/Qowaiv/qowaiv-codegenerator),
it still results in quite some lines of code (easily 400), that needs
maintenance, and unit test coverage.
Another way to deal with this, is by providing a generic SVO where the actual
behavior is injectable. Qowaiv has two flavors: `Id` and
`Svo`. In this document we focus on the latter.
``` C#
public sealed class ForMyCustomSvo : SvoBehavior { /* .. */ }
public class MyModel
{
public Svo MyProperty { get; init; }
}
```
With (global) `using` statements, this can be improved a lot:
``` C#
global using MyCustomSvo = Qowaiv.Svo;
public class MyModel
{
public MyCustomSvo MyProperty { get; init; }
}
```
Just having this definition might prevent some primitive obsession, but
with implementing some custom behavior, we can achieve some nice results.
## Validation
Ensuring the validness of a SVO is key. The following mechanisms are provided:
### Setting the minimum and/or maximum length
By simply setting the `MinLength` and/or the `MaxLength` strings exceeding
these constrains are rejected:
``` C#
public sealed class ForMySvo : SvoBehavior
{
public override int MinLength => 3;
public override int MaxLength => 9;
}
```
### A regular expression pattern
If defined, the `Regex` pattern can act as a second line of defense. As in the
following example, where we ensure that all characters that make up the SVO are
alphanumeric:
``` C#
public sealed class ForMySvo : SvoBehavior
{
public override Regex Pattern => new("^([A-Z][0-9])+$");
}
```
### Overriding the validation method
For more complex scenarios, the first options provided might not be sufficient.
Therefore, is also possible to override `IsValid()`:
``` C#
public sealed class ForMySvo : SvoBehavior
{
public override bool IsValid(string str, IFormatProvider? formatProvider, out string validated)
{
if (long.TryParse(str, out var number))
{
validated = number.ToString("000");
return true;
}
else
{
validated = default;
return false;
}
}
}
```
### Normalizing input
Before the input string is validated, the `NormalizeInput()` method is called.
By default the input is trimmed, but if preferred, extra measures can be taken:
``` C#
public sealed class ForMySvo : SvoBehavior
{
public override string NormalizeInput(string str, IFormatProvider formatProvider)
=> str?.Replace("-", "").ToUpperInvariant()
?? string.Empty;
}
```
## Formatting
By default the formatting is simple: `string.Empty` for the empty state, `"?"`
for the unknown state, and the internally stored string for the rest. The empty
state behavior is not overridable, the other two are:
``` C#
public sealed class ForMySvo : SvoBehavior
{
public override string FormatUnknown(string? format, IFormatProvider? formatProvider)
=> "Unknown";
public override string Format(string str, string? format, IFormatProvider? formatProvider)
=> format == "l"
? str.ToLower()
: str;
}
```
## Limitations
It should be pointed out that this approach has its limitations. Defining extra
properties on a `Svo` is not possible, neither are casts,
operation overloads or factory methods. Extra methods are, but only as extension
methods.