O problema não é o monolito, é o acoplamento
Quebrar um monolito emaranhado em serviços te dá um sistema distribuído emaranhado. Resolva o acoplamento primeiro.
Já perdi a conta de quantas vezes um time me disse 'precisamos quebrar o monolito' quando o que eles tinham, na verdade, era um problema de acoplamento que ia acompanhá-los em qualquer topologia que escolhessem.
Monolito é uma unidade de deploy. Acoplamento é uma propriedade de design. Dá para ter um monolito com fronteiras internas limpas que faz deploy em quatro minutos e permite que dez times trabalhem em paralelo. E dá para ter quarenta microsserviços que precisam subir todos juntos porque compartilham um banco e um conjunto de premissas implícitas. O segundo também é um monolito. Só que com latência de rede e sem transações.
O que acoplamento realmente é
Acoplamento é o grau em que uma mudança em um lugar força uma mudança em outro. Essa é a definição inteira. Não tem nada a ver com fronteiras de processo.
Os tipos que mais doem, na minha experiência, são estes. Acoplamento de dados: dois módulos leem e escrevem nas mesmas tabelas, então uma mudança de schema em um vira bug no outro. Acoplamento temporal: A precisa rodar antes de B, e ninguém anotou isso, então funciona até alguém reordenar um job. Acoplamento semântico: dois módulos 'sabem' que status 3 significa 'enviado', codificado como número mágico em cada um. Acoplamento de build: uma mudança num pacote utilitário força rebuild e reteste de tudo que o importa, ou seja, tudo.
Nenhum desses se resolve colocando uma chamada HTTP entre os módulos. Acoplamento de dados vira banco compartilhado entre serviços, que é pior. Acoplamento temporal vira race condition pela rede. Acoplamento semântico vira dois serviços que discordam sobre o que status 3 significa, e agora a discordância está nos logs de produção em vez de num erro de compilação.
O diagnóstico honesto
Antes de recomendar qualquer separação, peço três coisas. Primeiro, o grafo de dependências entre módulos, não entre serviços. A maioria dos times não consegue produzir. Só isso já me diz que o acoplamento não está documentado, o que significa que não está controlado.
Segundo, os últimos vinte pull requests. Conto quantos arquivos cada um tocou e em quantas pastas de primeiro nível. Se uma mudança típica em 'pedidos' toca 'pedidos', 'cobrança', 'notificações' e 'compartilhado', as fronteiras não estão onde as pastas estão, e uma separação em serviços seguindo as pastas vai exigir as mesmas quatro mudanças em quatro repositórios com quatro deploys.
Terceiro, o schema do banco com as chaves estrangeiras desenhadas. Se a tabela de pedidos faz join com usuários, produtos, endereços, pagamentos e uma tabela de auditoria compartilhada, e todo módulo consulta todas elas, não existe costura por onde cortar. Separar aqui significaria duplicar dados com jobs de sincronização ou fazer os serviços se chamarem de forma síncrona a cada leitura. As duas opções são piores do que o que você tem.
Resolva o acoplamento dentro do monolito primeiro
O trabalho que realmente ajuda é chato e pode ser feito sem mudar nada no modelo de deploy. Dê a cada módulo seu próprio conjunto de tabelas e proíba consultas entre módulos no code review, depois no lint, depois nas permissões do banco. Substitua chamadas diretas de função entre módulos por uma interface explícita que vive em um arquivo por módulo. Mova enums e códigos de status compartilhados para o módulo que é dono do conceito, e faça todo mundo importar de lá.
Faça isso por seis meses. Meça. Conte de novo os arquivos por PR. Quando mudanças em 'pedidos' pararem de tocar 'cobrança', você conquistou o direito de perguntar se deveriam ser serviços separados. E na maioria das vezes, quando o acoplamento some, a resposta é 'ainda não'. O monolito com fronteiras limpas faz deploy rápido, testa em um processo só e tem transações de verdade. São vantagens enormes que times jogam fora por um problema que não resolveram.
Quando a separação se justifica de fato
Existem razões honestas para extrair um serviço. Um módulo precisa de outro runtime, outra linguagem, outro perfil de escala, outra fronteira de compliance, ou outra cadência de release que está bloqueando os demais. Essas razões são específicas e mensuráveis. 'Está grande demais' não é uma delas. 'Está difícil de mudar' geralmente significa acoplamento, e acoplamento viaja junto.
Resolva o acoplamento. Depois olhe o monolito de novo. Pode ser que ele não seja o problema que você achava.