Deixe-me começar pedindo desculpas pela duração do post, mas eu realmente queria transmitir o máximo de detalhes antecipadamente, para que eu não dedique seu tempo indo e voltando nos comentários.
Estou projetando um aplicativo seguindo uma abordagem DDD e estou pensando em quais orientações posso seguir para determinar se uma Raiz Agregada deve conter outro AR ou se eles devem ser deixados como ARs independentes "independentes".
Tomemos o caso de um aplicativo de Relógio de ponto simples que permite que os funcionários entrem ou saiam do dia. A interface do usuário permite que eles insiram seu ID e PIN do funcionário, que são validados e o estado atual do funcionário recuperado. Se o funcionário estiver com entrada no relógio, a interface do usuário exibirá um botão "Clock Out"; e, por outro lado, se eles não tiverem entrada no relógio, o botão exibirá "Entrada do relógio". A ação realizada pelo botão também corresponde ao estado do funcionário.
O aplicativo é um cliente da Web que chama um servidor backend exposto por meio de uma interface de serviço RESTful. Minha primeira passagem na criação de URLs intuitivos e legíveis resultou nos dois seguintes pontos de extremidade:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
NOTA: São utilizados após a validação do ID e PIN do funcionário e um "token" representando o "usuário" é passado em um cabeçalho. Isso ocorre porque existe um "modo de gerente" que permite que um gerente ou supervisor faça o registro de entrada ou saída de outro funcionário. Mas, para o bem desta discussão, estou tentando manter as coisas simples.
No servidor, eu tenho um ApplicationService que fornece a API. Minha idéia inicial para o método ClockIn é algo como:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
Isso parece bastante direto até percebermos que as informações do cartão de ponto do Funcionário são realmente mantidas como uma lista de transações. Isso significa que toda vez que eu chamo ClockIn ou ClockOut, não estou mudando diretamente o estado do Employee, mas adicionando uma nova entrada no TimeSheet do Employee. O estado atual do Empregado (com ou sem clock) é derivado da entrada mais recente no TimeSheet.
Portanto, se eu seguir o código mostrado acima, meu repositório precisará reconhecer que as propriedades persistentes do Employee não foram alteradas, mas que uma nova entrada foi adicionada ao TimeSheet do Employee e execute uma inserção no armazenamento de dados.
Por outro lado (e aqui está a pergunta final da publicação), o TimeSheet parece ser uma raiz agregada e possui identidade (o ID e o período do funcionário) e eu poderia implementar facilmente a mesma lógica do TimeSheet.ClockIn (ID do Empregado).
Encontro-me debatendo os méritos das duas abordagens e, como afirmado no parágrafo inicial, fico imaginando quais critérios devo avaliar para determinar qual abordagem é mais adequada para o problema.