The answer to the problem of finding a proper way to decompose existing complicated system into smaller components is two-fold:
- Use known methodologies for finding potential boundaries;
- Iterate a lot to see if system system could be divided across these boundaries.
Let's start with examples of methods for finding potential boundaries:
- Look at the domain model from the perspective of Domain-Driven Design, seeking out bounded contexts and aggregate boundaries there (although classical DDDish BCs and aggregates could often be split in smaller focused parts as well).
- Separate domain-side problems (need domain expert) from implementation problems like scalability (in some projects performance might be part of the domain, too);
- Use domain events to capture state changes in some parts of your system; then identify related events and group them - they could be a core of a new component; non-related events could probably be put into separate components;
- Look from the perspective of separating project between multiple teams and individual developers;
- Look at transactional boundaries in your system. If there are that changes must absolutely happen together (or fail together) then they might be candidates for being put into a single component. If some changes don't really affect the others, then you could probably consider keeping them separately.
- Search for processes which must be coupled tightly from the temporal perspective - they might belong to one component. At the same time, presence of queues might indicate potential component boundary.
And, above all, simply keep on asking yourself one simple question - what things can be taken apart? How can we separate our software into atomic things that can be composed to solve the problem.
Decomposition is not simply about breaking some project into a bunch of tightly coupled modules. The idea is to identify the boundaries and invest effort into evolving them.
Methods for finding potential boundaries (like the ones listed above) only provide you with some hints and ideas. Your objective (as a designer) would be to play with these ideas, iterate and evolve system, while finding options that truly make it more robust, evolution-friendly and simple (put your priorities here).