简体   繁体   English

模块化单体中的跨模块通信

[英]Cross module communication in modular monolith

I have been learning about modular monolith project structure in this article: https://codewithmukesh.com/blog/modular-architecture-in-as.net-core我一直在这篇文章中学习模块化单体项目结构: https://codewithmukesh.com/blog/modular-architecture-in-as.net-core

Most of it makes sense to me but something I don't quite get is:其中大部分对我来说很有意义,但我不太明白的是:

Cross Module communication can happen only via Interfaces/events/in-memory bus.跨模块通信只能通过接口/事件/内存总线进行。 Cross Module DB Writes should be kept minimal or avoided completely.跨模块数据库写入应保持最少或完全避免。

How exactly does that cross-module communication look?跨模块通信究竟是什么样子的?

Let's say I have 3 modules:假设我有 3 个模块:

  • Product产品
  • User用户
  • Security安全

My security module registers an endpoint for DisableUser .我的安全模块为DisableUser注册了一个端点。 It's this endpoint's job to update a User and every Product associated with the user with a disabled status.这个端点的工作是更新User和与用户关联的每个Product的禁用状态。

How does the Security module call User & Product update status method in a unit of work? Security模块如何在一个工作单元中调用User & Product更新状态方法?

My understanding is that this pattern is intended to make it easier to extract a module to a microservice at a later date so I guess having it as a task of some sort makes it easier to change to a message broker but I am just not sure how this is supposed to look.我的理解是这种模式旨在让以后更容易将模块提取到微服务,所以我想将它作为某种任务可以更容易地更改为消息代理,但我只是不确定如何这应该看起来。

My example is obviously contrived, my main point is how do modules communicate together when read/writes are involved?我的例子显然是做作的,我的主要观点是当涉及读/写时模块如何相互通信?

First glance, I think one approach is to use Mediator events since the project already uses that.乍一看,我认为一种方法是使用 Mediator 事件,因为项目已经使用了它。 It would work well and keep everything separate.它会很好地工作并将所有内容分开。

To define Mediator event check this.要定义调解器事件,请检查此。
You define your events in shared core, for your example:您在共享核心中定义您的事件,例如:

public class UserDisabled : INotification
{
    public string UserId { get; set; }
}

From the User modules you will publish the event when the user get disabled当用户被禁用时,您将在用户模块中发布事件

await mediator.Publish(new UserDisabled{UserId = "Your userId"});

And Finally declare event handlers in every modules that need to react to the event最后在需要对事件做出反应的每个模块中声明事件处理程序

public class UserDisabledHandler : INotificationHandler<UserDisabled>
{
    public UserDisabledHandler()
    {
        //You can use depency injection here
    }
    public Task Handle(UserDisabled notification, CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}

However it is worth noting that this won't work if you want to switch to actual micro-services.但是值得注意的是,如果您想切换到实际的微服务,这将不起作用。 I'm not very familiar with micro services, but I think you need some form of event bus and that's where micro-services becomes complicated.我对微服务不是很熟悉,但我认为你需要某种形式的事件总线,这就是微服务变得复杂的地方。
There is information about that in this Microsoft book .本 Microsoft 书籍中有关于此的信息。

Theory理论

There are lot of misunderstandings about terminology in such questions, so let's mark 2 completely different architectures - monolith architecture and microservices architecture .在此类问题中存在很多对术语的误解,所以让我们标记 2 种完全不同的架构——单体架构微服务架构 So one architecture that stands between these both is a modular monolith architecture .因此,介于两者之间的一种架构是模块化单体架构

Monolith architecture mostly has a huge problem - high coupling and low cohesion because you have no strong methods to avoid it.单体架构大多存在一个巨大的问题—— 高耦合和低内聚,因为你没有强有力的方法来避免它。 So programmers decide to think about new ways of building different architectures to make really hard to fall down in high coupling low cohesion problem.因此程序员决定考虑构建不同架构的新方法,以使在高耦合低内聚问题中真正难以陷入困境。

Microservices architecture was a solution (despite other problems it solve too).微服务架构是一种解决方案(尽管它也解决了其他问题)。 Main point in microservices architecture is all about separation services from each other to avoid high coupling (because it is not so easy to setup communication between services as in monolith architecture).微服务架构的要点就是将服务彼此分离以避免高耦合(因为在服务之间建立通信不像单体架构那样容易)。

But programmers can't move from one architecture to completely different in "one click", so one (but not only one) way to build microservices architecture from monolith architecture is to make modular monolith first (just solve high coupling low cohesion problem but in monolith) and then extract modules to microservices easily.但是程序员不能“一键”从一种架构迁移到完全不同的架构,所以从单体架构构建微服务架构的一种(但不仅仅是一种)方法是先做模块化单体(只是解决高耦合低内聚问题但在单体),然后轻松地将模块提取到微服务中。

Communication沟通

To made coupling low we should focus on communication between services.为了降低耦合,我们应该关注服务之间的通信。 Lets work with sample you put in your question.让我们使用您在问题中提出的示例。

Imagine we have this monolith architecture:想象一下,我们有这个单体架构: 单体架构

We definitely see high coupling problem here.我们肯定在这里看到了高耦合问题。 Let's say we want to build it more modular.假设我们想要构建它更加模块化。 To make that, we need to add something between modules to separate them from each other, also we want modules to communicate, so the only thing we must to add is a bus.为此,我们需要在模块之间添加一些东西以将它们彼此分开,而且我们希望模块能够通信,所以我们唯一必须添加的是总线。

Something like that:像这样的东西: 在此处输入图像描述

PS Is could be completely separated not im-memory bus (like kafka or rabbitmq) PS 可以完全分离而不是内存总线(如 kafka 或 rabbitmq)

So your main question was about how to make communication between modules, there are few ways to do that.所以你的主要问题是关于如何在模块之间进行通信,有几种方法可以做到这一点。

Communication via interfaces (synchronous way)接口通讯(同步方式)

Modules could call each other directly (synchronously) through interfaces.模块可以通过接口直接(同步)相互调用。 Interface is an abstraction, so we don't know what stands behind that interface.接口是一种抽象,所以我们不知道该接口背后是什么。 It could be mock or real working module.它可以是模拟或真实的工作模块。 It means that one module doesn't know nothing about other modules, it knows only about some interfaces it communicate with.这意味着一个模块对其他模块一无所知,它只知道与之通信的一些接口。

public interface ISecurityModule { }
public interface IUserModule { }
public interface IProfileModule { }

public class SecurityModule : ISecurityModule
{
    public SecurityModule(IUserModule userModule) { } // Does not know about UserModule class directly
}

public class UserModule : IUserModule
{
    public UserModule(IProfileModule profileModule) { } // Does not know about ProfileModule class directly
}

public class ProfileModule : IProfileModule
{
    public ProfileModule(ISecurityModule securityModule) { } // Does not know about SecurityModule class directly
}

You can communicate between interfaces through methods call with no doubt but this solution doesn't help well to solve high coupling problem.毫无疑问,您可以通过方法调用在接口之间进行通信,但是这种解决方案并不能很好地解决高耦合问题。

Communication via bus (asynchronous way)总线通讯(异步方式)

Bus is a better way to build communication between modules because it forces you use Events/Messages/Commands to make communication.总线是在模块之间建立通信的更好方法,因为它强制您使用事件/消息/命令进行通信。 You can't use methods call directly anymore.您不能再直接使用方法调用。

To achieve that you should use some bus (separated or in-memory library).为此,您应该使用一些总线(分离或内存库)。 I recommend to check other questions (like this ) to find proper way to build such communication for your architecture.我建议检查其他问题(如this )以找到为您的架构构建此类通信的正确方法。

But be aware - using bus you make communication between modules asynchronous, so it forces you to rewrite inner module behaviour to support such communication way.但请注意 - 使用总线使模块之间的通信异步,因此它迫使您重写内部模块行为以支持这种通信方式。

About your example with DisableUser endpoint.关于您使用DisableUser端点的示例。 SecurityModule could just send command/event/message in bus that user was disabled in security module - so other services could handle this command/event/message and "disable" it using current module logic. SecurityModule可以只在总线中发送用户在安全模块中被禁用的命令/事件/消息 - 因此其他服务可以处理此命令/事件/消息并使用当前模块逻辑“禁用”它。

What's next下一步是什么

Next is a microservice architecture with completely separated services communicating through separated bus with separated databases too:接下来是一个微服务架构,它具有完全独立的服务,也通过独立的总线与独立的数据库进行通信: 在此处输入图像描述

Example例子

Not long time ago I've done project completely in microservices architecture after course.不久前,我在课程后完全完成了微服务架构的项目。
Check it here if you need good microservices architecture example.如果您需要良好的微服务架构示例,请在此处查看。

Images were created using Excalidraw图像是使用 Excalidraw 创建的

If you go with Modular Monolith check out cap .如果您使用 Modular Monolith go,请查看上限 Outbox is important in async communication even in Modular Monolith.即使在 Modular Monolith 中,发件箱在异步通信中也很重要。 Cap will give you way to handle retries, ensure data consistency and even add out-of-process handlers. Cap 将为您提供处理重试、确保数据一致性甚至添加进程外处理程序的方法。 I guess there are other libs too (one) , but I use this one.我想还有其他库(一个) ,但我用的是这个。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM