Skip to content
This repository has been archived by the owner on Jul 12, 2024. It is now read-only.

Commit

Permalink
refactor: store email outbox with marten
Browse files Browse the repository at this point in the history
  • Loading branch information
foxminchan committed Jun 1, 2024
1 parent 5f436c4 commit 3b93061
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 16 deletions.
6 changes: 4 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,15 @@
<!-- Web Utils -->
<PackageVersion Include="Htmx" Version="1.8.0" />
<PackageVersion Include="Htmx.TagHelpers" Version="1.8.0" />
<PackageVersion Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageVersion Include="WebEssentials.AspNetCore.PWA" Version="1.0.85" />
<PackageVersion Include="LigerShark.WebOptimizer.Core" Version="3.0.405" />
<PackageVersion Include="SmartComponents.AspNetCore" Version="0.1.0-preview10148" />
<PackageVersion Include="SmartComponents.Inference.OpenAI" Version="0.1.0-preview10148" />
<!-- Miscellaneous -->
<!-- Data -->
<PackageVersion Include="Marten" Version="7.18.0" />
<PackageVersion Include="Dapper" Version="2.1.35" />
<!-- Miscellaneous -->
<PackageVersion Include="Scrutor" Version="4.2.2" />
<PackageVersion Include="MediatR" Version="12.2.0" />
<PackageVersion Include="Nuke.Common" Version="8.0.0" />
Expand All @@ -100,7 +103,6 @@
<PackageVersion Include="DistributedLock.Redis" Version="1.0.3" />
<PackageVersion Include="Swashbuckle.AspNetCore" Version="6.6.2" />
<PackageVersion Include="StronglyTypedId" Version="1.0.0-beta08" />
<PackageVersion Include="Refit.HttpClientFactory" Version="7.0.0" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.9.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.25.1.91650" />
<PackageVersion Include="FluentValidation.AspNetCore" Version="11.3.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public async Task Handle(CreatedOrderEvent notification, CancellationToken cance
await redisService.HashRemoveAsync(nameof(Basket), notification.AccountId.ToString());

EmailMetadata metadata = new(
$"Thank you for your order {notification.Order.Id}!, Your order is being processed",
$"Thank you for your order. Order will be processed soon. Order ID: {notification.Order.Id}",
"Order Confirmation",
notification.Email);

Expand Down
15 changes: 10 additions & 5 deletions src/RookieShop.Application/Orders/Events/UpdatedOrderHandler.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using MediatR;
using RookieShop.Application.Orders.DTOs;
using RookieShop.Domain.Entities.OrderAggregator.Enums;
using RookieShop.Domain.Entities.OrderAggregator.Events;
using RookieShop.Infrastructure.Email.Smtp;
using RookieShop.Infrastructure.Email.Smtp.Abstractions;
Expand All @@ -10,11 +10,16 @@ public sealed class UpdatedOrderHandler(ISmtpService smtpService) : INotificatio
{
public async Task Handle(UpdatedOrderEvent notification, CancellationToken cancellationToken)
{
var order = notification.Order.ToOrderDto();

EmailMetadata metadata = new(
$"Your order {order.Id} has been updated to {order.OrderStatus}",
"Order Status ",
$"Your order status has been updated to {
notification.Order.OrderStatus switch {
OrderStatus.Pending => "Pending",
OrderStatus.Shipping => "Processing",
OrderStatus.Canceled => "Canceled",
OrderStatus.Completed => "Completed",
_ => "Unknown" }
}",
"Order Status",
notification.Order.Customer?.Email);

await smtpService.SendEmailAsync(metadata, cancellationToken);
Expand Down
7 changes: 7 additions & 0 deletions src/RookieShop.Infrastructure/Email/Extension.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Ardalis.GuardClauses;
using Marten;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
Expand Down Expand Up @@ -33,6 +34,10 @@ public static IHostApplicationBuilder AddEmail(this IHostApplicationBuilder buil

private static void ConfigureEmailService(this IHostApplicationBuilder builder)
{
var conn = builder.Configuration.GetConnectionString("shopdb");

Guard.Against.NullOrEmpty(conn);

builder.Services.AddResiliencePipeline(nameof(Smtp), resiliencePipelineBuilder => resiliencePipelineBuilder
.AddRetry(new()
{
Expand All @@ -43,6 +48,8 @@ private static void ConfigureEmailService(this IHostApplicationBuilder builder)
})
.AddTimeout(TimeSpan.FromSeconds(10)));

builder.Services.AddMarten(conn);

builder.Services.AddScoped<ISmtpService, SmtpService>();

builder.Services.Decorate<ISmtpService, SmtpOutboxDecorator>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace RookieShop.Infrastructure.Email.Smtp.Abstractions;

public sealed record EmailMetadata(
string Body,
string? Body,
string? Subject,
string? To,
string? Bcc = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

public sealed class EmailOutbox
{
public Guid Id { get; set; } = Guid.NewGuid();
public Guid Id { get; set; } = Guid.NewGuid();
public string? Body { get; set; }
public string? Subject { get; set; }
public string? To { get; set; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using Microsoft.Extensions.Logging;
using RookieShop.Infrastructure.Cache.Redis;
using Marten;
using Microsoft.Extensions.Logging;
using RookieShop.Infrastructure.Email.Smtp.Abstractions;

namespace RookieShop.Infrastructure.Email.Smtp.Decorator;

public sealed class SmtpOutboxDecorator(
ISmtpService smtpService,
ILogger<SmtpOutboxDecorator> logger,
IRedisService redisService) : ISmtpService
IDocumentSession session) : ISmtpService
{
public async Task SendEmailAsync(EmailMetadata emailMetadata, CancellationToken cancellationToken = default)
{
Expand All @@ -24,10 +24,24 @@ public async Task SendEmailAsync(EmailMetadata emailMetadata, CancellationToken
IsSent = false
};

await redisService.GetOrSetAsync(emailOutbox.Id.ToString(), () => emailOutbox, TimeSpan.FromDays(1));
session.Store(emailOutbox);

await smtpService.SendEmailAsync(emailMetadata, cancellationToken);
await session.SaveChangesAsync(cancellationToken);

await redisService.RemoveAsync(emailOutbox.Id.ToString());
try
{
await smtpService.SendEmailAsync(emailMetadata, cancellationToken);
emailOutbox.IsSent = true;
}
catch(Exception ex)
{
logger.LogError(ex, "[{Service}] Failed to send email to {To} with subject {Subject}",
nameof(SmtpOutboxDecorator), emailMetadata.To, emailMetadata.Subject);
}
finally
{
session.Update(emailOutbox);
await session.SaveChangesAsync(cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Marten" />
<PackageReference Include="Scrutor" />
<PackageReference Include="Stripe.net" />
<PackageReference Include="Quartz.Extensions.Hosting" />
Expand Down

0 comments on commit 3b93061

Please sign in to comment.