Skip to content

Commit

Permalink
Refactored all domain events into a shared library
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Apr 3, 2024
1 parent bdcff40 commit 23d3cce
Show file tree
Hide file tree
Showing 135 changed files with 1,818 additions and 1,583 deletions.
2 changes: 1 addition & 1 deletion docs/design-principles/0130-multitenancy.md
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ For SaaS products that wish to provision physical infrastructure to implement "I
Consider the following workflow:

1. A new customer signs up for the platform. They register a new user, and that will create a new `Personal` organization for them to use the product. This organization will have a billing subscription that gives them some [limited] access level to the product at this time (i.e., a trial).
2. At that time, or at some future time (like when they upgrade to a paid plan), a new event (e.g., `EndUsersDomain.Events.Registered`) can be subscribed to by adding a new `IEventNotificationRegistration` in one of the subdomains.
2. At that time, or at some future time (like when they upgrade to a paid plan), a new event (e.g., `Domain.Events.Shared.EndUsers.Registered`) can be subscribed to by adding a new `IEventNotificationRegistration` in one of the subdomains.
3. This event is then raised at runtime, which triggers an application (in some subdomain) to make some API call to some cloud-based process to provision some specific infrastructure (e.g., via queue message or direct via an API call to an Azure function or AWS Lambda - there are many integration options). Let's assume that this triggers Azure to create a new SQL database in a regional data center physically closer to where this specific customer is signing up.
4. Let's assume that this cloud provisioning process takes some time to complete (perhaps several minutes), and meanwhile, the customer is starting using the product and try it out for themselves (using their `Personal` organization, which we assume is using shared platform infrastructure at this time.
5. When the provisioning process is completed (a few minutes later), a new message [containing some data about the provisioning process] is created and dropped on the `provisioning` queue (in Azure or AWS).
Expand Down
11 changes: 6 additions & 5 deletions src/AncillaryDomain.UnitTests/EmailDeliverRootSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Domain.Common;
using Domain.Common.Identity;
using Domain.Common.ValueObjects;
using Domain.Events.Shared.Ancillary.EmailDelivery;
using Domain.Interfaces.Entities;
using Domain.Shared;
using FluentAssertions;
Expand Down Expand Up @@ -35,7 +36,7 @@ public void WhenCreate_ThenReturnsAssigned()
var result = EmailDeliveryRoot.Create(_recorder.Object, _idFactory.Object, messageId).Value;

result.MessageId.Should().Be(messageId);
result.Events.Last().Should().BeOfType<Events.EmailDelivery.Created>();
result.Events.Last().Should().BeOfType<Created>();
}

[Fact]
Expand Down Expand Up @@ -73,7 +74,7 @@ public void WhenSetEmailDetails_ThenDetailsAssigned()

result.Should().BeSuccess();
root.Recipient.Should().Be(recipient);
root.Events.Last().Should().BeOfType<Events.EmailDelivery.EmailDetailsChanged>();
root.Events.Last().Should().BeOfType<EmailDetailsChanged>();
}

[Fact]
Expand Down Expand Up @@ -103,7 +104,7 @@ public void WhenAttemptedDeliveryAndNotDelivered_ThenAddsAnAttempt()
result.Value.Should().BeFalse();
root.Attempts.Attempts.Count.Should().Be(1);
root.Attempts.Attempts[0].Should().BeNear(DateTime.UtcNow);
root.Events.Last().Should().BeOfType<Events.EmailDelivery.DeliveryAttempted>();
root.Events.Last().Should().BeOfType<DeliveryAttempted>();
}

[Fact]
Expand Down Expand Up @@ -141,7 +142,7 @@ public void WhenFailDelivery_ThenFails()
var result = root.FailedDelivery();

result.Should().BeSuccess();
root.Events.Last().Should().BeOfType<Events.EmailDelivery.DeliveryFailed>();
root.Events.Last().Should().BeOfType<DeliveryFailed>();
}

[Fact]
Expand Down Expand Up @@ -181,7 +182,7 @@ public void WhenSucceededDelivery_ThenDelivered()
result.Should().BeSuccess();
root.IsDelivered.Should().BeTrue();
root.Delivered.Should().BeNear(DateTime.UtcNow);
root.Events.Last().Should().BeOfType<Events.EmailDelivery.DeliverySucceeded>();
root.Events.Last().Should().BeOfType<DeliverySucceeded>();
}

private static QueuedMessageId CreateMessageId()
Expand Down
2 changes: 1 addition & 1 deletion src/AncillaryDomain/AncillaryDomain.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\Domain.Shared\Domain.Shared.csproj" />
<ProjectReference Include="..\Domain.Events.Shared\Domain.Events.Shared.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
5 changes: 3 additions & 2 deletions src/AncillaryDomain/AuditRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Domain.Common.Entities;
using Domain.Common.Identity;
using Domain.Common.ValueObjects;
using Domain.Events.Shared.Ancillary.Audits;
using Domain.Interfaces;
using Domain.Interfaces.Entities;
using Domain.Interfaces.ValueObjects;
Expand All @@ -16,7 +17,7 @@ public static Result<AuditRoot, Error> Create(IRecorder recorder, IIdentifierFac
TemplateArguments templateArguments)
{
var root = new AuditRoot(recorder, idFactory);
root.RaiseCreateEvent(AncillaryDomain.Events.Audits.Created.Create(root.Id, againstId, organizationId,
root.RaiseCreateEvent(AncillaryDomain.Events.Audits.Created(root.Id, againstId, organizationId,
auditCode,
messageTemplate, templateArguments));
return root;
Expand Down Expand Up @@ -62,7 +63,7 @@ protected override Result<Error> OnStateChanged(IDomainEvent @event, bool isReco
{
switch (@event)
{
case Events.Audits.Created created:
case Created created:
{
OrganizationId = created.OrganizationId.HasValue()
? created.OrganizationId.ToId()
Expand Down
21 changes: 11 additions & 10 deletions src/AncillaryDomain/EmailDeliveryRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Common.Extensions;
using Domain.Common.Entities;
using Domain.Common.Identity;
using Domain.Events.Shared.Ancillary.EmailDelivery;
using Domain.Interfaces;
using Domain.Interfaces.Entities;
using Domain.Interfaces.ValueObjects;
Expand All @@ -15,7 +16,7 @@ public static Result<EmailDeliveryRoot, Error> Create(IRecorder recorder, IIdent
QueuedMessageId messageId)
{
var root = new EmailDeliveryRoot(recorder, idFactory);
root.RaiseCreateEvent(AncillaryDomain.Events.EmailDelivery.Created.Create(root.Id, messageId));
root.RaiseCreateEvent(AncillaryDomain.Events.EmailDelivery.Created(root.Id, messageId));
return root;
}

Expand Down Expand Up @@ -62,7 +63,7 @@ protected override Result<Error> OnStateChanged(IDomainEvent @event, bool isReco
{
switch (@event)
{
case Events.EmailDelivery.Created created:
case Created created:
{
var messageId = QueuedMessageId.Create(created.MessageId);
if (!messageId.IsSuccessful)
Expand All @@ -74,7 +75,7 @@ protected override Result<Error> OnStateChanged(IDomainEvent @event, bool isReco
return Result.Ok;
}

case Events.EmailDelivery.EmailDetailsChanged changed:
case EmailDetailsChanged changed:
{
var emailAddress = EmailAddress.Create(changed.ToEmailAddress);
if (!emailAddress.IsSuccessful)
Expand All @@ -93,7 +94,7 @@ protected override Result<Error> OnStateChanged(IDomainEvent @event, bool isReco
return Result.Ok;
}

case Events.EmailDelivery.DeliveryAttempted changed:
case DeliveryAttempted changed:
{
var attempted = Attempts.Attempt(changed.When);
if (!attempted.IsSuccessful)
Expand All @@ -106,13 +107,13 @@ protected override Result<Error> OnStateChanged(IDomainEvent @event, bool isReco
return Result.Ok;
}

case Events.EmailDelivery.DeliveryFailed _:
case DeliveryFailed _:
{
Recorder.TraceDebug(null, "EmailDelivery {Id} failed a delivery", Id);
return Result.Ok;
}

case Events.EmailDelivery.DeliverySucceeded changed:
case DeliverySucceeded changed:
{
Delivered = changed.When;
Recorder.TraceDebug(null, "EmailDelivery {Id} succeeded delivery", Id);
Expand All @@ -132,7 +133,7 @@ public Result<bool, Error> AttemptDelivery()
}

var when = DateTime.UtcNow;
var attempted = RaiseChangeEvent(AncillaryDomain.Events.EmailDelivery.DeliveryAttempted.Create(Id, when));
var attempted = RaiseChangeEvent(AncillaryDomain.Events.EmailDelivery.DeliveryAttempted(Id, when));
if (!attempted.IsSuccessful)
{
return attempted.Error;
Expand All @@ -155,7 +156,7 @@ public Result<Error> FailedDelivery()

var when = DateTime.UtcNow;
return RaiseChangeEvent(
AncillaryDomain.Events.EmailDelivery.DeliveryFailed.Create(Id, when));
AncillaryDomain.Events.EmailDelivery.DeliveryFailed(Id, when));
}

public Result<Error> SetEmailDetails(string? subject, string? body, EmailRecipient recipient)
Expand All @@ -173,7 +174,7 @@ public Result<Error> SetEmailDetails(string? subject, string? body, EmailRecipie
}

return RaiseChangeEvent(
AncillaryDomain.Events.EmailDelivery.EmailDetailsChanged.Create(Id, subject!, body!, recipient));
AncillaryDomain.Events.EmailDelivery.EmailDetailsChanged(Id, subject!, body!, recipient));
}

public Result<Error> SucceededDelivery(Optional<string> transactionId)
Expand All @@ -190,7 +191,7 @@ public Result<Error> SucceededDelivery(Optional<string> transactionId)

var when = DateTime.UtcNow;
return RaiseChangeEvent(
AncillaryDomain.Events.EmailDelivery.DeliverySucceeded.Create(Id, when));
AncillaryDomain.Events.EmailDelivery.DeliverySucceeded(Id, when));
}

#if TESTINGONLY
Expand Down
165 changes: 50 additions & 115 deletions src/AncillaryDomain/Events.cs
Original file line number Diff line number Diff line change
@@ -1,152 +1,87 @@
using Common;
using Domain.Common.ValueObjects;
using Domain.Interfaces.Entities;
using Domain.Events.Shared.Ancillary.EmailDelivery;
using Created = Domain.Events.Shared.Ancillary.Audits.Created;

namespace AncillaryDomain;

public static class Events
{
public static class EmailDelivery
{
public sealed class Created : IDomainEvent
public static Domain.Events.Shared.Ancillary.EmailDelivery.Created Created(Identifier id,
QueuedMessageId messageId)
{
public static Created Create(Identifier id, QueuedMessageId messageId)
return new Domain.Events.Shared.Ancillary.EmailDelivery.Created
{
return new Created
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
MessageId = messageId
};
}

public required string MessageId { get; set; }

public required string RootId { get; set; }

public required DateTime OccurredUtc { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
MessageId = messageId
};
}

public sealed class EmailDetailsChanged : IDomainEvent
public static DeliveryAttempted DeliveryAttempted(Identifier id, DateTime when)
{
public static EmailDetailsChanged Create(Identifier id, string subject, string body, EmailRecipient to)
return new DeliveryAttempted
{
return new EmailDetailsChanged
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
Subject = subject,
Body = body,
ToEmailAddress = to.EmailAddress,
ToDisplayName = to.DisplayName
};
}

public required string Body { get; set; }

public required string Subject { get; set; }

public required string ToDisplayName { get; set; }

public required string ToEmailAddress { get; set; }

public required string RootId { get; set; }

public required DateTime OccurredUtc { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public sealed class DeliveryAttempted : IDomainEvent
public static DeliveryFailed DeliveryFailed(Identifier id, DateTime when)
{
public static DeliveryAttempted Create(Identifier id, DateTime when)
return new DeliveryFailed
{
return new DeliveryAttempted
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public required DateTime When { get; set; }

public required DateTime OccurredUtc { get; set; }

public required string RootId { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public sealed class DeliveryFailed : IDomainEvent
public static DeliverySucceeded DeliverySucceeded(Identifier id, DateTime when)
{
public static DeliveryFailed Create(Identifier id, DateTime when)
return new DeliverySucceeded
{
return new DeliveryFailed
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public required DateTime When { get; set; }

public required DateTime OccurredUtc { get; set; }

public required string RootId { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public sealed class DeliverySucceeded : IDomainEvent
public static EmailDetailsChanged EmailDetailsChanged(Identifier id, string subject, string body,
EmailRecipient to)
{
public static DeliverySucceeded Create(Identifier id, DateTime when)
return new EmailDetailsChanged
{
return new DeliverySucceeded
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
When = when
};
}

public required DateTime When { get; set; }

public required DateTime OccurredUtc { get; set; }

public required string RootId { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
Subject = subject,
Body = body,
ToEmailAddress = to.EmailAddress,
ToDisplayName = to.DisplayName
};
}
}

public static class Audits
{
public sealed class Created : IDomainEvent
public static Created Created(Identifier id, Identifier againstId, Optional<Identifier> organizationId,
string auditCode, Optional<string> messageTemplate, TemplateArguments templateArguments)
{
public static Created Create(Identifier id, Identifier againstId, Optional<Identifier> organizationId,
string auditCode, Optional<string> messageTemplate, TemplateArguments templateArguments)
return new Created
{
return new Created
{
RootId = id,
OccurredUtc = DateTime.UtcNow,
OrganizationId = organizationId.HasValue
? organizationId.Value.Text
: null,
AgainstId = againstId,
AuditCode = auditCode,
MessageTemplate = messageTemplate.ValueOrDefault ?? string.Empty,
TemplateArguments = templateArguments.Items
};
}

public required string AgainstId { get; set; }

public required string AuditCode { get; set; }

public required string MessageTemplate { get; set; }

public string? OrganizationId { get; set; }

public required List<string> TemplateArguments { get; set; }

public required string RootId { get; set; }

public required DateTime OccurredUtc { get; set; }
RootId = id,
OccurredUtc = DateTime.UtcNow,
OrganizationId = organizationId.HasValue
? organizationId.Value.Text
: null,
AgainstId = againstId,
AuditCode = auditCode,
MessageTemplate = messageTemplate.ValueOrDefault ?? string.Empty,
TemplateArguments = templateArguments.Items
};
}
}
}
Loading

0 comments on commit 23d3cce

Please sign in to comment.