TL; DR: Think about components and modules separately and set their โtouch pointsโ
The modules, as in your example, look like an end-to-end structure that is in good agreement with the recommended microservice practice. Thus, all of them can be part of one microservice. And if you intend to use DDD, you will want to include the limited context name in your package path.
In my own source code, I usually separate (at the top level) modules like config (for loading and analyzing, well, config), functional for the functional kernel, operational domain model for concurrency control, Akka chord structure, monitoring, etc. and adapters , where all the API, DB, and MQ codes live. And finally, the app module, where everything is running and the interfaces are tied to implementations. In addition, you usually have utils or commons for lower level templates, algorithms, etc.
In some architecture schools, there is a clear separation between modules and components. Although the former are part of the source code structure, the latter are runtime units that consume resources and live their own way.
In your case, microservices correspond to such components. These components can work in one JVM, and you get a monolith. Or they can run in a separate JVM on a (possibly) separate host. Then you call them microservices.
So you need to:
- make the source code of each component standalone so that it can run in a separate execution space (for example, a class loader, thread, threadpool, actor system subsystem). Therefore, you need a launcher to revive it. Then you can call from your
public static void main(...) . - enter some modules into your code that will contain the semantics of an individual component. So that you can understand the scope of the component from the code.
- An abstract connection between components so that you can use adapters (source code modules) to talk over the network or use mechanisms inside the JVM, such as calling a procedure or sending an Akka message.
I should note that at the lower levels you can use common source code modules in your components so that they can have some intersections in the code. But at a higher level, the source code will be distinctive, so you can separate it into modules according to the components.
You can use Akka and run each of your components in the supervision subtree, where the subtree supervisor is your main component of your component. Then this basic definition of an actor will be your main component module. If you need to pass components, you must pass the corresponding ActorRefs to the adapters as a configuration parameter.
You are talking about the centralization of communication points, but, in my opinion, if you adhere to the microservices paradigm and a high level of autonomy for your components, then for every API call someone must have a contract. Enter various interaction templates with a limited DDD context. If you leave it in some centrally managed module that each component should use, then this is an API control case. As long as you are the only supportive, this can be convenient. But when different developers (or even teams) accept their roles, you will need to make this decision again, taking into account the new conditions.
When you later separate the components, you will pass the URL to the adapters instead of ActorRefs.