Merge pull request #32 from meysamhadeli/develop

Develop
This commit is contained in:
Meysam Hadeli 2022-07-16 01:31:41 +04:30 committed by GitHub
commit fb6c7f3a10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 813 additions and 141 deletions

View File

@ -56,6 +56,21 @@ authorization: bearer {{Authenticate.response.body.access_token}}
GET {{flight-api}}
###
###
# @name Create_Seat
Post {{api-gateway}}/api/v1/flight/seat
accept: application/json
Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}}
{
"seatNumber": "12H9",
"type": 1,
"class": 1,
"flightId": 1
}
###
###
# @name Reserve_Seat
@ -66,7 +81,7 @@ authorization: bearer {{Authenticate.response.body.access_token}}
{
"flightId": 1,
"seatNumber": "12C"
"seatNumber": "12H9"
}
###
@ -143,6 +158,20 @@ authorization: bearer {{Authenticate.response.body.access_token}}
}
###
###
# @name Delete_Flights
DELETE {{api-gateway}}/api/v1/flight
accept: application/json
Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}}
{
"id": 1
}
###
###
# @name Create_Airport
POST {{api-gateway}}/api/v1/flight/airport
@ -167,9 +196,9 @@ Content-Type: application/json
authorization: bearer {{Authenticate.response.body.access_token}}
{
"name": "airbus",
"model": "320",
"manufacturingYear": 2010
"name": "airbus2",
"model": "322",
"manufacturingYear": 2012
}
###

View File

@ -7,3 +7,5 @@ public record FlightUpdated(long Id) : IIntegrationEvent;
public record FlightDeleted(long Id) : IIntegrationEvent;
public record AircraftCreated(long Id) : IIntegrationEvent;
public record AirportCreated(long Id) : IIntegrationEvent;
public record SeatCreated(long Id) : IIntegrationEvent;
public record SeatReserved(long Id) : IIntegrationEvent;

View File

@ -3,6 +3,7 @@ namespace BuildingBlocks.Core.Event;
[Flags]
public enum EventType
{
IntegrationEvent = 1,
DomainEvent = 2
DomainEvent = 1,
IntegrationEvent = 2,
InternalCommand = 4
}

View File

@ -1,10 +0,0 @@
using BuildingBlocks.Core.CQRS;
namespace BuildingBlocks.Core.Event;
public interface IInternalCommand : ICommand
{
long Id { get; }
DateTime OccurredOn { get; }
string Type { get; }
}

View File

@ -0,0 +1,11 @@
using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Utils;
public interface IInternalCommand
{
public long Id => SnowFlakIdGenerator.NewId();
public DateTime OccurredOn => DateTime.Now;
public string Type => TypeProvider.GetTypeName(GetType());
}

View File

@ -1,9 +1,10 @@
using BuildingBlocks.IdsGenerator;
using BuildingBlocks.Utils;
using ICommand = BuildingBlocks.Core.CQRS.ICommand;
namespace BuildingBlocks.Core.Event;
public class InternalCommand : IInternalCommand
public class InternalCommand : IInternalCommand, ICommand
{
public long Id { get; set; } = SnowFlakIdGenerator.NewId();

View File

@ -31,20 +31,22 @@ public sealed class EventDispatcher : IEventDispatcher
}
public async Task SendAsync<T>(IReadOnlyList<T> events, CancellationToken cancellationToken = default)
public async Task SendAsync<T>(IReadOnlyList<T> events, EventType eventType = default,
CancellationToken cancellationToken = default)
where T : IEvent
{
async Task PublishIntegrationEvent(IReadOnlyList<IIntegrationEvent> integrationEvents)
{
foreach (var integrationEvent in integrationEvents)
{
await _persistMessageProcessor.PublishMessageAsync(new MessageEnvelope(integrationEvent, SetHeaders()),
cancellationToken);
}
}
if (events.Count > 0)
{
async Task PublishIntegrationEvent(IReadOnlyList<IIntegrationEvent> integrationEvents)
{
foreach (var integrationEvent in integrationEvents)
{
await _persistMessageProcessor.PublishMessageAsync(
new MessageEnvelope(integrationEvent, SetHeaders()),
cancellationToken);
}
}
switch (events)
{
case IReadOnlyList<IDomainEvent> domainEvents:
@ -60,12 +62,24 @@ public sealed class EventDispatcher : IEventDispatcher
await PublishIntegrationEvent(integrationEvents);
break;
}
if (eventType == EventType.InternalCommand)
{
var internalMessages = await MapDomainEventToInternalCommandAsync(events as IReadOnlyList<IDomainEvent>)
.ConfigureAwait(false);
foreach (var internalMessage in internalMessages)
{
await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
}
}
}
}
public async Task SendAsync<T>(T @event, CancellationToken cancellationToken = default)
public async Task SendAsync<T>(T @event, EventType eventType = default,
CancellationToken cancellationToken = default)
where T : IEvent =>
await SendAsync(new[] {@event}, cancellationToken);
await SendAsync(new[] {@event}, eventType, cancellationToken);
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
@ -84,7 +98,7 @@ public sealed class EventDispatcher : IEventDispatcher
var eventType = @event.GetType();
_logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = _eventMapper.Map(@event);
var integrationEvent = _eventMapper.MapToIntegrationEvent(@event);
if (integrationEvent is null) continue;
@ -96,6 +110,31 @@ public sealed class EventDispatcher : IEventDispatcher
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
}
private Task<IReadOnlyList<InternalCommand>> MapDomainEventToInternalCommandAsync(
IReadOnlyList<IDomainEvent> events)
{
_logger.LogTrace("Processing internal message start...");
var internalCommands = new List<InternalCommand>();
using var scope = _serviceScopeFactory.CreateScope();
foreach (var @event in events)
{
var eventType = @event.GetType();
_logger.LogTrace($"Handling domain event: {eventType.Name}");
var integrationEvent = _eventMapper.MapToInternalCommand(@event);
if (integrationEvent is null) continue;
internalCommands.Add(integrationEvent);
}
_logger.LogTrace("Processing internal message done...");
return Task.FromResult<IReadOnlyList<InternalCommand>>(internalCommands);
}
private IEnumerable<IIntegrationEvent> GetWrappedIntegrationEvents(IReadOnlyList<IDomainEvent> domainEvents)
{
foreach (var domainEvent in domainEvents.Where(x =>

View File

@ -4,8 +4,8 @@ namespace BuildingBlocks.Core;
public interface IEventDispatcher
{
public Task SendAsync<T>(IReadOnlyList<T> events, CancellationToken cancellationToken = default)
public Task SendAsync<T>(IReadOnlyList<T> events, EventType eventType = default, CancellationToken cancellationToken = default)
where T : IEvent;
public Task SendAsync<T>(T @event, CancellationToken cancellationToken = default)
public Task SendAsync<T>(T @event, EventType eventType = default, CancellationToken cancellationToken = default)
where T : IEvent;
}

View File

@ -4,6 +4,6 @@ namespace BuildingBlocks.Core;
public interface IEventMapper
{
IIntegrationEvent Map(IDomainEvent @event);
IEnumerable<IIntegrationEvent> MapAll(IEnumerable<IDomainEvent> events);
IIntegrationEvent MapToIntegrationEvent(IDomainEvent @event);
InternalCommand MapToInternalCommand(IDomainEvent @event);
}

View File

@ -61,7 +61,9 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
var domainEvents = _dbContextBase.GetDomainEvents();
await _eventDispatcher.SendAsync(domainEvents.ToArray(), cancellationToken);
var eventType = typeof(TRequest).IsAssignableTo(typeof(IInternalCommand)) ? EventType.InternalCommand : EventType.DomainEvent;
await _eventDispatcher.SendAsync(domainEvents.ToArray(), eventType, cancellationToken);
return response;
}

View File

@ -23,12 +23,15 @@ public static class Extensions
var mongoOptions = services.GetOptions<MongoOptions>("MongoOptions");
var logOptions = services.GetOptions<LogOptions>("LogOptions");
services.AddHealthChecks()
var healthChecksBuilder = services.AddHealthChecks()
.AddSqlServer(sqlOptions.DefaultConnection)
.AddMongoDb(mongoOptions.ConnectionString)
.AddRabbitMQ(rabbitConnectionString: $"amqp://{rabbitMqOptions.UserName}:{rabbitMqOptions.Password}@{rabbitMqOptions.HostName}")
.AddElasticsearch(logOptions.ElasticUri);
if (mongoOptions.ConnectionString is not null)
healthChecksBuilder.AddMongoDb(mongoOptions.ConnectionString);
services.AddHealthChecksUI(setup =>
{
setup.SetEvaluationTimeInSeconds(60); // time in seconds between check

View File

@ -7,12 +7,7 @@ namespace Booking;
public sealed class EventMapper : IEventMapper
{
public IEnumerable<IIntegrationEvent> MapAll(IEnumerable<IDomainEvent> events)
{
return events.Select(Map);
}
public IIntegrationEvent Map(IDomainEvent @event)
public IIntegrationEvent MapToIntegrationEvent(IDomainEvent @event)
{
return @event switch
{
@ -20,4 +15,12 @@ public sealed class EventMapper : IEventMapper
_ => null
};
}
public InternalCommand MapToInternalCommand(IDomainEvent @event)
{
return @event switch
{
_ => null
};
}
}

View File

@ -20,9 +20,7 @@ using Flight.Data;
using Flight.Data.Seed;
using Flight.Extensions;
using FluentValidation;
using HealthChecks.UI.Client;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Prometheus;
using Serilog;

View File

@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
namespace Flight.Aircrafts.Events;
public record AircraftCreatedDomainEvent(long Id, string Name, string Model, int ManufacturingYear) : IDomainEvent;
public record AircraftCreatedDomainEvent(long Id, string Name, string Model, int ManufacturingYear, bool IsDeleted) : IDomainEvent;

View File

@ -0,0 +1,16 @@
using BuildingBlocks.IdsGenerator;
using Flight.Aircrafts.Features.CreateAircraft.Reads;
using Flight.Aircrafts.Models.Reads;
using Mapster;
namespace Flight.Aircrafts.Features;
public class AircraftMappings : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<CreateAircraftMongoCommand, AircraftReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.AircraftId, s => s.Id);
}
}

View File

@ -1,10 +1,11 @@
using BuildingBlocks.Core.CQRS;
using BuildingBlocks.IdsGenerator;
using Flight.Aircrafts.Dtos;
using MediatR;
namespace Flight.Aircrafts.Features.CreateAircraft;
public record CreateAircraftCommand(string Name, string Model, int ManufacturingYear) : IRequest<AircraftResponseDto>
public record CreateAircraftCommand(string Name, string Model, int ManufacturingYear) : ICommand<AircraftResponseDto>, IInternalCommand
{
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
}

View File

@ -0,0 +1,22 @@
using BuildingBlocks.Core.Event;
namespace Flight.Aircrafts.Features.CreateAircraft.Reads;
public class CreateAircraftMongoCommand : InternalCommand
{
public CreateAircraftMongoCommand(long id, string name, string model, int manufacturingYear, bool isDeleted)
{
Id = id;
Name = name;
Model = model;
ManufacturingYear = manufacturingYear;
IsDeleted = isDeleted;
}
public long Id { get; }
public string Name { get; }
public string Model { get; }
public int ManufacturingYear { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Aircrafts.Exceptions;
using Flight.Aircrafts.Models.Reads;
using Flight.Data;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Aircrafts.Features.CreateAircraft.Reads;
public class CreateAircraftMongoCommandHandler : ICommandHandler<CreateAircraftMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public CreateAircraftMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(CreateAircraftMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var aircraftReadModel = _mapper.Map<AircraftReadModel>(command);
var aircraft = await _flightReadDbContext.Aircraft.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == aircraftReadModel.Id, cancellationToken);
if (aircraft is not null)
throw new AircraftAlreadyExistException();
await _flightReadDbContext.Aircraft.InsertOneAsync(aircraftReadModel, cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -14,7 +14,7 @@ public class Aircraft : Aggregate<long>
public string Model { get; private set; }
public int ManufacturingYear { get; private set; }
public static Aircraft Create(long id, string name, string model, int manufacturingYear)
public static Aircraft Create(long id, string name, string model, int manufacturingYear, bool isDeleted = false)
{
var aircraft = new Aircraft
{
@ -28,7 +28,8 @@ public class Aircraft : Aggregate<long>
aircraft.Id,
aircraft.Name,
aircraft.Model,
aircraft.ManufacturingYear);
aircraft.ManufacturingYear,
isDeleted);
aircraft.AddDomainEvent(@event);

View File

@ -0,0 +1,11 @@
namespace Flight.Aircrafts.Models.Reads;
public class AircraftReadModel
{
public long Id { get; init; }
public long AircraftId { get; init; }
public string Name { get; init; }
public string Model { get; init; }
public int ManufacturingYear { get; init; }
public bool IsDeleted { get; init; }
}

View File

@ -0,0 +1,16 @@
using BuildingBlocks.IdsGenerator;
using Flight.Airports.Features.CreateAirport.Reads;
using Flight.Airports.Models.Reads;
using Mapster;
namespace Flight.Airports;
public class AirportMappings : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<CreateAirportMongoCommand, AirportReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.AirportId, s => s.Id);
}
}

View File

@ -2,4 +2,4 @@ using BuildingBlocks.Core.Event;
namespace Flight.Airports.Events;
public record AirportCreatedDomainEvent(long Id, string Name, string Address, string Code) : IDomainEvent;
public record AirportCreatedDomainEvent(long Id, string Name, string Address, string Code, bool IsDeleted) : IDomainEvent;

View File

@ -1,10 +1,11 @@
using BuildingBlocks.Core.CQRS;
using BuildingBlocks.IdsGenerator;
using Flight.Airports.Dtos;
using MediatR;
namespace Flight.Airports.Features.CreateAirport;
public record CreateAirportCommand(string Name, string Address, string Code) : IRequest<AirportResponseDto>
public record CreateAirportCommand(string Name, string Address, string Code) : ICommand<AirportResponseDto>, IInternalCommand
{
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
}

View File

@ -0,0 +1,21 @@
using BuildingBlocks.Core.Event;
namespace Flight.Airports.Features.CreateAirport.Reads;
public class CreateAirportMongoCommand : InternalCommand
{
public CreateAirportMongoCommand(long id, string name, string address, string code, bool isDeleted)
{
Id = id;
Name = name;
Address = address;
Code = code;
IsDeleted = isDeleted;
}
public long Id { get; }
public string Name { get; }
public string Address { get; }
public string Code { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Airports.Exceptions;
using Flight.Airports.Models.Reads;
using Flight.Data;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Airports.Features.CreateAirport.Reads;
public class CreateAirportMongoCommandHandler : ICommandHandler<CreateAirportMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public CreateAirportMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(CreateAirportMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var airportReadModel = _mapper.Map<AirportReadModel>(command);
var aircraft = await _flightReadDbContext.Airport.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == airportReadModel.Id, cancellationToken);
if (aircraft is not null)
throw new AirportAlreadyExistException();
await _flightReadDbContext.Airport.InsertOneAsync(airportReadModel, cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -14,7 +14,7 @@ public class Airport : Aggregate<long>
public string Address { get; private set; }
public string Code { get; private set; }
public static Airport Create(long id, string name, string address, string code)
public static Airport Create(long id, string name, string address, string code, bool isDeleted = false)
{
var airport = new Airport
{
@ -28,7 +28,8 @@ public class Airport : Aggregate<long>
airport.Id,
airport.Name,
airport.Address,
airport.Code);
airport.Code,
isDeleted);
airport.AddDomainEvent(@event);

View File

@ -0,0 +1,11 @@
namespace Flight.Airports.Models.Reads;
public class AirportReadModel
{
public long Id { get; init; }
public long AirportId { get; init; }
public string Name { get; init; }
public string Address { get; init; }
public string Code { get; init; }
public bool IsDeleted { get; set; }
}

View File

@ -1,5 +1,9 @@
using BuildingBlocks.Mongo;
using Flight.Aircrafts.Models.Reads;
using Flight.Airports.Models;
using Flight.Airports.Models.Reads;
using Flight.Flights.Models.Reads;
using Flight.Seats.Models.Reads;
using Humanizer;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
@ -11,7 +15,13 @@ public class FlightReadDbContext : MongoDbContext
public FlightReadDbContext(IOptions<MongoOptions> options) : base(options)
{
Flight = GetCollection<FlightReadModel>(nameof(Flight).Underscore());
Aircraft = GetCollection<AircraftReadModel>(nameof(Aircraft).Underscore());
Airport = GetCollection<AirportReadModel>(nameof(Airport).Underscore());
Seat = GetCollection<SeatReadModel>(nameof(Seat).Underscore());
}
public IMongoCollection<FlightReadModel> Flight { get; }
public IMongoCollection<AircraftReadModel> Aircraft { get; }
public IMongoCollection<AirportReadModel> Airport { get; }
public IMongoCollection<SeatReadModel> Seat { get; }
}

View File

@ -1,20 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using BuildingBlocks.Contracts.EventBus.Messages;
using BuildingBlocks.Core;
using BuildingBlocks.Core.Event;
using Flight.Aircrafts.Events;
using Flight.Aircrafts.Features.CreateAircraft.Reads;
using Flight.Airports.Events;
using Flight.Airports.Features.CreateAirport.Reads;
using Flight.Flights.Events.Domain;
using Flight.Flights.Features.CreateFlight.Reads;
using Flight.Flights.Features.DeleteFlight.Reads;
using Flight.Flights.Features.UpdateFlight.Reads;
using Flight.Seats.Events;
using Flight.Seats.Features.CreateSeat.Reads;
using Flight.Seats.Features.ReserveSeat.Reads;
namespace Flight;
// ref: https://www.ledjonbehluli.com/posts/domain_to_integration_event/
public sealed class EventMapper : IEventMapper
{
public IEnumerable<IIntegrationEvent> MapAll(IEnumerable<IDomainEvent> events) => events.Select(Map);
public IIntegrationEvent Map(IDomainEvent @event)
public IIntegrationEvent MapToIntegrationEvent(IDomainEvent @event)
{
return @event switch
{
@ -23,6 +27,26 @@ public sealed class EventMapper : IEventMapper
FlightDeletedDomainEvent e => new FlightDeleted(e.Id),
AirportCreatedDomainEvent e => new AirportCreated(e.Id),
AircraftCreatedDomainEvent e => new AircraftCreated(e.Id),
SeatCreatedDomainEvent e => new SeatCreated(e.Id),
SeatReservedDomainEvent e => new SeatReserved(e.Id),
_ => null
};
}
public InternalCommand MapToInternalCommand(IDomainEvent @event)
{
return @event switch
{
FlightCreatedDomainEvent e => new CreateFlightMongoCommand(e.Id, e.FlightNumber, e.AircraftId, e.DepartureDate, e.DepartureAirportId,
e.ArriveDate, e.ArriveAirportId, e.DurationMinutes, e.FlightDate, e.Status, e.Price, e.IsDeleted),
FlightUpdatedDomainEvent e => new UpdateFlightMongoCommand(e.Id, e.FlightNumber, e.AircraftId, e.DepartureDate, e.DepartureAirportId,
e.ArriveDate, e.ArriveAirportId, e.DurationMinutes, e.FlightDate, e.Status, e.Price, e.IsDeleted),
FlightDeletedDomainEvent e => new DeleteFlightMongoCommand(e.Id, e.FlightNumber, e.AircraftId, e.DepartureDate, e.DepartureAirportId,
e.ArriveDate, e.ArriveAirportId, e.DurationMinutes, e.FlightDate, e.Status, e.Price, e.IsDeleted),
AircraftCreatedDomainEvent e => new CreateAircraftMongoCommand(e.Id, e.Name, e.Model, e.ManufacturingYear, e.IsDeleted),
AirportCreatedDomainEvent e => new CreateAirportMongoCommand(e.Id, e.Name, e.Address, e.Code, e.IsDeleted),
SeatCreatedDomainEvent e => new CreateSeatMongoCommand(e.Id, e.SeatNumber, e.Type, e.Class, e.FlightId, e.IsDeleted),
SeatReservedDomainEvent e => new ReserveSeatMongoCommand(e.Id, e.SeatNumber, e.Type, e.Class, e.FlightId, e.IsDeleted),
_ => null
};
}

View File

@ -16,8 +16,6 @@
<Folder Include="Airports\Exceptions" />
<Folder Include="Data\Migrations" />
<Folder Include="Enum" />
<Folder Include="Flights\Features\UpdateFlight" />
<Folder Include="Seats\Features\CreateSeat" />
</ItemGroup>
<ItemGroup>

View File

@ -8,7 +8,7 @@ namespace Flight.Flights.Features.CreateFlight;
public record CreateFlightCommand(string FlightNumber, long AircraftId, long DepartureAirportId,
DateTime DepartureDate, DateTime ArriveDate, long ArriveAirportId,
decimal DurationMinutes, DateTime FlightDate, FlightStatus Status, decimal Price) : ICommand<FlightResponseDto>
decimal DurationMinutes, DateTime FlightDate, FlightStatus Status, decimal Price) : ICommand<FlightResponseDto>, IInternalCommand
{
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
}

View File

@ -44,10 +44,6 @@ public class CreateFlightCommandHandler : ICommandHandler<CreateFlightCommand, F
var newFlight = await _flightDbContext.Flights.AddAsync(flightEntity, cancellationToken);
var createFlightMongoCommand = _mapper.Map<CreateFlightMongoCommand>(newFlight.Entity);
await _persistMessageProcessor.AddInternalMessageAsync(createFlightMongoCommand, cancellationToken);
return _mapper.Map<FlightResponseDto>(newFlight.Entity);
}
}

View File

@ -6,12 +6,8 @@ namespace Flight.Flights.Features.CreateFlight.Reads;
public class CreateFlightMongoCommand : InternalCommand
{
public CreateFlightMongoCommand()
{
}
public CreateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate, long DepartureAirportId,
public CreateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
long DepartureAirportId,
DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, FlightStatus Status,
decimal Price, bool IsDeleted)
{
@ -29,15 +25,15 @@ public class CreateFlightMongoCommand : InternalCommand
this.IsDeleted = IsDeleted;
}
public string FlightNumber { get; init; }
public long AircraftId { get; init; }
public DateTime DepartureDate { get; init; }
public long DepartureAirportId { get; init; }
public DateTime ArriveDate { get; init; }
public long ArriveAirportId { get; init; }
public decimal DurationMinutes { get; init; }
public DateTime FlightDate { get; init; }
public FlightStatus Status { get; init; }
public decimal Price { get; init; }
public bool IsDeleted { get; init; }
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -1,5 +1,4 @@
using System.Linq;
using System.Threading;
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
@ -33,7 +32,7 @@ public class CreateFlightMongoCommandHandler : ICommandHandler<CreateFlightMongo
var flightReadModel = _mapper.Map<FlightReadModel>(command);
var flight = await _flightReadDbContext.Flight.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == command.Id, cancellationToken);
.FirstOrDefaultAsync(x => x.Id == flightReadModel.Id, cancellationToken);
if (flight is not null)
throw new FlightAlreadyExistException();

View File

@ -3,4 +3,4 @@ using Flight.Flights.Dtos;
namespace Flight.Flights.Features.DeleteFlight;
public record DeleteFlightCommand(long Id) : ICommand<FlightResponseDto>;
public record DeleteFlightCommand(long Id) : ICommand<FlightResponseDto>, IInternalCommand;

View File

@ -0,0 +1,39 @@
using System;
using BuildingBlocks.Core.Event;
using Flight.Flights.Models;
namespace Flight.Flights.Features.DeleteFlight.Reads;
public class DeleteFlightMongoCommand : InternalCommand
{
public DeleteFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
long DepartureAirportId,
DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, FlightStatus Status,
decimal Price, bool IsDeleted)
{
this.Id = Id;
this.FlightNumber = FlightNumber;
this.AircraftId = AircraftId;
this.DepartureDate = DepartureDate;
this.DepartureAirportId = DepartureAirportId;
this.ArriveDate = ArriveDate;
this.ArriveAirportId = ArriveAirportId;
this.DurationMinutes = DurationMinutes;
this.FlightDate = FlightDate;
this.Status = Status;
this.Price = Price;
this.IsDeleted = IsDeleted;
}
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,48 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Data;
using Flight.Flights.Exceptions;
using Flight.Flights.Models.Reads;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Flights.Features.DeleteFlight.Reads;
public class DeleteFlightMongoCommandHandler : ICommandHandler<DeleteFlightMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public DeleteFlightMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(DeleteFlightMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var flightReadModel = _mapper.Map<FlightReadModel>(command);
var flight = await _flightReadDbContext.Flight.AsQueryable()
.FirstOrDefaultAsync(x => x.FlightId == flightReadModel.FlightId, cancellationToken);
if (flight is null)
throw new FlightNotFountException();
await _flightReadDbContext.Flight.UpdateOneAsync(
x => x.FlightId == flightReadModel.FlightId,
Builders<FlightReadModel>.Update
.Set(x => x.IsDeleted, flightReadModel.IsDeleted),
cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -1,17 +1,25 @@
using AutoMapper;
using BuildingBlocks.IdsGenerator;
using Flight.Flights.Dtos;
using Flight.Flights.Features.CreateFlight.Reads;
using Flight.Flights.Features.DeleteFlight.Reads;
using Flight.Flights.Features.UpdateFlight.Reads;
using Flight.Flights.Models.Reads;
using Mapster;
namespace Flight.Flights.Features;
public class FlightMappings : Profile
public class FlightMappings : IRegister
{
public void Register(TypeAdapterConfig config)
{
config.NewConfig<Models.Flight, FlightResponseDto>();
config.NewConfig<Models.Flight, CreateFlightMongoCommand>();
config.NewConfig<CreateFlightMongoCommand, FlightReadModel>();
config.NewConfig<Models.Flight, FlightResponseDto>();
config.NewConfig<CreateFlightMongoCommand, FlightReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.FlightId, s => s.Id);
config.NewConfig<UpdateFlightMongoCommand, FlightReadModel>()
.Map(d => d.FlightId, s => s.Id);
config.NewConfig<DeleteFlightMongoCommand, FlightReadModel>()
.Map(d => d.FlightId, s => s.Id);
}
}

View File

@ -8,20 +8,19 @@ using Flight.Data;
using Flight.Flights.Dtos;
using Flight.Flights.Exceptions;
using MapsterMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
namespace Flight.Flights.Features.GetAvailableFlights;
public class GetAvailableFlightsQueryHandler : IQueryHandler<GetAvailableFlightsQuery, IEnumerable<FlightResponseDto>>
{
private readonly FlightDbContext _flightDbContext;
private readonly IMapper _mapper;
private readonly FlightReadDbContext _flightReadDbContext;
public GetAvailableFlightsQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
public GetAvailableFlightsQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
{
_mapper = mapper;
_flightDbContext = flightDbContext;
_flightReadDbContext = flightReadDbContext;
}
public async Task<IEnumerable<FlightResponseDto>> Handle(GetAvailableFlightsQuery query,
@ -29,7 +28,8 @@ public class GetAvailableFlightsQueryHandler : IQueryHandler<GetAvailableFlights
{
Guard.Against.Null(query, nameof(query));
var flight = await _flightDbContext.Flights.ToListAsync(cancellationToken);
var flight = (await _flightReadDbContext.Flight.AsQueryable().ToListAsync(cancellationToken))
.Where(x => !x.IsDeleted);
if (!flight.Any())
throw new FlightNotFountException();

View File

@ -10,18 +10,16 @@ using Swashbuckle.AspNetCore.Annotations;
namespace Flight.Flights.Features.GetFlightById;
[Route(BaseApiPath + "/flight")]
public class GetFlightByIdEndpoint: BaseController
public class GetFlightByIdEndpoint : BaseController
{
// [Authorize]
[Authorize]
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[SwaggerOperation(Summary = "Get flight by id", Description = "Get flight by id")]
public async Task<ActionResult> GetById([FromRoute] GetFlightByIdQuery query, CancellationToken cancellationToken)
{
throw new Exception();
var result = await Mediator.Send(query, cancellationToken);
return Ok(result);
}
}

View File

@ -6,20 +6,20 @@ using Flight.Data;
using Flight.Flights.Dtos;
using Flight.Flights.Exceptions;
using MapsterMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Flights.Features.GetFlightById;
public class GetFlightByIdQueryHandler : IQueryHandler<GetFlightByIdQuery, FlightResponseDto>
{
private readonly FlightDbContext _flightDbContext;
private readonly IMapper _mapper;
private readonly FlightReadDbContext _flightReadDbContext;
public GetFlightByIdQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
public GetFlightByIdQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
{
_mapper = mapper;
_flightDbContext = flightDbContext;
_flightReadDbContext = flightReadDbContext;
}
public async Task<FlightResponseDto> Handle(GetFlightByIdQuery query, CancellationToken cancellationToken)
@ -27,7 +27,7 @@ public class GetFlightByIdQueryHandler : IQueryHandler<GetFlightByIdQuery, Fligh
Guard.Against.Null(query, nameof(query));
var flight =
await _flightDbContext.Flights.SingleOrDefaultAsync(x => x.Id == query.Id, cancellationToken);
await _flightReadDbContext.Flight.AsQueryable().SingleOrDefaultAsync(x => x.Id == query.Id, cancellationToken);
if (flight is null)
throw new FlightNotFountException();

View File

@ -0,0 +1,39 @@
using System;
using BuildingBlocks.Core.Event;
using Flight.Flights.Models;
namespace Flight.Flights.Features.UpdateFlight.Reads;
public class UpdateFlightMongoCommand : InternalCommand
{
public UpdateFlightMongoCommand(long Id, string FlightNumber, long AircraftId, DateTime DepartureDate,
long DepartureAirportId,
DateTime ArriveDate, long ArriveAirportId, decimal DurationMinutes, DateTime FlightDate, FlightStatus Status,
decimal Price, bool IsDeleted)
{
this.Id = Id;
this.FlightNumber = FlightNumber;
this.AircraftId = AircraftId;
this.DepartureDate = DepartureDate;
this.DepartureAirportId = DepartureAirportId;
this.ArriveDate = ArriveDate;
this.ArriveAirportId = ArriveAirportId;
this.DurationMinutes = DurationMinutes;
this.FlightDate = FlightDate;
this.Status = Status;
this.Price = Price;
this.IsDeleted = IsDeleted;
}
public string FlightNumber { get; }
public long AircraftId { get; }
public DateTime DepartureDate { get; }
public long DepartureAirportId { get; }
public DateTime ArriveDate { get; }
public long ArriveAirportId { get; }
public decimal DurationMinutes { get; }
public DateTime FlightDate { get; }
public FlightStatus Status { get; }
public decimal Price { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,60 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Data;
using Flight.Flights.Exceptions;
using Flight.Flights.Models.Reads;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Flights.Features.UpdateFlight.Reads;
public class UpdateFlightMongoCommandHandler : ICommandHandler<UpdateFlightMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public UpdateFlightMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(UpdateFlightMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var flightReadModel = _mapper.Map<FlightReadModel>(command);
var flight = await _flightReadDbContext.Flight.AsQueryable()
.FirstOrDefaultAsync(x => x.FlightId == flightReadModel.FlightId, cancellationToken);
if (flight is null)
throw new FlightNotFountException();
await _flightReadDbContext.Flight.UpdateOneAsync(
x => x.FlightId == flightReadModel.FlightId,
Builders<FlightReadModel>.Update
.Set(x => x.Id, flightReadModel.Id)
.Set(x => x.Price, flightReadModel.Price)
.Set(x => x.ArriveDate, flightReadModel.ArriveDate)
.Set(x => x.AircraftId, flightReadModel.AircraftId)
.Set(x => x.DurationMinutes, flightReadModel.DurationMinutes)
.Set(x => x.DepartureDate, flightReadModel.DepartureDate)
.Set(x => x.FlightDate, flightReadModel.FlightDate)
.Set(x => x.FlightId, flightReadModel.FlightId)
.Set(x => x.FlightNumber, flightReadModel.FlightNumber)
.Set(x => x.IsDeleted, flightReadModel.IsDeleted)
.Set(x => x.Status, flightReadModel.Status)
.Set(x => x.ArriveAirportId, flightReadModel.ArriveAirportId)
.Set(x => x.DepartureAirportId, flightReadModel.DepartureAirportId),
cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -7,7 +7,7 @@ using MediatR;
namespace Flight.Flights.Features.UpdateFlight;
public record UpdateFlightCommand : ICommand<FlightResponseDto>, IInvalidateCacheRequest
public record UpdateFlightCommand : ICommand<FlightResponseDto>, IInvalidateCacheRequest, IInternalCommand
{
public long Id { get; init; }
public string FlightNumber { get; init; }

View File

@ -7,6 +7,7 @@ namespace Flight.Flights.Models.Reads;
public class FlightReadModel
{
public long Id { get; init; }
public long FlightId { get; set; }
public string FlightNumber { get; init; }
public long AircraftId { get; init; }
public DateTime DepartureDate { get; init; }

View File

@ -3,4 +3,4 @@ using Flight.Seats.Models;
namespace Flight.Seats.Events;
public record SeatCreatedDomainEvent(long Id, string SeatNumber, SeatType Type, SeatClass Class, long FlightId) : IDomainEvent;
public record SeatCreatedDomainEvent(long Id, string SeatNumber, SeatType Type, SeatClass Class, long FlightId, bool IsDeleted) : IDomainEvent;

View File

@ -0,0 +1,6 @@
using BuildingBlocks.Core.Event;
using Flight.Seats.Models;
namespace Flight.Seats.Events;
public record SeatReservedDomainEvent(long Id, string SeatNumber, SeatType Type, SeatClass Class, long FlightId, bool IsDeleted) : IDomainEvent;

View File

@ -1,11 +1,11 @@
using BuildingBlocks.Core.CQRS;
using BuildingBlocks.IdsGenerator;
using Flight.Seats.Dtos;
using Flight.Seats.Models;
using MediatR;
namespace Flight.Seats.Features.CreateSeat;
public record CreateSeatCommand(string SeatNumber, SeatType Type, SeatClass Class, long FlightId) : IRequest<SeatResponseDto>
public record CreateSeatCommand(string SeatNumber, SeatType Type, SeatClass Class, long FlightId) : ICommand<SeatResponseDto>, IInternalCommand
{
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
}

View File

@ -40,8 +40,6 @@ public class CreateSeatCommandHandler : IRequestHandler<CreateSeatCommand, SeatR
var newSeat = await _flightDbContext.Seats.AddAsync(seatEntity, cancellationToken);
await _flightDbContext.SaveChangesAsync(cancellationToken);
return _mapper.Map<SeatResponseDto>(newSeat.Entity);
}
}

View File

@ -0,0 +1,25 @@
using BuildingBlocks.Core.Event;
using Flight.Seats.Models;
namespace Flight.Seats.Features.CreateSeat.Reads;
public class CreateSeatMongoCommand : InternalCommand
{
public CreateSeatMongoCommand(long id, string seatNumber, SeatType type, SeatClass @class,
long flightId, bool isDeleted)
{
Id = id;
SeatNumber = seatNumber;
Type = type;
Class = @class;
FlightId = flightId;
IsDeleted = isDeleted;
}
public long Id { get; }
public string SeatNumber { get; }
public SeatType Type { get; }
public SeatClass Class { get; }
public long FlightId { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,44 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Data;
using Flight.Seats.Exceptions;
using Flight.Seats.Models.Reads;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
namespace Flight.Seats.Features.CreateSeat.Reads;
public class CreateSeatMongoCommandHandler : ICommandHandler<CreateSeatMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public CreateSeatMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(CreateSeatMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var seatReadModel = _mapper.Map<SeatReadModel>(command);
var seat = await _flightReadDbContext.Seat.AsQueryable()
.FirstOrDefaultAsync(x => x.Id == seatReadModel.Id, cancellationToken);
if (seat is not null)
throw new SeatAlreadyExistException();
await _flightReadDbContext.Seat.InsertOneAsync(seatReadModel, cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -1,7 +1,8 @@
using System.Collections.Generic;
using BuildingBlocks.Core.CQRS;
using Flight.Seats.Dtos;
using MediatR;
namespace Flight.Seats.Features.GetAvailableSeats;
public record GetAvailableSeatsQuery(long FlightId) : IRequest<IEnumerable<SeatResponseDto>>;
public record GetAvailableSeatsQuery(long FlightId) : IQuery<IEnumerable<SeatResponseDto>>;

View File

@ -8,19 +8,19 @@ using Flight.Seats.Dtos;
using Flight.Seats.Exceptions;
using MapsterMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;
using MongoDB.Driver;
namespace Flight.Seats.Features.GetAvailableSeats;
public class GetAvailableSeatsQueryHandler : IRequestHandler<GetAvailableSeatsQuery, IEnumerable<SeatResponseDto>>
{
private readonly FlightDbContext _flightDbContext;
private readonly IMapper _mapper;
private readonly FlightReadDbContext _flightReadDbContext;
public GetAvailableSeatsQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
public GetAvailableSeatsQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
{
_mapper = mapper;
_flightDbContext = flightDbContext;
_flightReadDbContext = flightReadDbContext;
}
@ -28,7 +28,8 @@ public class GetAvailableSeatsQueryHandler : IRequestHandler<GetAvailableSeatsQu
{
Guard.Against.Null(query, nameof(query));
var seats = await _flightDbContext.Seats.Where(x => x.FlightId == query.FlightId).ToListAsync(cancellationToken);
var seats = (await _flightReadDbContext.Seat.AsQueryable().ToListAsync(cancellationToken))
.Where(x => !x.IsDeleted);
if (!seats.Any())
throw new AllSeatsFullException();

View File

@ -0,0 +1,25 @@
using BuildingBlocks.Core.Event;
using Flight.Seats.Models;
namespace Flight.Seats.Features.ReserveSeat.Reads;
public class ReserveSeatMongoCommand : InternalCommand
{
public ReserveSeatMongoCommand(long id, string seatNumber, SeatType type, SeatClass @class, long flightId,
bool isDeleted)
{
Id = id;
SeatNumber = seatNumber;
Type = type;
Class = @class;
FlightId = flightId;
IsDeleted = isDeleted;
}
public long Id { get; }
public string SeatNumber { get; }
public SeatType Type { get; }
public SeatClass Class { get; }
public long FlightId { get; }
public bool IsDeleted { get; }
}

View File

@ -0,0 +1,40 @@
using System.Threading;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using BuildingBlocks.Core.CQRS;
using Flight.Data;
using Flight.Seats.Models.Reads;
using MapsterMapper;
using MediatR;
using MongoDB.Driver;
namespace Flight.Seats.Features.ReserveSeat.Reads;
public class ReserveSeatMongoCommandHandler : ICommandHandler<ReserveSeatMongoCommand>
{
private readonly FlightReadDbContext _flightReadDbContext;
private readonly IMapper _mapper;
public ReserveSeatMongoCommandHandler(
FlightReadDbContext flightReadDbContext,
IMapper mapper)
{
_flightReadDbContext = flightReadDbContext;
_mapper = mapper;
}
public async Task<Unit> Handle(ReserveSeatMongoCommand command, CancellationToken cancellationToken)
{
Guard.Against.Null(command, nameof(command));
var seatReadModel = _mapper.Map<SeatReadModel>(command);
await _flightReadDbContext.Seat.UpdateOneAsync(
x => x.SeatId == seatReadModel.SeatId,
Builders<SeatReadModel>.Update
.Set(x => x.IsDeleted, seatReadModel.IsDeleted),
cancellationToken: cancellationToken);
return Unit.Value;
}
}

View File

@ -1,6 +1,6 @@
using BuildingBlocks.Core.CQRS;
using Flight.Seats.Dtos;
using MediatR;
namespace Flight.Seats.Features.ReserveSeat;
public record ReserveSeatCommand(long FlightId, string SeatNumber) : IRequest<SeatResponseDto>;
public record ReserveSeatCommand(long FlightId, string SeatNumber) : ICommand<SeatResponseDto>, IInternalCommand;

View File

@ -1,5 +1,9 @@
using BuildingBlocks.IdsGenerator;
using Flight.Seats.Dtos;
using Flight.Seats.Features.CreateSeat.Reads;
using Flight.Seats.Features.ReserveSeat.Reads;
using Flight.Seats.Models;
using Flight.Seats.Models.Reads;
using Mapster;
namespace Flight.Seats.Features;
@ -9,6 +13,10 @@ public class SeatMappings : IRegister
public void Register(TypeAdapterConfig config)
{
config.NewConfig<Seat, SeatResponseDto>();
config.NewConfig<CreateSeatMongoCommand, SeatReadModel>()
.Map(d => d.Id, s => SnowFlakIdGenerator.NewId())
.Map(d => d.SeatId, s => s.Id);
config.NewConfig<ReserveSeatMongoCommand, SeatReadModel>()
.Map(d => d.SeatId, s => s.Id);
}
}

View File

@ -0,0 +1,12 @@
namespace Flight.Seats.Models.Reads;
public class SeatReadModel
{
public long Id { get; init; }
public long SeatId { get; init; }
public string SeatNumber { get; init; }
public SeatType Type { get; init; }
public SeatClass Class { get; init; }
public long FlightId { get; init; }
public bool IsDeleted { get; init; }
}

View File

@ -1,12 +1,14 @@
using System;
using System.Threading.Tasks;
using BuildingBlocks.Core.Model;
using Flight.Seats.Events;
namespace Flight.Seats.Models;
public class Seat : Aggregate<long>
{
public static Seat Create(long id, string seatNumber, SeatType type, SeatClass @class, long flightId)
public static Seat Create(long id, string seatNumber, SeatType type, SeatClass @class, long flightId,
bool isDeleted = false)
{
var seat = new Seat()
{
@ -14,9 +16,20 @@ public class Seat : Aggregate<long>
Class = @class,
Type = type,
SeatNumber = seatNumber,
FlightId = flightId
FlightId = flightId,
IsDeleted = isDeleted
};
var @event = new SeatCreatedDomainEvent(
seat.Id,
seat.SeatNumber,
seat.Type,
seat.Class,
seat.FlightId,
isDeleted);
seat.AddDomainEvent(@event);
return seat;
}
@ -24,6 +37,17 @@ public class Seat : Aggregate<long>
{
seat.IsDeleted = true;
seat.LastModified = DateTime.Now;
var @event = new SeatReservedDomainEvent(
seat.Id,
seat.SeatNumber,
seat.Type,
seat.Class,
seat.FlightId,
seat.IsDeleted);
seat.AddDomainEvent(@event);
return Task.FromResult(this);
}

View File

@ -63,10 +63,10 @@ app.UseRouting();
app.UseHttpMetrics();
app.UseProblemDetails();
app.UseHttpsRedirection();
app.UseCustomHealthCheck();
app.UseAuthentication();
app.UseAuthorization();
app.UseIdentityServer();
app.UseCustomHealthCheck();
app.UseEndpoints(endpoints =>
{

View File

@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using BuildingBlocks.Core;
using BuildingBlocks.Core.Event;
@ -7,16 +5,19 @@ namespace Identity;
public sealed class EventMapper : IEventMapper
{
public IEnumerable<IIntegrationEvent> MapAll(IEnumerable<IDomainEvent> events)
{
return events.Select(Map);
}
public IIntegrationEvent Map(IDomainEvent @event)
public IIntegrationEvent MapToIntegrationEvent(IDomainEvent @event)
{
return @event switch
{
_ => null
};
}
}
public InternalCommand MapToInternalCommand(IDomainEvent @event)
{
return @event switch
{
_ => null
};
}
}

View File

@ -50,7 +50,7 @@ public class RegisterNewUserCommandHandler : ICommandHandler<RegisterNewUserComm
throw new RegisterIdentityUserException(string.Join(',', roleResult.Errors.Select(e => e.Description)));
await _eventDispatcher.SendAsync(new UserCreated(applicationUser.Id, applicationUser.FirstName + " " + applicationUser.LastName,
applicationUser.PassPortNumber), cancellationToken);
applicationUser.PassPortNumber), cancellationToken: cancellationToken);
return new RegisterNewUserResponseDto
{

View File

@ -5,16 +5,19 @@ namespace Passenger;
public sealed class EventMapper : IEventMapper
{
public IEnumerable<IIntegrationEvent> MapAll(IEnumerable<IDomainEvent> events)
{
return events.Select(Map);
}
public IIntegrationEvent Map(IDomainEvent @event)
public IIntegrationEvent MapToIntegrationEvent(IDomainEvent @event)
{
return @event switch
{
_ => null
};
}
}
public InternalCommand MapToInternalCommand(IDomainEvent @event)
{
return @event switch
{
_ => null
};
}
}