Merge pull request #251 from meysamhadeli/refactor/refactor-core-domain

refactor: Refactor core domain in building-blocks
This commit is contained in:
Meysam Hadeli 2023-05-08 17:17:52 +03:30 committed by GitHub
commit c93bd2902a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 257 additions and 126 deletions

View File

@ -16,11 +16,24 @@ services:
command: command:
- "postgres" - "postgres"
- "-c" - "-c"
- "wal_level=logical" - "wal_level=logical"
networks: networks:
- booking - booking
#######################################################
# SqlServer
#######################################################
# sql:
# container_name: sql
# image: mcr.microsoft.com/mssql/server
# ports:
# - "1433:1433"
# environment:
# SA_PASSWORD: "Password@1234"
# ACCEPT_EULA: "Y"
####################################################### #######################################################
# Rabbitmq # Rabbitmq
####################################################### #######################################################

View File

@ -30,10 +30,24 @@ services:
command: command:
- "postgres" - "postgres"
- "-c" - "-c"
- "wal_level=logical" - "wal_level=logical"
networks: networks:
- booking - booking
#######################################################
# SqlServer
#######################################################
# sql:
# container_name: sql
# image: mcr.microsoft.com/mssql/server
# ports:
# - "1433:1433"
# environment:
# SA_PASSWORD: "Password@1234"
# ACCEPT_EULA: "Y"
####################################################### #######################################################
# Jaeger # Jaeger
####################################################### #######################################################
@ -141,8 +155,6 @@ services:
ports: ports:
- 6379:6379 - 6379:6379
networks: networks:
booking: booking:

View File

@ -77,6 +77,7 @@
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" /> <PackageReference Include="Serilog.Sinks.Seq" Version="5.2.2" />
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" /> <PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" /> <PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.3" />
<PackageReference Include="Sieve" Version="2.5.5" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.5.0" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.5.0" />
@ -99,6 +100,8 @@
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="6.2.2" /> <PackageReference Include="Duende.IdentityServer.EntityFramework" Version="6.2.2" />
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.2.2" /> <PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="6.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.2" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.2" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
<PackageReference Include="Testcontainers" Version="3.0.0" /> <PackageReference Include="Testcontainers" Version="3.0.0" />
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.0.0" /> <PackageReference Include="Testcontainers.EventStoreDb" Version="3.0.0" />
<PackageReference Include="Testcontainers.MongoDb" Version="3.0.0" /> <PackageReference Include="Testcontainers.MongoDb" Version="3.0.0" />
@ -141,10 +144,9 @@
<PackageReference Include="Google.Protobuf" Version="3.21.12" /> <PackageReference Include="Google.Protobuf" Version="3.21.12" />
<PackageReference Include="Grpc.Net.Client" Version="2.51.0" /> <PackageReference Include="Grpc.Net.Client" Version="2.51.0" />
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.51.0" /> <PackageReference Include="Grpc.Net.ClientFactory" Version="2.51.0" />
<PackageReference Update="AsyncFixer" Version="1.6.0" />
<PackageReference Update="Meziantou.Analyzer" Version="1.0.758" /> <PackageReference Update="Meziantou.Analyzer" Version="1.0.758" />
<PackageReference Remove="Microsoft.VisualStudio.Threading.Analyzers" /> <PackageReference Update="AsyncFixer" Version="1.6.0" />
<PackageReference Update="Microsoft.VisualStudio.Threading.Analyzers" Version="17.4.27" />
<PackageReference Update="Roslynator.Analyzers" Version="4.2.0" /> <PackageReference Update="Roslynator.Analyzers" Version="4.2.0" />
<PackageReference Update="Roslynator.CodeAnalysis.Analyzers" Version="4.2.0" /> <PackageReference Update="Roslynator.CodeAnalysis.Analyzers" Version="4.2.0" />
<PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.2.0" /> <PackageReference Update="Roslynator.Formatting.Analyzers" Version="4.2.0" />
@ -154,6 +156,7 @@
<ItemGroup> <ItemGroup>
<Folder Include="Contracts" /> <Folder Include="Contracts" />
<Folder Include="Core\Pagination" />
<Folder Include="EventStoreDB\BackgroundWorkers" /> <Folder Include="EventStoreDB\BackgroundWorkers" />
<Folder Include="PersistMessageProcessor\Data\Configurations" /> <Folder Include="PersistMessageProcessor\Data\Configurations" />
<Folder Include="PersistMessageProcessor\Data\Migrations" /> <Folder Include="PersistMessageProcessor\Data\Migrations" />

View File

@ -2,9 +2,7 @@
namespace BuildingBlocks.Core.Model; namespace BuildingBlocks.Core.Model;
public abstract record Aggregate : Aggregate<long>; public abstract record Aggregate<TId> : Entity<TId>, IAggregate<TId>
public abstract record Aggregate<TId> : Audit, IAggregate<TId>
{ {
private readonly List<IDomainEvent> _domainEvents = new(); private readonly List<IDomainEvent> _domainEvents = new();
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly(); public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
@ -22,8 +20,4 @@ public abstract record Aggregate<TId> : Audit, IAggregate<TId>
return dequeuedEvents; return dequeuedEvents;
} }
public long Version { get; set; }
public required TId Id { get; set; }
} }

View File

@ -1,10 +1,12 @@
namespace BuildingBlocks.Core.Model; namespace BuildingBlocks.Core.Model;
public interface IAudit public abstract record Entity<T> : IEntity<T>
{ {
public T Id { get; set; }
public DateTime? CreatedAt { get; set; } public DateTime? CreatedAt { get; set; }
public long? CreatedBy { get; set; } public long? CreatedBy { get; set; }
public DateTime? LastModified { get; set; } public DateTime? LastModified { get; set; }
public long? LastModifiedBy { get; set; } public long? LastModifiedBy { get; set; }
public bool IsDeleted { get; set; } public bool IsDeleted { get; set; }
public long Version { get; set; }
} }

View File

@ -2,18 +2,12 @@
namespace BuildingBlocks.Core.Model; namespace BuildingBlocks.Core.Model;
public interface IAggregate : IAudit, IVersion public interface IAggregate<T> : IAggregate, IEntity<T>
{
}
public interface IAggregate : IEntity
{ {
IReadOnlyList<IDomainEvent> DomainEvents { get; } IReadOnlyList<IDomainEvent> DomainEvents { get; }
IEvent[] ClearDomainEvents(); IEvent[] ClearDomainEvents();
} }
public interface IAggregate<out T> : IAggregate
{
T Id { get; }
}
public interface IVersion
{
long Version { get; set; }
}

View File

@ -1,6 +1,11 @@
namespace BuildingBlocks.Core.Model; namespace BuildingBlocks.Core.Model;
public abstract record Audit : IAudit public interface IEntity<T> : IEntity
{
public T Id { get; set; }
}
public interface IEntity: IVersion
{ {
public DateTime? CreatedAt { get; set; } public DateTime? CreatedAt { get; set; }
public long? CreatedBy { get; set; } public long? CreatedBy { get; set; }

View File

@ -0,0 +1,6 @@
namespace BuildingBlocks.Core.Model;
public interface IVersion
{
long Version { get; set; }
}

View File

@ -0,0 +1,36 @@
namespace BuildingBlocks.Core.Pagination;
using Sieve.Models;
using Sieve.Services;
public static class Extensions
{
public static async Task<IPageList<TEntity>> ApplyPagingAsync<TEntity>(
this IQueryable<TEntity> queryable,
IPageRequest pageRequest,
ISieveProcessor sieveProcessor,
CancellationToken cancellationToken = default
)
where TEntity : class
{
var sieveModel = new SieveModel
{
PageSize = pageRequest.PageSize,
Page = pageRequest.PageNumber,
Sorts = pageRequest.SortOrder,
Filters = pageRequest.Filters
};
// https://github.com/Biarity/Sieve/issues/34#issuecomment-403817573
var result = sieveProcessor.Apply(sieveModel, queryable, applyPagination: false);
var total = result.Count();
result = sieveProcessor.Apply(sieveModel, queryable, applyFiltering: false,
applySorting: false); // Only applies pagination
var items = await result
.ToAsyncEnumerable()
.ToListAsync(cancellationToken: cancellationToken);
return PageList<TEntity>.Create(items.AsReadOnly(), pageRequest.PageNumber, pageRequest.PageSize, total);
}
}

View File

@ -0,0 +1,16 @@
namespace BuildingBlocks.Core.Pagination;
public interface IPageList<T>
where T : class
{
int CurrentPageSize { get; }
int CurrentStartIndex { get; }
int CurrentEndIndex { get; }
int TotalPages { get; }
bool HasPrevious { get; }
bool HasNext { get; }
IReadOnlyList<T> Items { get; init; }
int TotalCount { get; init; }
int PageNumber { get; init; }
int PageSize { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace BuildingBlocks.Core.Pagination;
using MediatR;
public interface IPageQuery<out TResponse> : IPageRequest, IRequest<TResponse>
where TResponse : class { }

View File

@ -0,0 +1,9 @@
namespace BuildingBlocks.Core.Pagination;
public interface IPageRequest
{
int PageNumber { get; init; }
int PageSize { get; init; }
string? Filters { get; init; }
string? SortOrder { get; init; }
}

View File

@ -0,0 +1,19 @@
namespace BuildingBlocks.Core.Pagination;
public record PageList<T>(IReadOnlyList<T> Items, int PageNumber, int PageSize, int TotalCount) : IPageList<T>
where T : class
{
public int CurrentPageSize => Items.Count;
public int CurrentStartIndex => TotalCount == 0 ? 0 : ((PageNumber - 1) * PageSize) + 1;
public int CurrentEndIndex => TotalCount == 0 ? 0 : CurrentStartIndex + CurrentPageSize - 1;
public int TotalPages => (int)Math.Ceiling(TotalCount / (double)PageSize);
public bool HasPrevious => PageNumber > 1;
public bool HasNext => PageNumber < TotalPages;
public static PageList<T> Empty => new(Enumerable.Empty<T>().ToList(), 0, 0, 0);
public static PageList<T> Create(IReadOnlyList<T> items, int pageNumber, int pageSize, int totalItems)
{
return new PageList<T>(items, pageNumber, pageSize, totalItems);
}
}

View File

@ -50,12 +50,16 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
nameof(EfTxBehavior<TRequest, TResponse>), nameof(EfTxBehavior<TRequest, TResponse>),
typeof(TRequest).FullName); typeof(TRequest).FullName);
var domainEvents = _dbContextBase.GetDomainEvents(); while (true)
{
var domainEvents = _dbContextBase.GetDomainEvents();
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken); if (domainEvents is null || !domainEvents.Any())
return response;
await _dbContextBase.ExecuteTransactionalAsync(cancellationToken); await _dbContextBase.ExecuteTransactionalAsync(cancellationToken);
return response; await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
}
} }
} }

View File

@ -66,7 +66,7 @@ public static class Extensions
{ {
Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted; Expression<Func<IAggregate, bool>> filterExpr = e => !e.IsDeleted;
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes() foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()
.Where(m => m.ClrType.IsAssignableTo(typeof(IAudit)))) .Where(m => m.ClrType.IsAssignableTo(typeof(IEntity))))
{ {
// modify expression to handle correct child type // modify expression to handle correct child type
var parameter = Expression.Parameter(mutableEntityType.ClrType); var parameter = Expression.Parameter(mutableEntityType.ClrType);

View File

@ -3,7 +3,7 @@ using BuildingBlocks.Core.Model;
namespace BuildingBlocks.EventStoreDB.Events namespace BuildingBlocks.EventStoreDB.Events
{ {
public abstract record AggregateEventSourcing<TId> : Audit, IAggregateEventSourcing<TId> public abstract record AggregateEventSourcing<TId> : Entity<TId>, IAggregateEventSourcing<TId>
{ {
private readonly List<IDomainEvent> _domainEvents = new(); private readonly List<IDomainEvent> _domainEvents = new();
public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly(); public IReadOnlyList<IDomainEvent> DomainEvents => _domainEvents.AsReadOnly();
@ -23,10 +23,6 @@ namespace BuildingBlocks.EventStoreDB.Events
} }
public virtual void When(object @event) { } public virtual void When(object @event) { }
public long Version { get; protected set; } = -1;
public TId Id { get; protected set; }
} }
} }

View File

@ -3,17 +3,15 @@ using BuildingBlocks.Core.Model;
namespace BuildingBlocks.EventStoreDB.Events namespace BuildingBlocks.EventStoreDB.Events
{ {
public interface IAggregateEventSourcing : IProjection, IAudit using Microsoft.FSharp.Control;
public interface IAggregateEventSourcing : IProjection, IEntity
{ {
IReadOnlyList<IDomainEvent> DomainEvents { get; } IReadOnlyList<IDomainEvent> DomainEvents { get; }
IDomainEvent[] ClearDomainEvents(); IDomainEvent[] ClearDomainEvents();
long Version { get; }
} }
public interface IAggregateEventSourcing<out T> : IAggregateEventSourcing public interface IAggregateEventSourcing<T> : IAggregateEventSourcing, IEntity<T>
{ {
T Id { get; }
} }
} }

View File

@ -398,11 +398,17 @@ public class TestWriteFixture<TEntryPoint, TWContext> : TestFixture<TEntryPoint>
}); });
} }
public Task<T> FindAsync<T>(Guid id) public Task<T> FindAsync<T, TKey>(TKey id)
where T : class, IAudit where T : class, IEntity
{ {
return ExecuteDbContextAsync(db => db.Set<T>().FindAsync(id).AsTask()); return ExecuteDbContextAsync(db => db.Set<T>().FindAsync(id).AsTask());
} }
public Task<T> FirstOrDefaultAsync<T>()
where T : class, IEntity
{
return ExecuteDbContextAsync(db => db.Set<T>().FirstOrDefaultAsync());
}
} }
public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint> public class TestReadFixture<TEntryPoint, TRContext> : TestFixture<TEntryPoint>

View File

@ -7,6 +7,7 @@ using BuildingBlocks.Core.Event;
using BuildingBlocks.Core.Model; using BuildingBlocks.Core.Model;
using BuildingBlocks.EventStoreDB.Repository; using BuildingBlocks.EventStoreDB.Repository;
using BuildingBlocks.Web; using BuildingBlocks.Web;
using Elasticsearch.Net;
using Exceptions; using Exceptions;
using Flight; using Flight;
using FluentValidation; using FluentValidation;
@ -27,7 +28,7 @@ public record CreateBooking(Guid PassengerId, Guid FlightId, string Description)
public record CreateBookingResult(ulong Id); public record CreateBookingResult(ulong Id);
public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Audit, IDomainEvent; public record BookingCreatedDomainEvent(Guid Id, PassengerInfo PassengerInfo, Trip Trip) : Entity<Guid>, IDomainEvent;
public record CreateBookingRequestDto(Guid PassengerId, Guid FlightId, string Description); public record CreateBookingRequestDto(Guid PassengerId, Guid FlightId, string Description);

View File

@ -54,7 +54,7 @@ public class CreateFlightEndpoint : IMinimalEndpoint
return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response); return Results.CreatedAtRoute("GetFlightById", new { id = result.Id }, response);
}) })
.RequireAuthorization() // .RequireAuthorization()
.WithName("CreateFlight") .WithName("CreateFlight")
.WithApiVersionSet(builder.NewApiVersionSet("Flight").Build()) .WithApiVersionSet(builder.NewApiVersionSet("Flight").Build())
.Produces<CreateFlightResponseDto>(StatusCodes.Status201Created) .Produces<CreateFlightResponseDto>(StatusCodes.Status201Created)
@ -68,7 +68,7 @@ public class CreateFlightEndpoint : IMinimalEndpoint
} }
} }
internal class CreateFlightValidator : AbstractValidator<CreateFlight> public class CreateFlightValidator : AbstractValidator<CreateFlight>
{ {
public CreateFlightValidator() public CreateFlightValidator()
{ {

View File

@ -1,3 +1,5 @@
namespace Flight.Identity.Consumers.RegisterNewUser.V1;
using System.Threading.Tasks; using System.Threading.Tasks;
using BuildingBlocks.Contracts.EventBus.Messages; using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.Web; using BuildingBlocks.Web;
@ -6,8 +8,6 @@ using MassTransit;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Flight.Identity.Consumers.RegisterNewUser.Consumes.V1;
public class RegisterNewUserConsumerHandler : IConsumer<UserCreated> public class RegisterNewUserConsumerHandler : IConsumer<UserCreated>
{ {
private readonly AppOptions _options; private readonly AppOptions _options;

View File

@ -10,8 +10,10 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
using System;
using global::Flight.Data.Seed; using global::Flight.Data.Seed;
using global::Flight.Flights.Features.DeletingFlight.V1; using global::Flight.Flights.Features.DeletingFlight.V1;
using global::Flight.Flights.Models;
public class DeleteFlightTests : FlightIntegrationTestBase public class DeleteFlightTests : FlightIntegrationTestBase
{ {
@ -24,7 +26,7 @@ public class DeleteFlightTests : FlightIntegrationTestBase
public async Task should_delete_flight_from_db() public async Task should_delete_flight_from_db()
{ {
// Arrange // Arrange
var flightEntity = await Fixture.FindAsync<global::Flight.Flights.Models.Flight>( InitialData.Flights.First().Id); var flightEntity = await Fixture.FindAsync<Flight, Guid>( InitialData.Flights.First().Id);
var command = new DeleteFlight(flightEntity.Id); var command = new DeleteFlight(flightEntity.Id);
// Act // Act

View File

@ -9,9 +9,11 @@ using Xunit;
namespace Integration.Test.Flight.Features; namespace Integration.Test.Flight.Features;
using System;
using System.Linq; using System.Linq;
using global::Flight.Data.Seed; using global::Flight.Data.Seed;
using global::Flight.Flights.Features.UpdatingFlight.V1; using global::Flight.Flights.Features.UpdatingFlight.V1;
using global::Flight.Flights.Models;
public class UpdateFlightTests : FlightIntegrationTestBase public class UpdateFlightTests : FlightIntegrationTestBase
{ {
@ -24,7 +26,7 @@ public class UpdateFlightTests : FlightIntegrationTestBase
public async Task should_update_flight_to_db_and_publish_message_to_broker() public async Task should_update_flight_to_db_and_publish_message_to_broker()
{ {
// Arrange // Arrange
var flightEntity = await Fixture.FindAsync<global::Flight.Flights.Models.Flight>( InitialData.Flights.First().Id); var flightEntity = await Fixture.FindAsync<Flight, Guid>( InitialData.Flights.First().Id);
var command = new FakeUpdateFlightCommand(flightEntity).Generate(); var command = new FakeUpdateFlightCommand(flightEntity).Generate();
// Act // Act

View File

@ -5,80 +5,88 @@ using Flight.Flights.Enums;
using Flight.Seats.Enums; using Flight.Seats.Enums;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace Unit.Test.Common namespace Unit.Test.Common;
using MassTransit;
public static class DbContextFactory
{ {
using MassTransit; private static readonly Guid _airportId1 = NewId.NextGuid();
private static readonly Guid _airportId2 = NewId.NextGuid();
private static readonly Guid _aircraft1 = NewId.NextGuid();
private static readonly Guid _aircraft2 = NewId.NextGuid();
private static readonly Guid _aircraft3 = NewId.NextGuid();
private static readonly Guid _flightId1 = NewId.NextGuid();
public static class DbContextFactory public static FlightDbContext Create()
{ {
private static readonly Guid _airportId1 = NewId.NextGuid(); var options = new DbContextOptionsBuilder<FlightDbContext>()
private static readonly Guid _airportId2 = NewId.NextGuid(); .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
private static readonly Guid _aircraft1 = NewId.NextGuid();
private static readonly Guid _aircraft2 = NewId.NextGuid();
private static readonly Guid _aircraft3 = NewId.NextGuid();
private static readonly Guid _flightId1 = NewId.NextGuid();
public static FlightDbContext Create() var context = new FlightDbContext(options, currentUserProvider: null);
// Seed our data
FlightDataSeeder(context);
return context;
}
private static void FlightDataSeeder(FlightDbContext context)
{
var airports = new List<global::Flight.Airports.Models.Airport>
{ {
var options = new DbContextOptionsBuilder<FlightDbContext>() global::Flight.Airports.Models.Airport.Create(_airportId1, "Lisbon International Airport", "LIS",
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options; "12988"),
global::Flight.Airports.Models.Airport.Create(_airportId2, "Sao Paulo International Airport", "BRZ",
"11200")
};
var context = new FlightDbContext(options, currentUserProvider: null); context.Airports.AddRange(airports);
// Seed our data var aircrafts = new List<global::Flight.Aircrafts.Models.Aircraft>
FlightDataSeeder(context);
return context;
}
private static void FlightDataSeeder(FlightDbContext context)
{ {
var airports = new List<global::Flight.Airports.Models.Airport> global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft1, "Boeing 737", "B737", 2005),
{ global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft2, "Airbus 300", "A300", 2000),
global::Flight.Airports.Models.Airport.Create(_airportId1, "Lisbon International Airport", "LIS", "12988"), global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft3, "Airbus 320", "A320", 2003)
global::Flight.Airports.Models.Airport.Create(_airportId2, "Sao Paulo International Airport", "BRZ", "11200") };
};
context.Airports.AddRange(airports); context.Aircraft.AddRange(aircrafts);
var aircrafts = new List<global::Flight.Aircrafts.Models.Aircraft> var flights = new List<global::Flight.Flights.Models.Flight>
{
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft1, "Boeing 737", "B737", 2005),
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft2, "Airbus 300", "A300", 2000),
global::Flight.Aircrafts.Models.Aircraft.Create(_aircraft3, "Airbus 320", "A320", 2003)
};
context.Aircraft.AddRange(aircrafts);
var flights = new List<global::Flight.Flights.Models.Flight>
{
global::Flight.Flights.Models.Flight.Create(_flightId1, "BD467", _aircraft1, _airportId1, new DateTime(2022, 1, 31, 12, 0, 0),
new DateTime(2022, 1, 31, 14, 0, 0),
_airportId2, 120m,
new DateTime(2022, 1, 31), FlightStatus.Completed,
8000)
};
context.Flights.AddRange(flights);
var seats = new List<global::Flight.Seats.Models.Seat>
{
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12A", SeatType.Window, SeatClass.Economy, _flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12B", SeatType.Window, SeatClass.Economy, _flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12C", SeatType.Middle, SeatClass.Economy, _flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12D", SeatType.Middle, SeatClass.Economy, _flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12E", SeatType.Aisle, SeatClass.Economy, _flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12F", SeatType.Aisle, SeatClass.Economy, _flightId1)
};
context.Seats.AddRange(seats);
context.SaveChanges();
}
public static void Destroy(FlightDbContext context)
{ {
context.Database.EnsureDeleted(); global::Flight.Flights.Models.Flight.Create(_flightId1, "BD467", _aircraft1, _airportId1,
context.Dispose(); new DateTime(2022, 1, 31, 12, 0, 0),
} new DateTime(2022, 1, 31, 14, 0, 0),
_airportId2, 120m,
new DateTime(2022, 1, 31), FlightStatus.Completed,
8000)
};
context.Flights.AddRange(flights);
var seats = new List<global::Flight.Seats.Models.Seat>
{
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12A", SeatType.Window, SeatClass.Economy,
_flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12B", SeatType.Window, SeatClass.Economy,
_flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12C", SeatType.Middle, SeatClass.Economy,
_flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12D", SeatType.Middle, SeatClass.Economy,
_flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12E", SeatType.Aisle, SeatClass.Economy,
_flightId1),
global::Flight.Seats.Models.Seat.Create(NewId.NextGuid(), "12F", SeatType.Aisle, SeatClass.Economy,
_flightId1)
};
context.Seats.AddRange(seats);
context.SaveChanges();
}
public static void Destroy(FlightDbContext context)
{
context.Database.EnsureDeleted();
context.Dispose();
} }
} }

View File

@ -1,4 +1,4 @@
namespace Unit.Test.Flight.Features.Domain namespace Unit.Test.Flight.Features.Domains
{ {
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;

View File

@ -1,4 +1,4 @@
namespace Unit.Test.Flight.Features.Domain; namespace Unit.Test.Flight.Features.Domains;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;

View File

@ -1,13 +1,12 @@
namespace Unit.Test.Flight.Features.Commands.CreateFlight; namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using FluentAssertions; using FluentAssertions;
using global::Flight.Flights.Dtos;
using global::Flight.Flights.Features.CreatingFlight.V1; using global::Flight.Flights.Features.CreatingFlight.V1;
using Common; using Unit.Test.Common;
using Fakes; using Unit.Test.Fakes;
using Xunit; using Xunit;
[Collection(nameof(UnitTestFixture))] [Collection(nameof(UnitTestFixture))]

View File

@ -1,4 +1,4 @@
namespace Unit.Test.Flight.Features.Commands.CreateFlight; namespace Unit.Test.Flight.Features.Handlers.CreateFlight;
using FluentAssertions; using FluentAssertions;
using FluentValidation.TestHelper; using FluentValidation.TestHelper;

View File

@ -63,7 +63,7 @@ public sealed class IdentityContext : IdentityDbContext<User, Role, Guid,
public IReadOnlyList<IDomainEvent> GetDomainEvents() public IReadOnlyList<IDomainEvent> GetDomainEvents()
{ {
var domainEntities = ChangeTracker var domainEntities = ChangeTracker
.Entries<Aggregate>() .Entries<IAggregate>()
.Where(x => x.Entity.DomainEvents.Any()) .Where(x => x.Entity.DomainEvents.Any())
.Select(x => x.Entity) .Select(x => x.Entity)
.ToList(); .ToList();