文章目录 项目地址 一、Application 层 1.1 定义CQRS的接口以及其他服务 1. Command 2. IQuery查询 3. 当前时间服务接口 4. 邮件发送服务接口 1.2 ReserveBooking Command 1. 处理传入的参数 2. ReserveBookingCommandHandler 3. BookingReservedDomainEvent 1.3 GetBooking Query 1. 创建Dapper的链接接口 2. Query 3. QueryHandler
项目地址
dbt
airflow
一、Application 层
1.1 定义CQRS的接口以及其他服务
1. Command
ICommand.cs
using Bookify. Domain. Abstractions ;
using MediatR ;
namespace Bookify. Application. Abstractions. Messaging ;
public interface ICommand : IRequest< Result>
{
}
public interface ICommand< TReponse> : IRequest< Result< TReponse> >
{
}
ICommandHandler.cs
namespace Bookify. Application. Abstractions. Messaging ;
public interface ICommandHandler< TCommand> : IRequestHandler< TCommand, Result> where TCommand : ICommand
{
} public interface ICommandHandler< TCommand, TResponse> : IRequestHandler< TCommand, Result< TResponse> > where TCommand : ICommand< TResponse>
{
}
2. IQuery查询
using Bookify. Domain. Abstractions ;
using MediatR ;
namespace Bookify. Application. Abstractions. Messaging ;
public interface IQuery< TResponse> : IRequest< Result< TResponse> >
{
}
using Bookify. Domain. Abstractions ;
using MediatR ;
namespace Bookify. Application. Abstractions. Messaging ;
public interface IQueryHandler< TQuery, TResponse> : IRequestHandler< TQuery, Result< TResponse> > where TQuery : IQuery< TResponse>
{
}
3. 当前时间服务接口
namespace Bookify. Application. Abstractions. Clock ; public interface IDateTimeProvider
{ DateTime UtcNow { get ; }
}
4. 邮件发送服务接口
namespace Bookify. Application. Abstractions. Email ;
public interface IEmailService
{ Task SendAsync ( Domain. Users. Email recipient, string subject, string body) ;
}
1.2 ReserveBooking Command
1. 处理传入的参数
ReserveBookingCommand.cs
:返回值是Guid,参数时4个
using Bookify. Application. Abstractions. Messaging ;
namespace Bookify. Application. Bookings. ReserveBooking ;
public record ReserveBookingCommand ( Guid ApartmentId, Guid UserId, DateOnly StartDate, DateOnly EndDate) : ICommand< Guid> ;
2. ReserveBookingCommandHandler
internal sealed class ReserveBookingCommandHandler : ICommandHandler< ReserveBookingCommand, Guid>
{ private readonly IUserRepository _userRepository; private readonly IApartmentRepository _apartmentRepository; private readonly IBookingRepository _bookingRepository; private readonly IUnitOfWork _unitOfWork; private readonly PricingService _pricingService; private readonly IDateTimeProvider _dateTimeProvider; public ReserveBookingCommandHandler ( IUserRepository userRepository, IApartmentRepository apartmentRepository, IBookingRepository bookingRepository, IUnitOfWork unitOfWork, PricingService pricingService, IDateTimeProvider dateTimeProvider) { _userRepository = userRepository; _apartmentRepository = apartmentRepository; _bookingRepository = bookingRepository; _unitOfWork = unitOfWork; _pricingService = pricingService; _dateTimeProvider = dateTimeProvider; } public async Task< Result< Guid> > Handle ( ReserveBookingCommand request, CancellationToken cancellationToken) { User? user = await _userRepository. GetByIdAsync ( request. UserId, cancellationToken) ; if ( user is null ) { return Result. Failure < Guid> ( UserErrors. NotFound) ; } Apartment? apartment = await _apartmentRepository. GetByIdAsync ( request. ApartmentId, cancellationToken) ; if ( apartment is null ) { return Result. Failure < Guid> ( ApartmentErrors. NotFound) ; } var duration = DateRange. Create ( request. StartDate, request. EndDate) ; if ( await _bookingRepository. IsOverlappingAsync ( apartment, duration, cancellationToken) ) { return Result. Failure < Guid> ( BookingErrors. Overlap) ; } var booking = Booking. Reserve ( apartment, user. Id, duration, _dateTimeProvider. UtcNow, _pricingService) ; _bookingRepository. Add ( booking) ; await _unitOfWork. SaveChangesAsync ( cancellationToken) ; return booking. Id; }
}
3. BookingReservedDomainEvent
处理 BookingReservedDomainEvent 事件的逻辑,这里只是一个处理的函数,并没有自动执行,只有当发布了事件之后,才会触发
namespace Bookify. Application. Bookings. ReserveBooking ;
internal sealed class BookingReservedDomainEventHandler : INotificationHandler< BookingReservedDomainEvent>
{ private readonly IBookingRepository _bookingRepository; private readonly IUserRepository _userRepository; private readonly IEmailService _emailService; public BookingReservedDomainEventHandler ( IEmailService emailService, IUserRepository userRepository, IBookingRepository bookingRepository) { _emailService = emailService; _userRepository = userRepository; _bookingRepository = bookingRepository; } public async Task Handle ( BookingReservedDomainEvent notification, CancellationToken cancellationToken) { Booking? booking = await _bookingRepository. GetByIdAsync ( notification. BookingId, cancellationToken) ; if ( booking is null ) { return ; } User? user = await _userRepository. GetByIdAsync ( booking. UserId, cancellationToken) ; if ( user is null ) { return ; } await _emailService. SendAsync ( user. Email, "Booking reserved!" , "You have 10 minutes to confirm this booking" ) ; }
}
1.3 GetBooking Query
1. 创建Dapper的链接接口
namespace Bookify. Application. Abstractions. Data ;
public interface ISqlConnectionFactory
{ IDbConnection CreateConnection ( ) ;
}
2. Query
GetBookingQuery
:传入BookingID,返回BookingResponse
using Bookify. Application. Abstractions. Messaging ;
namespace Bookify. Application. Bookings. GetBooking ;
public sealed record GetBookingQuery ( Guid BookingId) : IQuery< BookingResponse> ;
namespace Bookify. Application. Bookings. GetBooking ;
public sealed class BookingResponse
{ public Guid Id { get ; init; } public Guid UserId { get ; init; } public Guid ApartmentId { get ; init; } public int Status { get ; init; } public decimal PriceAmount { get ; init; } public string PriceCurrency { get ; init; } public decimal CleaningFeeAmount { get ; init; } public string CleaningFeeCurrency { get ; init; } public decimal AmenitiesUpChargeAmount { get ; init; } public string AmenitiesUpChargeCurrency { get ; init; } public decimal TotalPriceAmount { get ; init; } public string TotalPriceCurrency { get ; init; } public DateOnly DurationStart { get ; init; } public DateOnly DurationEnd { get ; init; } public DateTime CreatedOnUtc { get ; init; }
}
3. QueryHandler
using System. Data ;
using Bookify. Application. Abstractions. Data ;
using Bookify. Application. Abstractions. Messaging ;
using Bookify. Domain. Abstractions ;
using Dapper ; namespace Bookify. Application. Bookings. GetBooking ;
internal sealed class GetBookingQueryHandler : IQueryHandler< GetBookingQuery, BookingResponse>
{ private readonly ISqlConnectionFactory _sqlConnectionFactory; public GetBookingQueryHandler ( ISqlConnectionFactory sqlConnectionFactory) { _sqlConnectionFactory = sqlConnectionFactory; } public async Task< Result< BookingResponse> > Handle ( GetBookingQuery request, CancellationToken cancellationToken) { using IDbConnection connection = _sqlConnectionFactory. CreateConnection ( ) ; const string sql = "" "SELECTid AS Id, apartment_id AS ApartmentId, user_id AS UserId, status AS Status, price_for_period_amount AS PriceAmount, price_for_period_currency AS PriceCurrency, cleaning_fee_amount AS CleaningFeeAmount, cleaning_fee_currency AS CleaningFeeCurrency, amenities_up_charge_amount AS AmenitiesUpChargeAmount, amenities_up_charge_currency AS AmenitiesUpChargeCurrency, total_price_amount AS TotalPriceAmount, total_price_currency AS TotalPriceCurrency, duration_start AS DurationStart, duration_end AS DurationEnd, created_on_utc AS CreatedOnUtcFROM bookingsWHERE id = @BookingId"" "; BookingResponse? booking = await connection. QueryFirstOrDefaultAsync < BookingResponse> ( sql, new { request. BookingId} ) ; return booking; }
}