Skip to content

Instantly share code, notes, and snippets.

@tomohisa
Created October 17, 2024 23:39
Show Gist options
  • Save tomohisa/2d8c80b3a21b6bccc6b0f3607631b019 to your computer and use it in GitHub Desktop.
Save tomohisa/2d8c80b3a21b6bccc6b0f3607631b019 to your computer and use it in GitHub Desktop.
Command Idea with Injections
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