mirror of
https://github.com/farcasclaudiu/PracticeCalendar.git
synced 2026-06-22 07:01:16 +03:00
refactoring and integration tests
This commit is contained in:
@@ -27,8 +27,6 @@ namespace PracticeCalendar
|
||||
//inject infrastructure
|
||||
builder.Services.AddInfrastructureServices(builder.Configuration);
|
||||
|
||||
//mapster
|
||||
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Default": "Debug",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Default": "Debug",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
using MediatR;
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Events;
|
||||
using PracticeCalendar.Domain.Interfaces;
|
||||
|
||||
namespace PracticeCalendar.Application.PracticeEvents.Events
|
||||
{
|
||||
public class AttendeeAddedEventNotification : INotificationHandler<DomainEventNotification<AttendeeAddedEvent>>
|
||||
{
|
||||
private readonly IEmailSender emailSender;
|
||||
|
||||
public AttendeeAddedEventNotification(IEmailSender emailSender)
|
||||
{
|
||||
this.emailSender = emailSender;
|
||||
}
|
||||
|
||||
public async Task Handle(DomainEventNotification<AttendeeAddedEvent> notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var sendTo = notification.DomainEvent.AddedAtendee.EmailAddress;
|
||||
await emailSender.SendEmailAsync(sendTo, "system",
|
||||
"You have been added to the event", "Confirmed you have been added to the event.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,6 @@
|
||||
public class DomainEventBase
|
||||
{
|
||||
public DateTime EventDate { get; protected set; } = DateTime.UtcNow;
|
||||
public bool IsPublished { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using MediatR;
|
||||
|
||||
namespace PracticeCalendar.Domain.Common
|
||||
{
|
||||
public class DomainEventNotification<TDomainEvent> : INotification where TDomainEvent : DomainEventBase
|
||||
{
|
||||
public TDomainEvent DomainEvent { get; }
|
||||
|
||||
public DomainEventNotification(TDomainEvent domainEvent)
|
||||
{
|
||||
DomainEvent = domainEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace PracticeCalendar.Domain.Common.Interfaces
|
||||
{
|
||||
public interface IDomainEventService
|
||||
{
|
||||
Task Publish(DomainEventBase domainEvent);
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,9 @@ namespace PracticeCalendar.Domain.Entities
|
||||
{
|
||||
this.Title = title;
|
||||
this.Description = description;
|
||||
|
||||
var titleDescUpdatedEvent = new EventUpdateTitleAndDescriptionEvent(this);
|
||||
base.RegisterDomainEvent(titleDescUpdatedEvent);
|
||||
}
|
||||
|
||||
public void AttendeeAcceptEvent(int attendeeId)
|
||||
@@ -49,6 +52,9 @@ namespace PracticeCalendar.Domain.Entities
|
||||
if (attendee == null)
|
||||
throw new InvalidAttendeeException(attendeeId);
|
||||
attendee.SetIsAttending(true);
|
||||
|
||||
var attendeeAcceptedEvent = new AttendeeAcceptedEvent(this, attendee);
|
||||
base.RegisterDomainEvent(attendeeAcceptedEvent);
|
||||
}
|
||||
|
||||
public void AttendeeDeclineEvent(int attendeeId)
|
||||
@@ -57,6 +63,9 @@ namespace PracticeCalendar.Domain.Entities
|
||||
if (attendee == null)
|
||||
throw new InvalidAttendeeException(attendeeId);
|
||||
attendee.SetIsAttending(false);
|
||||
|
||||
var attendeeDeclinedEvent = new AttendeeDeclinedEvent(this, attendee);
|
||||
base.RegisterDomainEvent(attendeeDeclinedEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Entities;
|
||||
|
||||
namespace PracticeCalendar.Domain.Events
|
||||
{
|
||||
public sealed class AttendeeAcceptedEvent : DomainEventBase
|
||||
{
|
||||
public AttendeeAcceptedEvent(PracticeEvent practiceEvent, Attendee attendee)
|
||||
{
|
||||
PracticeEvent = practiceEvent;
|
||||
Attendee = attendee;
|
||||
}
|
||||
|
||||
public PracticeEvent PracticeEvent { get; }
|
||||
public Attendee Attendee { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Entities;
|
||||
|
||||
namespace PracticeCalendar.Domain.Events
|
||||
{
|
||||
public sealed class AttendeeDeclinedEvent : DomainEventBase
|
||||
{
|
||||
public AttendeeDeclinedEvent(PracticeEvent practiceEvent, Attendee attendee)
|
||||
{
|
||||
PracticeEvent = practiceEvent;
|
||||
Attendee = attendee;
|
||||
}
|
||||
|
||||
public PracticeEvent PracticeEvent { get; }
|
||||
public Attendee Attendee { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Entities;
|
||||
|
||||
namespace PracticeCalendar.Domain.Events
|
||||
{
|
||||
public sealed class EventUpdateTitleAndDescriptionEvent : DomainEventBase
|
||||
{
|
||||
public EventUpdateTitleAndDescriptionEvent(PracticeEvent practiceEvent)
|
||||
{
|
||||
PracticeEvent = practiceEvent;
|
||||
}
|
||||
|
||||
public PracticeEvent PracticeEvent { get; }
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using PracticeCalendar.Domain.Common.Interfaces;
|
||||
using PracticeCalendar.Domain.Interfaces;
|
||||
using PracticeCalendar.Infrastructure.Notification;
|
||||
using PracticeCalendar.Infrastructure.Persistence;
|
||||
using PracticeCalendar.Infrastructure.Services;
|
||||
|
||||
namespace PracticeCalendar.Infrastructure
|
||||
{
|
||||
@@ -17,6 +18,7 @@ namespace PracticeCalendar.Infrastructure
|
||||
|
||||
services.AddDbContext(connectionString);
|
||||
services.AddTransient<IEmailSender, FileEmailSender>();
|
||||
services.AddTransient<IDomainEventService, DomainEventService>();
|
||||
|
||||
services.AddScoped(typeof(IRepository<>), typeof(EfRepository<>));
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Common.Interfaces;
|
||||
using PracticeCalendar.Domain.Entities;
|
||||
using System.Reflection;
|
||||
|
||||
@@ -6,9 +8,15 @@ namespace PracticeCalendar.Infrastructure.Persistence
|
||||
{
|
||||
public class ApplicationDbContext : DbContext
|
||||
{
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
|
||||
private readonly IDomainEventService domainEventService;
|
||||
|
||||
public DbSet<Attendee> Atendees => Set<Attendee>();
|
||||
public DbSet<PracticeEvent> PracticeEvents => Set<PracticeEvent>();
|
||||
|
||||
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IDomainEventService domainEventService)
|
||||
: base(options)
|
||||
{
|
||||
this.domainEventService = domainEventService;
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
@@ -17,7 +25,30 @@ namespace PracticeCalendar.Infrastructure.Persistence
|
||||
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
|
||||
}
|
||||
|
||||
public DbSet<Attendee> Atendees => Set<Attendee>();
|
||||
public DbSet<PracticeEvent> PracticeEvents => Set<PracticeEvent>();
|
||||
|
||||
|
||||
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
|
||||
{
|
||||
var events = ChangeTracker.Entries<EntityBase>()
|
||||
.Select(x => x.Entity.DomainEvents)
|
||||
.SelectMany(x => x)
|
||||
.Where(domainEvent => !domainEvent.IsPublished)
|
||||
.ToArray();
|
||||
|
||||
var result = await base.SaveChangesAsync(cancellationToken);
|
||||
|
||||
await DispatchEvents(events);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private async Task DispatchEvents(DomainEventBase[] events)
|
||||
{
|
||||
foreach (var @event in events)
|
||||
{
|
||||
@event.IsPublished = true;
|
||||
await domainEventService.Publish(@event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using PracticeCalendar.Domain.Common;
|
||||
using PracticeCalendar.Domain.Common.Interfaces;
|
||||
|
||||
namespace PracticeCalendar.Infrastructure.Services
|
||||
{
|
||||
public class DomainEventService : IDomainEventService
|
||||
{
|
||||
private readonly ILogger<DomainEventService> logger;
|
||||
private readonly IPublisher mediator;
|
||||
|
||||
public DomainEventService(ILogger<DomainEventService> logger, IPublisher mediator)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.mediator = mediator;
|
||||
}
|
||||
public async Task Publish(DomainEventBase domainEvent)
|
||||
{
|
||||
logger.LogInformation("Publishing domain event. Event - {event}", domainEvent.GetType().Name);
|
||||
await mediator.Publish(GetNotificationCorrespondingToDomainEvent(domainEvent));
|
||||
}
|
||||
|
||||
private INotification GetNotificationCorrespondingToDomainEvent(DomainEventBase domainEvent)
|
||||
{
|
||||
return (INotification)Activator.CreateInstance(
|
||||
typeof(DomainEventNotification<>).MakeGenericType(domainEvent.GetType()), domainEvent)!;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user