Theory
There are lot of misunderstandings about terminology in such questions, so let's mark 2 completely different architectures - monolith architecture and microservices architecture. 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:
P.S. Is could be completely separated not im-memory bus (like kafka or 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.
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. 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.
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
IMyEventNofitication : INotificationHandler<MyEvent>
in the shared project, and implement it with your logic in each module that wants to subscribe to the event. You then Publish said event through MediatR. – Riordan