refactoring and integration tests

This commit is contained in:
2022-10-10 04:02:46 +03:00
parent 28a6981001
commit ec54d2c255
15 changed files with 172 additions and 7 deletions
-2
View File
@@ -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 -1
View File
@@ -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)!;
}
}
}