Created
October 17, 2024 23:39
-
-
Save tomohisa/2d8c80b3a21b6bccc6b0f3607631b019 to your computer and use it in GitHub Desktop.
Command Idea with Injections
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using ResultBoxes; | |
namespace OpenAttendanceManagement.Common.EventSourcing; | |
public interface IAggregatePayload; | |
public record EmptyAggregatePayload : IAggregatePayload; | |
public record Aggregate<TAggregatePayload>(Guid AggregateId, int Version, TAggregatePayload Payload) | |
where TAggregatePayload : IAggregatePayload; | |
public interface IAggregateProjector | |
{ | |
public static abstract Func<IAggregatePayload, IEvent, IAggregatePayload> Projector(); | |
} | |
public interface IEventPayload; | |
public interface IEvent | |
{ | |
public IEventPayload GetPayload(); | |
} | |
public record Event<TPayload>(Guid AggregateId, int Version, TPayload Payload) : IEvent where TPayload : IEventPayload | |
{ | |
public IEventPayload GetPayload() => Payload; | |
} | |
public record UnconfirmedUser(string Name, string Email) : IAggregatePayload; | |
public record ConfirmedUser(string Name, string Email) : IAggregatePayload; | |
public record UserRegistered(string Name, string Email) : IEventPayload; | |
public record UserConfirmed : IEventPayload; | |
public record UserUnconfirmed : IEventPayload; | |
public class UserProjector : IAggregateProjector | |
{ | |
public static Func<IAggregatePayload, IEvent, IAggregatePayload> Projector() => | |
(payload, @event) => payload switch | |
{ | |
EmptyAggregatePayload => @event.GetPayload() switch | |
{ | |
UserRegistered registered => new UnconfirmedUser(registered.Name, registered.Email), | |
_ => payload | |
}, | |
UnconfirmedUser unconfirmedUser => @event.GetPayload() switch | |
{ | |
UserConfirmed => new ConfirmedUser(unconfirmedUser.Name, unconfirmedUser.Email), | |
_ => payload | |
}, | |
ConfirmedUser confirmedUser => @event.GetPayload() switch | |
{ | |
UserUnconfirmed => new UnconfirmedUser(confirmedUser.Name, confirmedUser.Email), | |
_ => payload | |
} | |
}; | |
} | |
public record PartitionKeys(Guid AggregateId, string Group, string RootPartitionKey) | |
{ | |
public static PartitionKeys Generate(string group = "default", string rootPartitionKey = "default") => | |
new(Guid.NewGuid(), group, rootPartitionKey); | |
public static PartitionKeys Existing( | |
Guid aggregateId, | |
string group = "default", | |
string rootPartitionKey = "default") => | |
new(aggregateId, group, rootPartitionKey); | |
} | |
public record EventOrNone(IEventPayload? EventPayload, bool HasEvent) | |
{ | |
public static EventOrNone Empty => new(default, false); | |
public static ResultBox<EventOrNone> None => Empty; | |
public static EventOrNone FromValue(IEventPayload value) => new(value, true); | |
public static ResultBox<EventOrNone> Event(IEventPayload value) => ResultBox.FromValue(FromValue(value)); | |
public IEventPayload GetValue() => HasEvent && EventPayload is not null | |
? EventPayload | |
: throw new ResultsInvalidOperationException("no value"); | |
public static implicit operator EventOrNone(UnitValue value) => Empty; | |
} | |
public interface IPureCommandCommon<TCommand, TProjector, TAggregatePayload> | |
where TCommand : IPureCommandCommon<TCommand, TProjector, TAggregatePayload>, IEquatable<TCommand> | |
where TProjector : IAggregateProjector | |
where TAggregatePayload : IAggregatePayload; | |
public interface | |
IPureCommand<TCommand, TProjector, TAggregatePayload> : IPureCommandCommon<TCommand, TProjector, TAggregatePayload> | |
where TCommand : IPureCommand<TCommand, TProjector, TAggregatePayload>, IEquatable<TCommand> | |
where TProjector : IAggregateProjector | |
where TAggregatePayload : IAggregatePayload | |
{ | |
public static abstract PartitionKeys SpecifyPartitionKeys(TCommand input); | |
public static abstract ResultBox<EventOrNone> Handle(TCommand input, Func<Aggregate<TAggregatePayload>> stateFunc); | |
} | |
public interface | |
IPureCommandWithInjection<TCommand, TProjector, TAggregatePayload, TInjections> : IPureCommandCommon<TCommand, | |
TProjector, | |
TAggregatePayload> | |
where TCommand : IPureCommandWithInjection<TCommand, TProjector, TAggregatePayload, TInjections>, | |
IEquatable<TCommand> | |
where TProjector : IAggregateProjector | |
where TAggregatePayload : IAggregatePayload | |
{ | |
public static abstract PartitionKeys SpecifyPartitionKeys(TCommand input); | |
public static abstract ResultBox<EventOrNone> Handle( | |
TCommand input, | |
Func<Aggregate<TAggregatePayload>> stateFunc, | |
TInjections injections); | |
} | |
public record RegisterClient1(string Name, string Email) | |
: IPureCommand<RegisterClient1, UserProjector, EmptyAggregatePayload> | |
{ | |
public static PartitionKeys SpecifyPartitionKeys(RegisterClient1 input) => PartitionKeys.Generate(); | |
public static ResultBox<EventOrNone> Handle( | |
RegisterClient1 input, | |
Func<Aggregate<EmptyAggregatePayload>> stateFunc) => | |
EventOrNone.Event(new UserRegistered(input.Name, input.Email)); | |
} | |
public record RegisterClientWithInjection(string Name, string Email) | |
: IPureCommandWithInjection<RegisterClientWithInjection, UserProjector, EmptyAggregatePayload, | |
RegisterClientWithInjection.Injections> | |
{ | |
public static PartitionKeys SpecifyPartitionKeys(RegisterClientWithInjection input) => PartitionKeys.Generate(); | |
public static ResultBox<EventOrNone> Handle( | |
RegisterClientWithInjection input, | |
Func<Aggregate<EmptyAggregatePayload>> stateFunc, | |
Injections injections) => injections.EmailAlreadyExists(input.Email) | |
? EventOrNone.None | |
: EventOrNone.Event(new UserRegistered(input.Name, input.Email)); | |
public class Injections | |
{ | |
public Func<string, bool> EmailAlreadyExists { get; init; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment