mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-29 01:04:56 +08:00
add flight read models
This commit is contained in:
parent
a6f7bd46d5
commit
47b7493ba0
23
booking.rest
23
booking.rest
@ -56,6 +56,21 @@ authorization: bearer {{Authenticate.response.body.access_token}}
|
|||||||
GET {{flight-api}}
|
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
|
# @name Reserve_Seat
|
||||||
@ -66,7 +81,7 @@ authorization: bearer {{Authenticate.response.body.access_token}}
|
|||||||
|
|
||||||
{
|
{
|
||||||
"flightId": 1,
|
"flightId": 1,
|
||||||
"seatNumber": "12C"
|
"seatNumber": "12H9"
|
||||||
}
|
}
|
||||||
###
|
###
|
||||||
|
|
||||||
@ -181,9 +196,9 @@ Content-Type: application/json
|
|||||||
authorization: bearer {{Authenticate.response.body.access_token}}
|
authorization: bearer {{Authenticate.response.body.access_token}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "airbus",
|
"name": "airbus2",
|
||||||
"model": "320",
|
"model": "322",
|
||||||
"manufacturingYear": 2010
|
"manufacturingYear": 2012
|
||||||
}
|
}
|
||||||
###
|
###
|
||||||
|
|
||||||
|
|||||||
@ -7,3 +7,5 @@ public record FlightUpdated(long Id) : IIntegrationEvent;
|
|||||||
public record FlightDeleted(long Id) : IIntegrationEvent;
|
public record FlightDeleted(long Id) : IIntegrationEvent;
|
||||||
public record AircraftCreated(long Id) : IIntegrationEvent;
|
public record AircraftCreated(long Id) : IIntegrationEvent;
|
||||||
public record AirportCreated(long Id) : IIntegrationEvent;
|
public record AirportCreated(long Id) : IIntegrationEvent;
|
||||||
|
public record SeatCreated(long Id) : IIntegrationEvent;
|
||||||
|
public record SeatReserved(long Id) : IIntegrationEvent;
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
using BuildingBlocks.IdsGenerator;
|
using BuildingBlocks.IdsGenerator;
|
||||||
using Flight.Aircrafts.Dtos;
|
using Flight.Aircrafts.Dtos;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace Flight.Aircrafts.Features.CreateAircraft;
|
namespace Flight.Aircrafts.Features.CreateAircraft;
|
||||||
|
|
||||||
public record CreateAircraftCommand(string Name, string Model, int ManufacturingYear) : IRequest<AircraftResponseDto>, IInternalCommand
|
public record CreateAircraftCommand(string Name, string Model, int ManufacturingYear) : ICommand<AircraftResponseDto>, IInternalCommand
|
||||||
{
|
{
|
||||||
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
|
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
using BuildingBlocks.IdsGenerator;
|
using BuildingBlocks.IdsGenerator;
|
||||||
using Flight.Airports.Dtos;
|
using Flight.Airports.Dtos;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace Flight.Airports.Features.CreateAirport;
|
namespace Flight.Airports.Features.CreateAirport;
|
||||||
|
|
||||||
public record CreateAirportCommand(string Name, string Address, string Code) : IRequest<AirportResponseDto>, IInternalCommand
|
public record CreateAirportCommand(string Name, string Address, string Code) : ICommand<AirportResponseDto>, IInternalCommand
|
||||||
{
|
{
|
||||||
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
|
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ using Flight.Aircrafts.Models.Reads;
|
|||||||
using Flight.Airports.Models;
|
using Flight.Airports.Models;
|
||||||
using Flight.Airports.Models.Reads;
|
using Flight.Airports.Models.Reads;
|
||||||
using Flight.Flights.Models.Reads;
|
using Flight.Flights.Models.Reads;
|
||||||
|
using Flight.Seats.Models.Reads;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
@ -16,9 +17,11 @@ public class FlightReadDbContext : MongoDbContext
|
|||||||
Flight = GetCollection<FlightReadModel>(nameof(Flight).Underscore());
|
Flight = GetCollection<FlightReadModel>(nameof(Flight).Underscore());
|
||||||
Aircraft = GetCollection<AircraftReadModel>(nameof(Aircraft).Underscore());
|
Aircraft = GetCollection<AircraftReadModel>(nameof(Aircraft).Underscore());
|
||||||
Airport = GetCollection<AirportReadModel>(nameof(Airport).Underscore());
|
Airport = GetCollection<AirportReadModel>(nameof(Airport).Underscore());
|
||||||
|
Seat = GetCollection<SeatReadModel>(nameof(Seat).Underscore());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IMongoCollection<FlightReadModel> Flight { get; }
|
public IMongoCollection<FlightReadModel> Flight { get; }
|
||||||
public IMongoCollection<AircraftReadModel> Aircraft { get; }
|
public IMongoCollection<AircraftReadModel> Aircraft { get; }
|
||||||
public IMongoCollection<AirportReadModel> Airport { get; }
|
public IMongoCollection<AirportReadModel> Airport { get; }
|
||||||
|
public IMongoCollection<SeatReadModel> Seat { get; }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,9 @@ using Flight.Flights.Events.Domain;
|
|||||||
using Flight.Flights.Features.CreateFlight.Reads;
|
using Flight.Flights.Features.CreateFlight.Reads;
|
||||||
using Flight.Flights.Features.DeleteFlight.Reads;
|
using Flight.Flights.Features.DeleteFlight.Reads;
|
||||||
using Flight.Flights.Features.UpdateFlight.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;
|
namespace Flight;
|
||||||
|
|
||||||
@ -24,6 +27,8 @@ public sealed class EventMapper : IEventMapper
|
|||||||
FlightDeletedDomainEvent e => new FlightDeleted(e.Id),
|
FlightDeletedDomainEvent e => new FlightDeleted(e.Id),
|
||||||
AirportCreatedDomainEvent e => new AirportCreated(e.Id),
|
AirportCreatedDomainEvent e => new AirportCreated(e.Id),
|
||||||
AircraftCreatedDomainEvent e => new AircraftCreated(e.Id),
|
AircraftCreatedDomainEvent e => new AircraftCreated(e.Id),
|
||||||
|
SeatCreatedDomainEvent e => new SeatCreated(e.Id),
|
||||||
|
SeatReservedDomainEvent e => new SeatReserved(e.Id),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -40,6 +45,8 @@ public sealed class EventMapper : IEventMapper
|
|||||||
e.ArriveDate, e.ArriveAirportId, e.DurationMinutes, e.FlightDate, e.Status, e.Price, e.IsDeleted),
|
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),
|
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),
|
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
|
_ => null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
<Folder Include="Airports\Exceptions" />
|
<Folder Include="Airports\Exceptions" />
|
||||||
<Folder Include="Data\Migrations" />
|
<Folder Include="Data\Migrations" />
|
||||||
<Folder Include="Enum" />
|
<Folder Include="Enum" />
|
||||||
<Folder Include="Seats\Features\CreateSeat" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -8,20 +8,19 @@ using Flight.Data;
|
|||||||
using Flight.Flights.Dtos;
|
using Flight.Flights.Dtos;
|
||||||
using Flight.Flights.Exceptions;
|
using Flight.Flights.Exceptions;
|
||||||
using MapsterMapper;
|
using MapsterMapper;
|
||||||
using MediatR;
|
using MongoDB.Driver;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Flight.Flights.Features.GetAvailableFlights;
|
namespace Flight.Flights.Features.GetAvailableFlights;
|
||||||
|
|
||||||
public class GetAvailableFlightsQueryHandler : IQueryHandler<GetAvailableFlightsQuery, IEnumerable<FlightResponseDto>>
|
public class GetAvailableFlightsQueryHandler : IQueryHandler<GetAvailableFlightsQuery, IEnumerable<FlightResponseDto>>
|
||||||
{
|
{
|
||||||
private readonly FlightDbContext _flightDbContext;
|
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
private readonly FlightReadDbContext _flightReadDbContext;
|
||||||
|
|
||||||
public GetAvailableFlightsQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
|
public GetAvailableFlightsQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
|
||||||
{
|
{
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_flightDbContext = flightDbContext;
|
_flightReadDbContext = flightReadDbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<FlightResponseDto>> Handle(GetAvailableFlightsQuery query,
|
public async Task<IEnumerable<FlightResponseDto>> Handle(GetAvailableFlightsQuery query,
|
||||||
@ -29,7 +28,8 @@ public class GetAvailableFlightsQueryHandler : IQueryHandler<GetAvailableFlights
|
|||||||
{
|
{
|
||||||
Guard.Against.Null(query, nameof(query));
|
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())
|
if (!flight.Any())
|
||||||
throw new FlightNotFountException();
|
throw new FlightNotFountException();
|
||||||
|
|||||||
@ -10,18 +10,16 @@ using Swashbuckle.AspNetCore.Annotations;
|
|||||||
namespace Flight.Flights.Features.GetFlightById;
|
namespace Flight.Flights.Features.GetFlightById;
|
||||||
|
|
||||||
[Route(BaseApiPath + "/flight")]
|
[Route(BaseApiPath + "/flight")]
|
||||||
public class GetFlightByIdEndpoint: BaseController
|
public class GetFlightByIdEndpoint : BaseController
|
||||||
{
|
{
|
||||||
// [Authorize]
|
[Authorize]
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
[SwaggerOperation(Summary = "Get flight by id", Description = "Get flight by id")]
|
[SwaggerOperation(Summary = "Get flight by id", Description = "Get flight by id")]
|
||||||
public async Task<ActionResult> GetById([FromRoute] GetFlightByIdQuery query, CancellationToken cancellationToken)
|
public async Task<ActionResult> GetById([FromRoute] GetFlightByIdQuery query, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
throw new Exception();
|
|
||||||
var result = await Mediator.Send(query, cancellationToken);
|
var result = await Mediator.Send(query, cancellationToken);
|
||||||
|
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,20 +6,20 @@ using Flight.Data;
|
|||||||
using Flight.Flights.Dtos;
|
using Flight.Flights.Dtos;
|
||||||
using Flight.Flights.Exceptions;
|
using Flight.Flights.Exceptions;
|
||||||
using MapsterMapper;
|
using MapsterMapper;
|
||||||
using MediatR;
|
using MongoDB.Driver;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using MongoDB.Driver.Linq;
|
||||||
|
|
||||||
namespace Flight.Flights.Features.GetFlightById;
|
namespace Flight.Flights.Features.GetFlightById;
|
||||||
|
|
||||||
public class GetFlightByIdQueryHandler : IQueryHandler<GetFlightByIdQuery, FlightResponseDto>
|
public class GetFlightByIdQueryHandler : IQueryHandler<GetFlightByIdQuery, FlightResponseDto>
|
||||||
{
|
{
|
||||||
private readonly FlightDbContext _flightDbContext;
|
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
private readonly FlightReadDbContext _flightReadDbContext;
|
||||||
|
|
||||||
public GetFlightByIdQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
|
public GetFlightByIdQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
|
||||||
{
|
{
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_flightDbContext = flightDbContext;
|
_flightReadDbContext = flightReadDbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FlightResponseDto> Handle(GetFlightByIdQuery query, CancellationToken cancellationToken)
|
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));
|
Guard.Against.Null(query, nameof(query));
|
||||||
|
|
||||||
var flight =
|
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)
|
if (flight is null)
|
||||||
throw new FlightNotFountException();
|
throw new FlightNotFountException();
|
||||||
|
|||||||
@ -3,4 +3,4 @@ using Flight.Seats.Models;
|
|||||||
|
|
||||||
namespace Flight.Seats.Events;
|
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;
|
||||||
|
|||||||
@ -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;
|
||||||
@ -1,11 +1,11 @@
|
|||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
using BuildingBlocks.IdsGenerator;
|
using BuildingBlocks.IdsGenerator;
|
||||||
using Flight.Seats.Dtos;
|
using Flight.Seats.Dtos;
|
||||||
using Flight.Seats.Models;
|
using Flight.Seats.Models;
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Flight.Seats.Features.CreateSeat;
|
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();
|
public long Id { get; set; } = SnowFlakIdGenerator.NewId();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,8 +40,6 @@ public class CreateSeatCommandHandler : IRequestHandler<CreateSeatCommand, SeatR
|
|||||||
|
|
||||||
var newSeat = await _flightDbContext.Seats.AddAsync(seatEntity, cancellationToken);
|
var newSeat = await _flightDbContext.Seats.AddAsync(seatEntity, cancellationToken);
|
||||||
|
|
||||||
await _flightDbContext.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return _mapper.Map<SeatResponseDto>(newSeat.Entity);
|
return _mapper.Map<SeatResponseDto>(newSeat.Entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,8 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
using Flight.Seats.Dtos;
|
using Flight.Seats.Dtos;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
|
|
||||||
namespace Flight.Seats.Features.GetAvailableSeats;
|
namespace Flight.Seats.Features.GetAvailableSeats;
|
||||||
|
|
||||||
public record GetAvailableSeatsQuery(long FlightId) : IRequest<IEnumerable<SeatResponseDto>>;
|
public record GetAvailableSeatsQuery(long FlightId) : IQuery<IEnumerable<SeatResponseDto>>;
|
||||||
|
|||||||
@ -8,19 +8,19 @@ using Flight.Seats.Dtos;
|
|||||||
using Flight.Seats.Exceptions;
|
using Flight.Seats.Exceptions;
|
||||||
using MapsterMapper;
|
using MapsterMapper;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace Flight.Seats.Features.GetAvailableSeats;
|
namespace Flight.Seats.Features.GetAvailableSeats;
|
||||||
|
|
||||||
public class GetAvailableSeatsQueryHandler : IRequestHandler<GetAvailableSeatsQuery, IEnumerable<SeatResponseDto>>
|
public class GetAvailableSeatsQueryHandler : IRequestHandler<GetAvailableSeatsQuery, IEnumerable<SeatResponseDto>>
|
||||||
{
|
{
|
||||||
private readonly FlightDbContext _flightDbContext;
|
|
||||||
private readonly IMapper _mapper;
|
private readonly IMapper _mapper;
|
||||||
|
private readonly FlightReadDbContext _flightReadDbContext;
|
||||||
|
|
||||||
public GetAvailableSeatsQueryHandler(IMapper mapper, FlightDbContext flightDbContext)
|
public GetAvailableSeatsQueryHandler(IMapper mapper, FlightReadDbContext flightReadDbContext)
|
||||||
{
|
{
|
||||||
_mapper = mapper;
|
_mapper = mapper;
|
||||||
_flightDbContext = flightDbContext;
|
_flightReadDbContext = flightReadDbContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -28,7 +28,8 @@ public class GetAvailableSeatsQueryHandler : IRequestHandler<GetAvailableSeatsQu
|
|||||||
{
|
{
|
||||||
Guard.Against.Null(query, nameof(query));
|
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())
|
if (!seats.Any())
|
||||||
throw new AllSeatsFullException();
|
throw new AllSeatsFullException();
|
||||||
|
|||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
using Flight.Seats.Dtos;
|
using Flight.Seats.Dtos;
|
||||||
using MediatR;
|
|
||||||
|
|
||||||
namespace Flight.Seats.Features.ReserveSeat;
|
namespace Flight.Seats.Features.ReserveSeat;
|
||||||
|
|
||||||
public record ReserveSeatCommand(long FlightId, string SeatNumber) : IRequest<SeatResponseDto>;
|
public record ReserveSeatCommand(long FlightId, string SeatNumber) : ICommand<SeatResponseDto>, IInternalCommand;
|
||||||
|
|||||||
@ -1,5 +1,9 @@
|
|||||||
|
using BuildingBlocks.IdsGenerator;
|
||||||
using Flight.Seats.Dtos;
|
using Flight.Seats.Dtos;
|
||||||
|
using Flight.Seats.Features.CreateSeat.Reads;
|
||||||
|
using Flight.Seats.Features.ReserveSeat.Reads;
|
||||||
using Flight.Seats.Models;
|
using Flight.Seats.Models;
|
||||||
|
using Flight.Seats.Models.Reads;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
|
|
||||||
namespace Flight.Seats.Features;
|
namespace Flight.Seats.Features;
|
||||||
@ -9,6 +13,10 @@ public class SeatMappings : IRegister
|
|||||||
public void Register(TypeAdapterConfig config)
|
public void Register(TypeAdapterConfig config)
|
||||||
{
|
{
|
||||||
config.NewConfig<Seat, SeatResponseDto>();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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; }
|
||||||
|
}
|
||||||
@ -1,12 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using BuildingBlocks.Core.Model;
|
using BuildingBlocks.Core.Model;
|
||||||
|
using Flight.Seats.Events;
|
||||||
|
|
||||||
namespace Flight.Seats.Models;
|
namespace Flight.Seats.Models;
|
||||||
|
|
||||||
public class Seat : Aggregate<long>
|
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()
|
var seat = new Seat()
|
||||||
{
|
{
|
||||||
@ -14,9 +16,20 @@ public class Seat : Aggregate<long>
|
|||||||
Class = @class,
|
Class = @class,
|
||||||
Type = type,
|
Type = type,
|
||||||
SeatNumber = seatNumber,
|
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;
|
return seat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,6 +37,17 @@ public class Seat : Aggregate<long>
|
|||||||
{
|
{
|
||||||
seat.IsDeleted = true;
|
seat.IsDeleted = true;
|
||||||
seat.LastModified = DateTime.Now;
|
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);
|
return Task.FromResult(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user