diff --git a/src/Koogle.Domain/Interfaces/IEmailService.cs b/src/Koogle.Domain/Interfaces/IEmailService.cs
new file mode 100644
index 0000000..bd62575
--- /dev/null
+++ b/src/Koogle.Domain/Interfaces/IEmailService.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Koogle.Domain.Interfaces
+{
+ ///
+ /// Service interface for email notifications.
+ ///
+ public interface IEmailService
+ {
+ ///
+ /// Sends notification to club admins about a new membership request.
+ ///
+ /// The club receiving the membership request.
+ /// Display name of the requesting user.
+ /// Email of the requesting user.
+ /// Cancellation token.
+ Task SendMembershipRequestNotificationAsync(
+ Guid clubId,
+ string userDisplayName,
+ string userEmail,
+ CancellationToken ct = default);
+
+ ///
+ /// Sends notification to user that their membership was approved.
+ ///
+ /// Email of the approved user.
+ /// Name of the club.
+ /// Cancellation token.
+ Task SendMembershipApprovedAsync(
+ string userEmail,
+ string clubName,
+ CancellationToken ct = default);
+
+ ///
+ /// Sends notification to user that their membership was rejected.
+ ///
+ /// Email of the rejected user.
+ /// Name of the club.
+ /// Optional rejection reason.
+ /// Cancellation token.
+ Task SendMembershipRejectedAsync(
+ string userEmail,
+ string clubName,
+ string? reason,
+ CancellationToken ct = default);
+ }
+}
diff --git a/src/Koogle.Infrastructure/DependencyInjection.cs b/src/Koogle.Infrastructure/DependencyInjection.cs
index 3fd9522..67fb679 100644
--- a/src/Koogle.Infrastructure/DependencyInjection.cs
+++ b/src/Koogle.Infrastructure/DependencyInjection.cs
@@ -4,6 +4,7 @@ using Koogle.Infrastructure.Data;
using Koogle.Infrastructure.Identity;
using Koogle.Infrastructure.Repositories;
using Koogle.Infrastructure.Security;
+using Koogle.Infrastructure.Services;
using KoogleApp.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
@@ -84,6 +85,9 @@ public static class DependencyInjection
services.AddScoped();
services.AddScoped();
+ // Services
+ services.AddScoped();
+
services.AddCascadingAuthenticationState();
return services;
diff --git a/src/Koogle.Infrastructure/Services/StubEmailService.cs b/src/Koogle.Infrastructure/Services/StubEmailService.cs
new file mode 100644
index 0000000..1f6c4c2
--- /dev/null
+++ b/src/Koogle.Infrastructure/Services/StubEmailService.cs
@@ -0,0 +1,80 @@
+using Koogle.Domain.Interfaces;
+using Microsoft.Extensions.Logging;
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Koogle.Infrastructure.Services;
+
+///
+/// Stub email service that logs instead of sending emails.
+/// TODO: Replace with actual SMTP implementation.
+///
+public class StubEmailService : IEmailService
+{
+ private readonly ILogger _logger;
+
+ ///
+ /// Creates a new StubEmailService instance.
+ ///
+ public StubEmailService(ILogger logger)
+ {
+ _logger = logger;
+ }
+
+ ///
+ public Task SendMembershipRequestNotificationAsync(
+ Guid clubId,
+ string userDisplayName,
+ string userEmail,
+ CancellationToken ct = default)
+ {
+ _logger.LogInformation(
+ "[EMAIL STUB] Membership request notification - ClubId: {ClubId}, User: {UserDisplayName} ({UserEmail})",
+ clubId, userDisplayName, userEmail);
+
+ // TODO: Implement actual email sending via SMTP
+ // - Get club admin emails from database
+ // - Build notification template
+ // - Send via configured SMTP provider
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ public Task SendMembershipApprovedAsync(
+ string userEmail,
+ string clubName,
+ CancellationToken ct = default)
+ {
+ _logger.LogInformation(
+ "[EMAIL STUB] Membership approved - To: {UserEmail}, Club: {ClubName}",
+ userEmail, clubName);
+
+ // TODO: Implement actual email sending via SMTP
+ // - Build approval notification template
+ // - Include link to dashboard
+ // - Send via configured SMTP provider
+
+ return Task.CompletedTask;
+ }
+
+ ///
+ public Task SendMembershipRejectedAsync(
+ string userEmail,
+ string clubName,
+ string? reason,
+ CancellationToken ct = default)
+ {
+ _logger.LogInformation(
+ "[EMAIL STUB] Membership rejected - To: {UserEmail}, Club: {ClubName}, Reason: {Reason}",
+ userEmail, clubName, reason ?? "(no reason provided)");
+
+ // TODO: Implement actual email sending via SMTP
+ // - Build rejection notification template
+ // - Include reason if provided
+ // - Send via configured SMTP provider
+
+ return Task.CompletedTask;
+ }
+}