Think of the architecture as a wheel that you're running in like a hamster. You never "finish" architecting, but you know that you make progress when you complete full revolutions of the wheel. In the first half of the wheel, you code simply and naively and accumulate features and problems. In the second half, you gradually sort out those problems, identifying the low-hanging fruit for refactors first and then gradually applying more advanced techniques to go from an 80% correct solution to 90% by identifying the things that are key components, which would benefit from a more complex treatment, while throwing out things that are unjustifiably complex and replacing them with "dumber" code. Then in the next revolution, you go from 90% to 95%, and then to 98%, 99%, 99.9%, and so on, until the day the codebase is abandoned.

If you apply the procedures in the wrong order - trying to do advanced design in the first half of the wheel, for example - the project immediately suffers because of premature assumptions. This is why rewrites are so risky, because you're throwing out accumulated knowledge from previous turns of the wheel in the hopes that you can jump to a higher correctness level. Remember - even if you succeed at the rewrite, you never get to 100%.

So, based on your description, we can say that you're somewhere in the second half, and you currently have a problem with analysis of the current architecture. First figure out which parts are highest priority, and which are likely to continue to last for a whole turning unaided. Then dig into the structure of the high-priority code looking for two things:

1. Bad factorings - things that would work better if they were rewritten inlined. Each time you inline, you remove at least one point of dependency; each time you factor out something done in two places, you add at least one point of reuse. Thus you can make a ton of progress just by inlining the existing source, reading the result, and then finding new reusable parts in that - typically the outcome is a net positive on dependencies/reuse.

2. Bad data design - structures that cause more problems than they solve. At first data is always "just" parameters passed to an algorithm, but in any real program data soon also has to carry around information determining the future state of the program at a broader level(running different code based on the type of the data, etc.). It's this second aspect that is key - what you are looking for is how the current form of the data is motivating dependencies in the code, and whether a different canonical form would reduce dependencies. Sometimes this means more structure, sometimes it means less.

This is a "shaking off the dust" action where you discover the true nature of the codebase - as opposed to everyone's initial conceptions - and it can be done at any time. You can also use techniques like drawing the callgraph or running benchmarking tools with an eye towards finding architectural bottlenecks. Eventually you'll see patterns that warrant the inclusion of some nice higher-level, shared construct. Those are the design wins you are looking for - you only need one or two of them to make a huge impact, and they only come right at the completion of the revolution, enabling a new round of more naive, product-facing code to be written. Taking care of the low-hanging fruit makes the code base less confusing to work on, thus it typically precedes the big wins.

The introduction of new external dependencies like a different database product is one of those ways in which you can make a big win, but you don't want to add a big dependency lightly, since it raises the lower bound on how much effort is needed just to maintain the system at a basic level. It's always another tradeoff that adds new code "behind the scenes", and the main advantage is that you aren't writing that new code yourself.

As you go through this process, testing and static analysis becomes crucial for making sure all the shuffling around of things isn't causing an issue, so don't proceed until you have a process for that. Testing can always start as a manually-driven checklist, and then automated as automation wins are identified. Likewise, static analysis can start as a human code review process and then be supplemented with automated tools. Too much automation creates another dependency, thus it has to be weighed against human time costs.