Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -594,3 +594,6 @@ coverage/
!assets/packages/*

.tools/

# Local NuGet feed for unpublished package consumption (Purview.ValueObjects migration)
.local-feed/
16 changes: 9 additions & 7 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@
<!-- Keep compiler-facing Roslyn packages aligned with the minimum SDK pinned in global.json so analyser ProjectReferences load on build machines. -->
<RoslynCompilerVersion>5.9.0</RoslynCompilerVersion>
<RoslynAnalyzersVersion>5.9.0</RoslynAnalyzersVersion>
<TUnitVersion>1.67.0</TUnitVersion>
<PurviewSGFrameworkVersion>1.0.0-prerelease.42</PurviewSGFrameworkVersion>
<PurviewZodSharpVersion>2.0.0-prerelease.6</PurviewZodSharpVersion>
<TUnitVersion>1.68.4</TUnitVersion>
<PurviewSGFrameworkVersion>1.0.0-prerelease.44</PurviewSGFrameworkVersion>
<PurviewValueObjectsVersion>1.0.0-prerelease.2</PurviewValueObjectsVersion>
<PurviewZodSharpVersion>2.0.0-prerelease.9</PurviewZodSharpVersion>
<TestcontainersVersion>4.15.0</TestcontainersVersion>
<BenchmarkDotNetVersion>0.14.0</BenchmarkDotNetVersion>
<DotnetRuntimeVersion>10.0.12</DotnetRuntimeVersion>
<AspireVersion>13.5.3</AspireVersion>
<AspireVersion>13.5.4</AspireVersion>
<OTelVersion>1.18.0</OTelVersion>
<MSExtensionVersion>10.10.0</MSExtensionVersion>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Aspire.Azure.Storage.Blobs" Version="$(AspireVersion)" />
<PackageVersion Include="AspireC4.Hosting" Version="13.5.3" />
<PackageVersion Include="AspireC4.Hosting" Version="13.5.3.2" />
<PackageVersion Include="Bogus" Version="35.6.5" />
<PackageVersion Include="LinqKit" Version="1.3.11" />
<PackageVersion Include="FluentValidation" Version="12.1.1" />
Expand All @@ -33,13 +34,14 @@
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(DotnetRuntimeVersion)" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="$(DotnetRuntimeVersion)" />
<PackageVersion Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.20.1" />
<PackageVersion Include="Purview.Aspire.ResourceKit" Version="1.0.0-prerelease.25" />
<PackageVersion Include="Purview.Aspire.ResourceKit" Version="1.0.0-prerelease.30" />
<PackageVersion Include="Purview.SourceGeneratorFramework" Version="$(PurviewSGFrameworkVersion)" />
<PackageVersion
Include="Purview.SourceGeneratorFramework.Testing.TUnit"
Version="$(PurviewSGFrameworkVersion)"
/>
<PackageVersion Include="Purview.Telemetry.SourceGenerator" Version="5.0.0-prerelease.8" />
<PackageVersion Include="Purview.Telemetry.SourceGenerator" Version="5.0.0-prerelease.11" />
<PackageVersion Include="Purview.ValueObjects" Version="$(PurviewValueObjectsVersion)" />
<PackageVersion Include="Purview.ZodSharp" Version="$(PurviewZodSharpVersion)" />
<PackageVersion Include="Purview.ZodSharp.AspNetCore" Version="$(PurviewZodSharpVersion)" />
<PackageVersion Include="Purview.ZodSharp.SystemTextJson" Version="$(PurviewZodSharpVersion)" />
Expand Down
17 changes: 8 additions & 9 deletions docs/wiki/Source-Generator-Behaviors.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,11 @@ public sealed record BioUpdatedEvent

## Value-object conversion behavior

> The `[Scalar]` / `[ValueObject]` generator and analyzer are provided by the `Purview.ValueObjects` package,
> referenced transitively by `Purview.EventSourcing`. Value objects live in the `Purview.ValueObjects` and
> `Purview.ValueObjects.Serialization` namespaces (previously `Purview.EventSourcing.ValueObjects` and
> `Purview.EventSourcing.Serialization`).

- Generated mapping paths use `Create(...)` semantics for strict command-time conversion/validation.
- Contextual `Create(TValue, in ValueObjectContext<TAggregate>)` is used when available.
- Replay/hydration paths apply event payloads through generated `Apply(...)` logic.
Expand Down Expand Up @@ -302,15 +307,9 @@ Common aggregate diagnostic IDs:
- `EVENTSTORE021` event schema version must be positive
- `EVENTSTORE022` duplicate event schema version on aggregate

Common value-object diagnostic IDs:

- `EVENTSTORE101` value object must be partial
- `EVENTSTORE102` nested value objects unsupported
- `EVENTSTORE103` generic value objects unsupported
- `EVENTSTORE104` scalar property missing
- `EVENTSTORE107` strict mode relies on a generated `Create`
- `EVENTSTORE108` conflicting `[Scalar]` and `[ValueObject]` attributes
- `EVENTSTORE109` scalar value objects should be record structs
Value-object diagnostics are provided by the `Purview.ValueObjects` package (the `[Scalar]`/`[ValueObject]`
generator and analyzer moved out of this repository). They use the `VO1001`–`VO1008` ID range; see the
`Purview.ValueObjects` package documentation for the full list.

The analyzer and the generator share the same validation rules (the model builders are the single source
of truth). When validation fails, the generator skips generation entirely — it never emits an invalid
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"allowPrerelease": false
},
"msbuild-sdks": {
"Purview.BuildSdk": "1.0.0-prerelease.56"
"Purview.BuildSdk": "1.0.0-prerelease.57"
},
"test": {
"runner": "Microsoft.Testing.Platform"
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "purview-event-sourcing",
"version": "2.0.0-prerelease.34",
"version": "2.0.0-prerelease.35",
"license": "MIT",
"author": {
"name": "Kieron Lanning",
Expand All @@ -14,4 +14,4 @@
"type": "git",
"url": "git+https://github.com/purview-dev/event-sourcing.git"
}
}
}
71 changes: 7 additions & 64 deletions src/src/Benchmarks/SourceGeneratorPerformanceScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,6 @@ static class SourceGeneratorPerformanceScenarios
static () => new AggregateSourceGenerator(),
EditedSource: SharedStubs.All + Samples.AggregateMultiEdited
),
new(
"ScalarValueObject",
nameof(ValueObjectSourceGenerator),
SharedStubs.All + Samples.ScalarValueObject,
static () => new ValueObjectSourceGenerator()
),
new(
"ComplexValueObject",
nameof(ValueObjectSourceGenerator),
SharedStubs.All + Samples.ComplexValueObject,
static () => new ValueObjectSourceGenerator()
),
];

static class SharedStubs
Expand Down Expand Up @@ -120,13 +108,12 @@ namespace Purview.EventSourcing.Aggregates
public readonly record struct EventRecord(object Event, Purview.EventSourcing.Aggregates.Events.EventMetadata Metadata);
}

namespace Purview.EventSourcing.ValueObjects
namespace Purview.ValueObjects
{
public readonly record struct ValueObjectContext<TAggregate>(
TAggregate Aggregate,
public readonly record struct ValueObjectContext<TOwner>(
TOwner Owner,
string MemberName,
string? EventName = null,
string? CommandName = null
string? Reason = null
);

public interface IValueObject { }
Expand All @@ -140,7 +127,7 @@ public interface IScalarValueObject<TSelf, TValue> : IValueObject, System.ICompa
}
}

namespace Purview.EventSourcing.Serialization
namespace Purview.ValueObjects.Serialization
{
public enum ValueObjectDeserializationMode
{
Expand Down Expand Up @@ -202,12 +189,12 @@ public enum OrderStatusCode
Confirmed = 1
}

[Purview.EventSourcing.Serialization.Scalar]
[Purview.ValueObjects.Serialization.Scalar]
public readonly partial record struct OrderStatus
{
public OrderStatusCode Value { get; }
private OrderStatus(OrderStatusCode value) => Value = value;
public static OrderStatus Create(OrderStatusCode value, in Purview.EventSourcing.ValueObjects.ValueObjectContext<OrderAggregate> context) => new(value);
public static OrderStatus Create(OrderStatusCode value, in Purview.ValueObjects.ValueObjectContext<OrderAggregate> context) => new(value);
public static OrderStatus Hydrate(OrderStatusCode value) => new(value);
}

Expand Down Expand Up @@ -335,49 +322,5 @@ public partial class ShipmentAggregate : Purview.EventSourcing.Aggregates.Aggreg
}
}
""";

public const string ScalarValueObject = """
namespace Testing
{
[Purview.EventSourcing.Serialization.Scalar]
public readonly partial record struct EmailAddress
{
public string Value { get; }

private EmailAddress(string value) => Value = value;

static partial void OnNormalize(ref string value)
{
value = value?.Trim().ToLowerInvariant()!;
}

static partial void OnValidate(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new System.ArgumentException("Email address cannot be empty.", nameof(value));
}
}
}
""";

public const string ComplexValueObject = """
namespace Testing
{
[Purview.EventSourcing.Serialization.ValueObject]
public readonly partial record struct Money
{
public decimal Amount { get; }
public string Currency { get; }

private Money(decimal amount, string currency)
{
Amount = amount;
Currency = currency;
}

public static Money Hydrate(decimal amount, string currency) => new(amount, currency);
}
}
""";
}
}
1 change: 1 addition & 0 deletions src/src/EventSourcing/EventSourcing.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Purview.ValueObjects" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ tags:

Use this skill when defining value objects in `Purview.EventSourcing` with source-generator support.

> The `[Scalar]` / `[ValueObject]` generator, analyzer, and runtime contracts are provided by the
> `Purview.ValueObjects` package (a dependency of `Purview.EventSourcing`). Value objects use the
> `Purview.ValueObjects` and `Purview.ValueObjects.Serialization` namespaces. The rules below still apply
> to how value objects behave inside aggregates.

## Goals

- Model scalar, non-scalar, and contextual value objects with strong invariants.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Text.Json;
using System.Text.Json.Serialization;
using Purview.ValueObjects.Serialization;

namespace Purview.EventSourcing.Serialization;

Expand Down
74 changes: 0 additions & 74 deletions src/src/EventSourcing/Serialization/ScalarAttribute.cs

This file was deleted.

Loading
Loading