? food delivery modular monolith
food delivery modular monolith是一种实用而虚构的food delivery modular monolith ,它使用.NET核心和不同的软件体系结构和技术构建,例如模块化整体体系结构,垂直切片体系结构, CQRS模式,域驱动设计(DDD) ,事件驱动的体系结构。为了在独立模块之间进行通信,我们使用内存中的内存经纪人使用异步消息传递,也使用同步通信使用REST和GRPC调用进行实时通信。
该应用程序不是以业务为导向的,我的重点主要是技术部件,我只想使用不同的技术,软件体系结构设计,原理以及我们创建模块化整体应用程序所需的所有内容。
该应用程序将移植到另一个存储库中的微服务架构上,该存储库可在食品交换 – 微服务存储库中使用。
?请记住,此存储库是一项正在进行的工作,并且将随着时间的流逝而完成
支持
如果您喜欢随意使用此存储库,它会有所帮助:)
感谢一群支持我!
目录
- 计划
- 技术 – 图书馆
- 域和有限上下文 – 模块边界
- 应用架构
- 应用结构
- 模块中的垂直切片
- 先决条件
- 如何运行
- 贡献
- 项目参考
- 执照
计划
该项目正在进行中,随着时间的推移将添加新功能。
桌子中表示高级计划
| 特征 | 地位 |
|---|---|
| 构建块 | 完成了✔️ |
技术 – 图书馆
- ✔️.NET 8- .NET框架和.NET核心,包括ASP.NET和ASP.NET Core
- ✔️NPGSQL实体框架核心提供商-NPGSQL具有实体框架(EF)核心提供商。它的行为与其他EF核心提供商(例如SQL Server)一样
- ✔️FulentValidation-流行的.NET验证库,用于构建强大的验证规则
- ✔️Swagger &Swagger UI-用于记录API建立在ASP.NET Core上的Swagger工具
- ✔️serilog-简单.NET记录与完全结构的事件
- ✔️Polly -Polly是一个.NET弹性和瞬态故障处理库,它允许开发人员表达诸如重试,断路器,超时,舱壁隔离和以流利和线程安全的方式等政策
- ✔️审查员– Microsoft.extensions.epententiondixpoxtions的组装扫描和装饰扩展
- ✔️opentelemetry -dotnet- opentelemetry .net客户端
- ✔️duendesoftware IdentityServer-最灵活和符合标准的OpenID Connect和OAuth 2.X框架ASP.NET Core的框架
- ✔️NEWTONSOFT.JSON -JSON.NET是.NET的流行高性能JSON框架
- ✔️ASPNETCORE.DIASGNOSTICS.HEALTHCHECKS-企业HealthChecks for ASP.NET核心诊断软件包
- ✔️microsoft.aspnetcore.authentication.jwtbearer-在.NET核心中处理JWT身份验证和授权
- ✔️Nsubstitute-友好的替代.Net嘲笑库。
- ✔️STYLECAPANALYZERS-使用.NET编译器平台的Stylecop规则实现
- ✔️automapper- .net中的基于约定的对象对象映射器。
- ✔️HELANG.MIDDLEWARE.PROBLEMEDAILS-用于处理.NET CORE中的中间件
- ✔️IDGEN -.NET
域和有限上下文 – 模块边界
托多
应用架构
Bellow体系结构表明,有一个公共API (MicroService World中的API项目或API网关)托管我们所有的内部模块,并且可以为客户端访问,这是通过HTTP请求/响应完成的。然后,API项目将HTTP请求路由到相应的模块。在这里,我们的API项目而不是HTTP调用或网络调用我们的模块,我们有一些内存呼叫来调用我们的内部模块。在我们的应用程序中,这是最小API中的GatewayProcessor类的责任,或者是普通控制器中的CustomServiceBasedControllerRactivator。在我们的API中,没有代码仅托管我们的模块,并且使用垂直切片体系结构中每个模块中定义的路由,例如目录模块中的createProductendpoint。当用户http请求从API到达的此端点时,内心此端点我们使用GatewayProcessor <CatalogModuleconFiguration>将使用专用组合根的内存中的请求发送到我们的模块。在幕后,每个模块的GatewayProcessor使用专用组成根或根服务提供商,并且负责CompositionRootRegistry为每个模块保存和创建组成根。
当内存呼叫到达内部模块时,模块应处理此请求自动源。实际上,在模块化的整体中,我们的每个模块都应像以完全自主行为的方式一样对待微服务。为了实现此目标,我们应该为每个模块使用分离的组合根,实际上,对于每个组成词根,我们都有一个分开的服务提供商。 (在这里阅读更多…)
组成根:使用模块的单独组合根,我们可以到达自主权也可以创建其自己的对象依赖图或依赖容器,即它应该具有自己的组成根。
每个模块都在其自己的组成根或其自己的服务提供商内运行,并且可以直接访问其自己的本地数据库或模式以及其依赖项,例如文件,映射器等。所有这些依赖项仅适用于该模块而不是其他模块。实际上,模块是彼此分离的,并且是自主的(不是物理而是实际上)。另外,这种方法使每个模块更容易迁移到微服务时,例如,缩放目的。在这种情况下,我们可以将给定的模块提取到分离的微服务中,我们的模块化整体将与此服务进行通信,也许会与RabbitMQ(例如RabbitMQ)进行交流。
在此体系结构中,模块应在大多数情况下互相交谈,除非我们立即需要数据,例如获取一些数据并发送给用户。对于模块之间的异步通信,我们使用内存经纪人,但是我们可以根据需求和同步通信使用其他消息经纪人,我们使用REST呼叫或GRPC调用。
模块是基于事件的,这意味着他们可以发布和/或订阅设置中发生的任何事件。通过使用这种方法在模块之间进行通信,每个模块不需要了解其他模块中发生的其他模块或处理错误。
在此体系结构中,我们使用CQRS模式将读写模型分离出其他CQR的优势。现在在这里,我不为简单起见事件采购,但将来我会使用它来同步读写与发送流并为某些订户使用投影功能,以通过发送的流来同步他们的数据并在订户端创建我们的自定义读取模型。
在这里,我有一个使用Postgres数据库来处理更好的一致性和酸性交易保证的写入模型。除o此写作方面,我使用了一个读取的侧模型,该模型使用mongoDB来更好地执行我们的读取侧,而无需与任何嵌套文档在我们的文档中提起一些嵌套文档,也可以更好地可扩展性,并具有一些良好的缩放功能。
为了同步我们的阅读侧和写入侧,我们有2个选项,使用事件驱动的体系结构(不使用事件流中的事件流):
-
如果我们的读取端处于同一服务中,则在保存数据侧的数据中,我将内部命令记录保存在我的命令处理器存储中(就像我们在Outbox模式中所做的事情一样),在评论写入侧面后,我们的命令处理器管理器会读取未输入命令,并将其发送给相同的相应服务中的命令处理程序,并且此处理程序可以在我们的MongoDB数据库中保存其读取模型,将其保存在读取的范围内。
-
如果我们的阅读方面在另一个服务中,我们在提交我们的写入方面发布了一个集成事件(将此消息保存在发箱中),并且我们所有的订户都可以获取此事件并将其保存在他们的阅读模型(MongoDB)中。
所有这些都是应用程序中的可选,只能使用服务所需的内容。例如。如果该服务由于业务而不想使用DDD非常简单,并且大多数是CRUD,我们可以使用以数据为中心的架构,或者我们的应用程序不是基于任务而不是基于CQR,而不是基于CQR,而是将读取侧隔开并再次写入侧面,我们只能使用一个简单的基于CRUD的应用程序。
在这里,我使用了发件箱进行保证交付,可以用作集成事件的登陆区域,然后再将其发布给消息经纪人。
发件框模式可确保至少一次成功发送消息(例如队列)。有了这种模式,我们将其放在临时存储(例如数据库表)中,而不是直接向队列发布消息,以防止在任何故障(ateast-once交付)中丢失任何消息和某些重试机制。例如,当我们将数据保存为服务中的一个交易的一部分时,我们还保存了消息(集成事件),后来我们希望在另一个微服务中处理作为同一交易的一部分。要处理的消息列表称为带有消息传递类型Outbox的Storemessage,这是我们的消息保存服务的一部分。该基础架构还支持收件箱消息传递类型和内部消息传递类型(内部处理)。
另外,我们还有一个背景服务MessagePersistClockgroundService,可定期检查数据库中的存储信息,并尝试使用我们的MessagePersistencesService Service将消息发送给经纪人。在确认发布后(例如,从经纪人出发),它将消息标记为已处理以避免出现。但是,我们可能无法将消息标记为由于通信错误而将消息标记为处理的,例如代理不可用。在这种情况下,我们的MessagePersistEcterBackgroundService试图重新发出未经处理的消息,并且实际上是至关重要的交付。我们可以确定该消息将被发送一次,但也可以多次发送!这就是为什么这种方法的另一个名称是一次或寄出的交付。我们应该记住这一点,并尝试将消息的接收器设计为依据,这意味着:
在发送消息时,此概念转化为一条消息,无论是一次还是多次接收,都具有相同的效果。这意味着即使接收者接收相同消息的重复项,也可以安全地不满意而不会引起任何问题。
为了在接收方侧处理势力和确切的一致性交付,我们可以使用收件箱模式。
此模式类似于输出模式。它用于处理传入消息(例如,从队列)仅处理一次消息(即使执行多次执行)的唯一处理。因此,我们有一张表,我们正在存储传入的消息。与输入模式相反,我们首先将消息保存在数据库中,然后我们将ACK返回队列。如果保存成功,但我们没有将ACK退回队列,那么将重新交货。这就是为什么我们再次至关重要的交付。之后,收件箱背景过程运行,并将处理尚未处理的收件箱消息。另外,我们可以防止使用特定的MessgaeidMultpiph执行消息。例如,在执行我们的收件箱消息后,请致电我们的订阅事件处理程序,我们将ACK在他们成功时将ACK发送到队列。 (系统的收件箱部分正在进行中,我将尽快介绍此部分)
应用结构
在这个项目中,我使用垂直切片体系结构或重组对垂直切片体系结构也使用了该项目中的特征文件夹结构。
- 我们将每个请求视为独特的用例或切片,将所有问题封装并分组从前端到后方。
- 当我们在N-Tire体系结构中的应用程序中添加或更改功能时,我们通常会在应用程序中触摸许多不同的“层”。我们正在更改用户界面,将字段添加到模型,修改验证等等。我们没有沿层耦合,而是沿着切片垂直夫妇,每个变化仅影响一个切片。
- 我们最大程度地减少了切片之间的耦合,并最大化切片中的耦合。
- 通过这种方法,我们的每个垂直切片都可以自行决定如何最好地满足请求。新功能仅添加代码,我们不会更改共享代码并担心副作用。对于使用CQRS模式实现垂直切片体系结构是一个很好的匹配。
另外,我在这里使用CQR将我的功能分解为非常小的零件,这些零件使我们的应用:
- 最大化性能,可伸缩性和简单性。
- 在我们的代码的其他部分中,在没有任何破坏的变化的情况下,将新功能添加到这种机制非常容易。新功能仅添加代码,我们不会更改共享代码并担心副作用。
- 易于维护,任何更改仅影响一个命令或查询(或切片),并避免其他零件上的任何破坏变化
- 它为我们提供了更好的关注点和交叉切割问题(在媒体行为行为管道的帮助下),而不是做很多事情的大型服务课程。
随着使用CQR,我们的代码将更加与坚实的原则相符,尤其是:
- 单一责任规则 – 因为负责给定操作的逻辑被包含在其自己的类型中。
- 开放闭合规则 – 由于要添加新操作,因此您不需要编辑任何现有类型,因此需要添加一个新的文件,其中包含代表该操作的新类型。
在这里,我们没有使用一些技术拆分,例如我们的服务,控制器和数据模型的文件夹或图层,这些模型会增加我们的技术分裂和在层或文件夹之间跳跃之间的依赖性,而是将每个业务功能切成某些垂直切片,并且内部每个slices都具有特定于该功能的技术文件夹的结构(命令,处理程序,处理程序,运营商,基础架构,基础架构,reposority,reposority,Ropository,Ropository,Conlocter,Controller,Conlocter,Conlocter,数据模型,数据,…)。
通常,当我们从事给定功能时,我们需要一些技术知识:例如:
- API端点(控制器)
- 请求输入(DTO)
- 请求输出(DTO)
- 一些要处理请求的类,例如命令和命令处理程序或查询和查询处理程序
- 数据模型
现在,我们彼此之间拥有所有这些东西,它降低了某些层或文件夹之间的跳跃和依赖关系。
保持这样的分裂与CQRS效果很好。它隔离了我们的操作,并垂直切割应用程序代码,而不是水平切片。在我们的CQRS模式中,每个命令/查询处理程序是一个单独的切片。在这里,您可以减少层之间的耦合。每个处理程序都可以是一个分开的代码单元,甚至可以是复制/粘贴。因此,我们可以调低特定方法以不遵循一般约定(例如使用自定义SQL查询甚至其他存储)。在传统的分层体系结构中,当我们以一层更改核心通用机制时,它会影响所有方法。
高级结构
托多
模块中的垂直切片
托多
先决条件
- 该应用程序使用HTTP托管API,在计算机上设置有效证书,您可以创建一个自签名的证书。
- 安装git -https://git-scm.com/downloads。
- 安装.NET CORE 7.0 -https://dotnet.microsoft.com/download/dotnet/7.0。
- 安装Visual Studio 2022,Rider或Vscode。
- 安装Docker-https://docs.docker.com/docker-for-windows/install/。
- 确保您有〜10GB磁盘空间。
- 克隆项目https://gith**ub*.com/mehdihadeli/food-delivery-modular-monolith,请确保正在编译
- 运行docker-compose.infrastructure.yaml文件,用于使用Docker-Compose -f ./deployments/docker-compose.infrastructure.yaml -d命令运行先决条件基础架构。
- 开放式食品交通溶液。
如何运行
对于运行此应用程序,我们可以通过运行src/api/fooddelivery.api/fooddelivery.api.api.csproj项目在我们的开发环境中运行应用程序及其模块,对我来说是骑手。
对于测试API,我使用了vscode的REST客户端插件,其相关文件方案可在_httpclients文件夹中找到。同样,在运行API后,您可以访问 /Swagger Route路径中所有模块的Swagger Open API。
在此应用程序中,我使用带有Ethereal名称的假电子邮件发送者作为发送电子邮件的SMTP提供商。通过应用程序发送电子邮件后,您可以在Ethereal消息面板中查看发送电子邮件的列表。我的临时用户名和密码可在所有appSettings文件内部可用。
贡献
该应用程序处于开发状态。您可以随意提交拉动请求或创建问题。
项目参考
- https://g*ithub*.*com/kgrzybek/modular-monolith-with-ddd
- https://gith*ub.*co*m/oskardudycz/eventsourcing.netcore
- https://git*hu*b.c*om/dotnet-architecture/eshoponcontainers
- https://githu***b.com/jbogard/contosouniversitydotnetcore页面
- https://g*i*thub.*com/thangchung/clean-architecture-dotnet
- https://g*ithub.*com*/jasontaylordev/cleanarchitecture
- https://g*ithub**.com/dijanapenic/ddd-vshop
执照
该项目符合MIT许可。
