(Eu não li o Código Limpo e não conheço muito Java.)
Faz sentido aplicar a idéia de criar muitas entidades minúsculas, cada uma com uma responsabilidade claramente definida, aos namespaces?
Sim, assim como ocorre com a refatoração em várias classes e múltiplas funções.
Um pequeno grupo de classes relacionadas sempre deve ser agrupado em um espaço para nome?
Sem realmente responder: sim, você deve pelo menos usar um espaço para nome de nível superior. Isso pode ser baseado no projeto, organização ou o que você quiser, mas o uso de poucos nomes globais reduzirá os conflitos de nomes. Um único espaço para nome para agrupar todo o resto nele apenas apresenta um nome global. (Exceto as funções externas "C", mas isso se deve à interoperabilidade C e afeta apenas outras funções externas "C".)
Um pequeno grupo de classes relacionadas deve ser envolvido em um espaço para nome dedicado a elas? Provavelmente. Especialmente se você estiver usando um prefixo comum nessas classes - FrobberThing, FrobberThang, FrobberDoohickey -, considere um espaço para nome - frobber :: Thing e assim por diante. Ainda estaria no seu espaço para nome raiz ou outro espaço para nome se eles fizerem parte de um projeto maior.
Essa é a maneira de gerenciar a complexidade de ter muitas classes minúsculas ou o custo de gerenciar muitos espaços para nome seria proibitivo?
Tomando o exemplo acima de nomes prefixados, não é mais difícil gerenciar frobber :: Thing do que FrobberThing. Pode até ser mais fácil com algumas ferramentas, como documentação e conclusão de código. Há uma diferença com a ADL, mas isso pode funcionar a seu favor: menos nomes nos espaços para nome associados tornam a ADL mais fácil de descobrir e você pode usar declarações para injetar nomes específicos em um espaço para nome ou outro.
Os aliases de namespace permitem que você use um nome mais curto para um namespace mais longo em um contexto específico, o que novamente permite um uso mais fácil:
void f() {
namespace CWVLN = Company_with_very_long_name; // Example from the standard.
// In this scope, use CWVLN::name instead of Company_with_very_long_name::name.
namespace fs = boost::filesystem; // Commonly used.
}
Considere o Boost, que possui um único espaço para nome de raiz, boost e, em seguida, muitos subespaços de nomes - boost :: asio, boost :: io, boost :: sistema de arquivos, boost :: tuplas, etc. - para várias bibliotecas. Alguns nomes são "promovidos" para o espaço para nome raiz:
Todas as definições estão no namespace :: boost :: tuples, mas os nomes mais comuns são elevados para namespace :: boost com o uso de declarações. Estes nomes são: tupla, make_tuple, tie e get. Além disso, ref e cref são definidos diretamente no espaço para nome :: boost.
A maior diferença das linguagens com módulos "reais" é o quão comum é usar uma estrutura mais plana, o que geralmente acontece porque é assim que funciona, a menos que você faça um esforço específico e específico para definir nomes aninhados.