Am going to be facing a similar scenario very soon. My thoughts on this:
1 and 2 are essentially the same approach. Specifically, 'a client' whether the end client (1) or your separate module (2) that composes multiple calls are going to be calling multiple modules to get the relevant data from each before compiling into a single view as required.
I see three potential architectures:
- You have no global database and each module uses the same data model for writes and reads.
- You have no global database and each module has a separate database for writes and reads (using internal events to synchronise the read model when commands make changes to the write model).
- You have a global database that handles integration events from the various modules to compile a global read database.
For my solution, I would aspire to achieve number 3. Then a separate global query module can be implemented to return results from the global database and leave the modules alone. This would keep the module write databases free to optimally respond to commands without potential complex reads hitting the database at the same time.
Number 2 would keep the module write databases optimal but would require multiple queries to the different module read databases to compile a single view for the client.
Number 1 is the least favourable as it would hit the same database that is being used for writes in each module and also require a layer to compile multiple queries from different modules into a global view.
My plan for attacking this will be phased:
Firstly, create a 'collation layer' that exposes queries to return global views for the client. I would shy away from having the end client doing the composition as any changes in your module architecture would then hit the client directly instead of your 'collation layer'. The collation layer can protect the end client from such changes.
Initially, I have architecture 1. So, my collation layer will indeed be hitting the same databases as the modules are using for writes. But it's quick to get going.
However, having established that collation layer, I can then look to optimise my underlying solution without impacting client interfaces. By that time, I should have a clear view of what a global data schema would look like so I would change my architecture to number 3 directly. The problem with attempting architecture 3 during development is I can imagine the maintenance of changes in modules and the corresponding potential changes in the global data model would slow things down too much.
Architecture 2 is a little easier to achieve during development as the read model would only be impacted by changes to the local module.
Depending on your sensitivity to read and writes hitting the same database for the module during development and initial release, architecture 2 may be a decent stepping stone for you.
For me, I'm happy to go with initial release (small volume of customers) using the same module database for reads and writes hidden behind the interface in my collation layer, so I can jump straight to architecture 3 before more customers come on board.
As I've found with DDD, the correct approach usually depends ...
Not an answer as such, but perhaps those thoughts will help you make your own decision.