(二)毛子整洁架构(CQRS/Dapper/DomianEvent Handler)


文章目录

  • 项目地址
  • 一、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

  • 用于处理除查询以外的
  1. ICommand.cs
using Bookify.Domain.Abstractions;
using MediatR;
namespace Bookify.Application.Abstractions.Messaging;
//无返回值的命令
public interface ICommand : IRequest<Result>
{
}
//返回一个TReponse的命令
public interface ICommand<TReponse> : IRequest<Result<TReponse>>
{
}
  1. 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查询

  • 用于查询
    IQuery.cs
using Bookify.Domain.Abstractions;
using MediatR;
namespace Bookify.Application.Abstractions.Messaging;
public interface IQuery<TResponse> : IRequest<Result<TResponse>>
{
}
  • IQueryHandler.cs
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){// Check if the user existsUser? user = await _userRepository.GetByIdAsync(request.UserId, cancellationToken);if (user is null){return Result.Failure<Guid>(UserErrors.NotFound);}// Check if the apartment existsApartment? 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);// Check if the booking duration is validif (await _bookingRepository.IsOverlappingAsync(apartment, duration, cancellationToken)){return Result.Failure<Guid>(BookingErrors.Overlap);}var booking = Booking.Reserve(apartment,user.Id,duration,_dateTimeProvider.UtcNow,_pricingService);//添加booking_bookingRepository.Add(booking);//保存await _unitOfWork.SaveChangesAsync(cancellationToken);return booking.Id;}
}

3. BookingReservedDomainEvent

  • 处理 BookingReservedDomainEvent 事件的逻辑,这里只是一个处理的函数,并没有自动执行,只有当发布了事件之后,才会触发
namespace Bookify.Application.Bookings.ReserveBooking;/// 处理 BookingReservedDomainEvent 事件
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){//通过事件里传来的 BookingId 从数据库查出预订信息。Booking? booking = await _bookingRepository.GetByIdAsync(notification.BookingId, cancellationToken);if (booking is null){return;}//根据 booking.UserId 查出用户信息。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

  • 所有查询,直接使用Dapper

1. 创建Dapper的链接接口

  • 用于连接数据库用
namespace Bookify.Application.Abstractions.Data;
//Dapper链接数据库的工厂接口
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>;
  • BookingResponse.cs
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();//执行的sqlconst 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""";//执行sqlBookingResponse? booking = await connection.QueryFirstOrDefaultAsync<BookingResponse>(sql,new{request.BookingId});return booking;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/48939.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构与算法:图论——最短路径

最短路径 先给出一些leetcode算法题&#xff0c;以后遇见了相关题目再往上增加 最短路径的4个常用算法是Floyd、Bellman-Ford、SPFA、Dijkstra。不同应用场景下&#xff0c;应有选择地使用它们&#xff1a; 图的规模小&#xff0c;用Floyd。若边的权值有负数&#xff0c;需要…

uniapp-商城-43-shop 后台管理 页面

后台管理较为简单&#xff0c;主要用于后台数据的管理&#xff0c;包含商品类别和商品信息&#xff0c;其实还可以扩展到管理用户等等 1、后台首页 包含 分类管理 商品管理 关于商家等几个栏目 主要代码&#xff1a; <template><view class"manage">…

LeetCode 1. 两数之和(Java)

LeetCode 1. 两数之和&#xff08;暴力 vs 哈希表&#xff09; 题目描述 给定一个整数数组 nums 和一个整数 target&#xff0c;要求找出数组中和为目标值的两个数&#xff0c;并返回它们的下标。假设每个输入只有一种答案&#xff0c;且同一元素不能重复使用。 示例&#xf…

《软件项目管理》笔记一

软件项目管理概述 项目管理属于软件工程的组成之一&#xff0c;另外两部分为&#xff1a;软件开发&#xff0c;过程改进。 参考书如下&#xff1a; 1.1 项目与软件项目 1、项目&#xff1a; 为了创造一个唯一的产品或提供一个唯一的服务而进行 的临时性的努力。 2、项目的…

深度学习:智能车牌识别系统(python)

这是一个基于opencv的智能车牌识别系统,有GUI界面。程序能自动识别图片中的车牌号码,并支持中文和英文字符识别,支持选择本地图片文件,支持多种图片格式(jpg、jpeg、png、bmp、gif)。 下面,我将按模块功能对代码进行分段说明: 1. 导入模块部分 import tkinter as tk…

Redis 持久化机制全面解析:RDB 与 AOF 的原理与实践

目录 前言1. Redis 持久化的总体思路2. RDB&#xff1a;快照机制详解2.1 RDB 的工作原理2.2 RDB 的优势2.3 RDB 的局限性 3. AOF&#xff1a;追加日志机制详解3.1 AOF 的工作原理3.2 AOF 的优势3.3 AOF 的缺陷 4. RDB 与 AOF 的对比分析4.1 数据丢失风险4.2 文件大小与恢复速度…

混淆矩阵(Confusion Matrix)

混淆矩阵&#xff08;Confusion Matrix&#xff09;是一个用于评估分类模型性能的工具&#xff0c;特别是在机器学习和统计学领域。它展示了模型预测结果与实际结果之间的关系。混淆矩阵通常用于二分类或多分类问题中&#xff0c;但也可以扩展到更多类别的情况。 一、混淆矩阵…

TB6600HG是一款PWM(脉宽调制)斩波型单芯片双极性正弦波微步进电机驱动集成电路。

该驱动器支持电机的正向和反向旋转控制&#xff0c;并具有多种激励模式&#xff0c;包括2相、1-2相、W1-2相、2W1-2相和4W1-2相。 使用这款驱动器&#xff0c;只需时钟信号即可驱动2相双极性步进电机&#xff0c;且振动小、效率高。 主要特点&#xff1a; 单芯片双极性正弦波…

【论文阅读】Towards Stable Backdoor Purification through Feature Shift Tuning

NeurIPS 2023 & 2024 Spotlight https://github.com/AISafety-HKUST/Backdoor_Safety_Tuning 我们的贡献包括&#xff1a; 我们对各种调整策略进行了广泛的评估&#xff0c;发现普通的微调&#xff08;FT&#xff09;和简单的线性探测&#xff08;LP&#xff09;在高投毒率…

创龙全志T536全国产(4核A55 ARM+RISC-V+NPU 17路UART)工业开发板硬件说明书

前 言 本文档主要介绍TLT536-EVM评估板硬件接口资源以及设计注意事项等内容。 T536MX-CXX/T536MX-CEN2处理器的IO电平标准一般为1.8V、3.3V,上拉电源一般不超过3.3V或1.8V,当外接信号电平与IO电平不匹配时,中间需增加电平转换芯片或信号隔离芯片。按键或接口需考虑ESD设计…

Jenkins忘记admin密码后的恢复步骤

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 时间较长没有使用…

React 组件prop添加类型

给函数的props做注解 import { useState } from reacttype Props { className:string,title?:string } // 自定义一个Button组件 function Button(props:Props){// 解构出classname\const {className} propsreturn <button className{className}>点击我</button&g…

如何使用docker配置ros-noetic环境并使用rviz,gazebo

参考链接&#xff1a;【Ubuntu】Docker中配置ROS并可视化Rviz及Gazebo_docker ros-CSDN博客 前言&#xff1a; 其实这个东西是相当必要的&#xff0c;因为我们有时候需要在一台电脑上跑好几个项目&#xff0c;每个项目都有不同的依赖&#xff0c;这些依赖冲突搞得人头皮发麻&…

(已完结)完美解决C盘拓展卷是灰色的无法扩容的问题以及如何正确地在WINDOS上从一个盘扩容到C盘

众所周知&#xff0c;window系统在“计算机”管理中自带了一个磁盘管理系统 但是在使用过程中会出现各种各样无法扩容的毛病。 第一&#xff1a;首先排查&#xff0c;大多数人在扩容之前忽视了一点就是&#xff0c;我们现代的很多新机器都是默认开启BitLocker加密的&#xff…

阿里云服务器-centos部署定时同步数据库数据-dbswitch

前言&#xff1a; 本文章介绍通过dbswitch工具实现2个mysql数据库之间实现自动同步数据。 应用场景&#xff1a;公司要求实现正式环境数据库数据自动冷备 dbswitch依赖环境&#xff1a;git ,maven,jdk 方式一&#xff1a; 不需要在服务器中安装git和maven&#xff0c;直接用…

leetcode 141. Linked List Cycle

题目描述&#xff1a; 代码&#xff1a; 用哈希表也可以解决&#xff0c;但真正考察的是用快慢指针法。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Soluti…

微软输入法常用快捷键介绍以及调教技巧

微软输入法&#xff08;Microsoft Pinyin Input Method&#xff09;是 Windows 系统内置的中文输入工具&#xff0c;以其高效、智能化的特点广受用户喜爱。掌握其常用快捷键和特殊模式可以显著提升输入效率。本文将介绍微软输入法在 Windows 10/11 环境下的常用快捷键及 U 模式…

Paddle Serving|部署一个自己的OCR识别服务器

前言 之前使用C部署了自己的OCR识别服务器&#xff0c;Socket网络传输部分是自己写的&#xff0c;回过头来一看&#xff0c;自己犯傻了&#xff0c;PaddleOCR本来就有自己的OCR服务器项目&#xff0c;叫PaddleServing&#xff0c;这里记录一下部署过程。 1 下载依赖环境 1.1 …

AI与情感计算:如何让机器更好地理解人类情感与情绪?

引言&#xff1a;当AI遇上人类情感 当我们说起人工智能&#xff0c;脑海里第一时间想到的&#xff0c;可能是聪明的语音助手、精准的推荐系统&#xff0c;或者无所不能的机器人。但如果有一天&#xff0c;这些机器不再只是“执行指令”&#xff0c;而是能看出你今天心情不好&am…

普通IT的股票交易成长史--20250507晚复盘

声明&#xff1a;本文章的内容只是自己学习的总结&#xff0c;不构成投资建议。价格行为理论学习可参考简介中的几位&#xff0c;感谢他们的无私奉献。 送给自己的话&#xff1a; 仓位就是生命&#xff0c;绝对不能满仓&#xff01;&#xff01;&#xff01;&#xff01;&…
推荐文章